单链表为什么要设置头结点

您所在的位置:网站首页 单链表没有头结点和有头结点的区别 单链表为什么要设置头结点

单链表为什么要设置头结点

2024-07-15 18:54| 来源: 网络整理| 查看: 265

 

链表中第一个结点的存储位置叫做头指针,那么整个链表的存取就必须是从头指针开始进行了。之后的每一个结点,其实就是上一个的后继指针指向的位置。

这里有个地方要注意,就是对头指针概念的理解,这个很重要。“链表中第一个结点的存储位置叫做头指针”,如果链表有头结点,那么头指针就是指向头结点数据域的指针。画一个图吧。

 

单链表为什么要设置头结点_二维

头指针就是链表的名字。头指针仅仅是个指针而已。

头结点是为了操作的统一与方便而设立的,放在第一个元素结点之前,其数据域一般无意义(当然有些情况下也可存放链表的长度、用做监视哨等等)。 有了头结点后,对在第一个元素结点前插入结点和删除第一个结点,其操作与对其它结点的操作统一了。 首元结点也就是第一个元素的结点,它是头结点后边的第一个结点。 头结点不是链表所必需的。 是的,对于头指针,我们也可以有相应的理解了。 在线性表的链式存储结构中,头指针是指链表指向第一个结点的指针,若链表有头结点,则头指针就是指向链表头结点的指针。 头指针具有标识作用,故常用头指针冠以链表的名字。 无论链表是否为空,头指针均不为空。头指针是链表的必要元素。   单链表也可以没有头结点。如果没有头结点的话,那么单链表就会变成这样: 单链表为什么要设置头结点_结点_02

typdef struct LNode{

int data,

LinkList *next;

}LNode,*LinkList;

LinkList head=new LNode();

head->next=null;

插入n个元素

LinkList p=head;

for(int i=0;idata=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)。       另外,对于函数的形参是指针时,只要该参数不在左边(即都是右值操作),二维指针(形参)就可以简化为一维指针。如上面带头结点的尾插 简化版本。  转自:javascript:void(0)   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)。       另外,对于函数的形参是指针时,只要该参数不在左边(即都是右值操作),二维指针(形参)就可以简化为一维指针。如上面带头结点的尾插 简化版本。


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3