单链表为什么要设置头结点
总结:
使得在链表头部的操作(如:插⼊删除等)与在链表中部与尾部⼀致(统⼀)
使⾮空链表与空链表的操作统⼀
转载:
链表中第⼀个结点的存储位置叫做头指针,那么整个链表的存取就必须是从头指针开始进⾏了。之后的每⼀个结点,其实就是上⼀个的后继指针指向的位置。
这⾥有个地⽅要注意,就是对头指针概念的理解,这个很重要。“链表中第⼀个结点的存储位置叫做头指针”,如果链表有头结点,那么头指针就是指向头结点数据域的指针。画⼀个图吧。
头指针就是链表的名字。头指针仅仅是个指针⽽已。
是为了操作的统⼀与⽅便⽽设⽴的,放在第⼀个元素结点之前,其数据域⼀般⽆意义(当然有些情况下也可存放链表的长度、⽤做监视哨等等)。
有了头结点后,对在第⼀个元素结点前插⼊结点和删除第⼀个结点,其操作与对其它结点的操作统⼀了。
⾸元结点也就是第⼀个元素的结点,它是头结点后边的第⼀个结点。
头结点不是链表所必需的。
是的,对于头指针,我们也可以有相应的理解了。
在线性表的链式存储结构中,头指针是指链表指向第⼀个结点的指针,若链表有头结点,则头指针就是指向链表头结点的指针。
头指针具有标识作⽤,故常⽤头指针冠以链表的名字。
⽆论链表是否为空,头指针均不为空。是链表的必要元素。
单链表也可以没有头结点。如果没有头结点的话,那么单链表就会变成这样:
typdef struct LNode{
int data,
sizeof 指针LinkList *next;
}LNode,*LinkList;
LinkList head=new LNode();
head->next=null;
插⼊n个元素
LinkList p=head;
for(int i=0;i<n;i++)
{
LinkList q=new LNode();
q->data=i; q->next=null;
p->next=q;
}
为了使空链表与⾮空链表处理⼀致,我们通常设⼀个头结点。
转的⼀篇⽂章:
带有头结点:不带头结点
不带头结点初始化
Node *head; //声明头结点
带头结点初始化
void InitList(Node **head){
*head=(Node *)malloc( sizeof(Node)); (*head)->next=NULL;
}
带头结点尾插⼊,统⼀操作
⽅式⼀:
void CreatList(Node **head){
Node *r=*head,*s;
int a;
while(scanf("%d",&a)){
if(a!=0){
s=(Node *)malloc(sizeof(Node)); s->value=a;
r->next=s;
r=s;
}
else{
r->next=NULL;
break;
}
}
}
调⽤CreatList(&head);
⽅式⼆:
void CreatList(Node *head){
Node *r=head,*s;
... //下⾯的都⼀样
}
调⽤CreatList(head);⽅式⼀:
void InitList(Node **head){
*head=NULL;
}
调⽤InitList(&head);
⽅式⼆:
void InitList(Node *head){
head=NULL;
}
调⽤InitList(head);
不带头结点尾插⼊,第⼀个节点与其他节点分开操作void CreatList(Node **head){
Node *p,*t; /*p⼯作指针,t临时指针*/
int a,i=1;
while(scanf("%d",&a)){
if(a!=0){
t=(Node *)malloc(sizeof(Node));
t->value=a;
if(i==1){
*head=t;
}
else{
p->next=t;
}
p=t;
}
else{
p->next=NULL;
break;
}
i++;
}
}
调⽤CreatList(&head);
⼀、两者区别:
1、不带头结点的单链表对于第⼀个节点的操作与其他节点不⼀样,需要特殊处理,这增加了程序的复杂性和出现bug的机会,因此,通常
在单链表的开始结点之前附设⼀个头结点。
2、带头结点的单链表,初始时⼀定返回的是指向头结点的地址,所以⼀定要⽤⼆维指针,否则将导致内存访问失败或异常。
3、带头结点与不带头结点初始化、插⼊、删除、输出操作都不样,在遍历输出链表数据时,带头结点的判断条件是while(head->next!=NULL),
⽽不带头结点是while(head!=NULL),虽然头指针可以在初始时设定,但是如1所述,对于特殊情况如只有⼀个节点会出现问题。
⼆、为什么不带头结点初始化有2种⽅式,⽽带头结点只有1种⽅式呢?
因为不带头结点声明Node *head 时;C编译器将其⾃动初始化为NULL,于是根本不需要调⽤InitList(head);也即不带头结点的初始化
是个伪操作。⽽带头结点的初始化在堆开辟了⼀段内存,需要修改head指针变量指向的地址(即head的值),所以要修改head的值,必须传保
存head变量的地址(即⼆维指针)。⽽直接调⽤CreatList(head);相当于传head变量的值,函数修改的是head的副本,⽆法真正改变head的值。
注:这⾥可以将head指针看成⼀个变量(不管它保存的是地址),就⽐较好理解了。
三(key)、其实本质上还是传值,传址的问题,只不过指针本⾝保存的地址,让这个过程变得有点纠结。在函数调⽤需要修改指针变量的指向(value)时,应该传递指针变量的地址(address)。
另外,对于函数的形参是指针时,只要该参数不在左边(即都是右值操作),⼆维指针(形参)就可以简化为⼀维指针。如上⾯带头结点的尾插
简化版本。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论