Chinaunix首页 | 论坛 | 博客
  • 博客访问: 227347
  • 博文数量: 74
  • 博客积分: 450
  • 博客等级: 下士
  • 技术积分: 290
  • 用 户 组: 普通用户
  • 注册时间: 2007-04-12 08:46
文章分类

全部博文(74)

文章存档

2019年(3)

2018年(11)

2017年(5)

2016年(24)

2015年(20)

2007年(11)

我的朋友

分类: C/C++

2017-08-09 14:52:11

 

中,在单链表的开始结点之前附设一个类型相同的结点,称之为头结点。头结点的数据域可以不存储任何信息,头结点的指针域存储指向开始结点的指针(即第一个元素结点的存储位置)。

作用
1、防止单链表是空的而设的.当链表为空的时候,带头结点的头指针就指向头结点.如果当链表为空的时候,单链表没有带头结点,那么它的头指针就为NULL.
2、是为了方便单链表的特殊操作,插入在表头或者删除第一个结点.这样就保持了单链表操作的统一性!
3、单链表加上头结点之后,无论单链表是否为空,头指针始终指向头结点,因此空表和非空表的处理也统一了,方便了单链表的操作,也减少了程序的复杂性和出现bug的机会。
4、对单链表的多数操作应明确对哪个结点以及该结点的前驱。不带头结点的链表对首元结点、中间结点分别处理等;而带头结点的链表因为有头结点,首元结点、中间结点的操作相同 ,从而减少分支,使算法变得简单 ,流程清晰。对单链表进行插入、删除操作时,如果在首元结点之前插入或删除的是首元结点,不带头结点的单链表需改变头指针的值,在C 算法的函数形参表中头指针一般使用指针的指针(在C+ +中使用引用 &);而带头结点的单链表不需改变头指针的值,函数参数表中头结点使用指针变量即可。

单链表带头结点&不带头结点
 
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)。
      另外,对于函数的形参是指针时,只要该参数不在左边(即都是右值操作),二维指针(形参)就可以简化为一维指针。如上面带头结点的尾插
简化版本。

阅读(838) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~