最近在学习链表,出现了几个容易混淆的概念,分别是头结点,头指针,首元结点,他们三个究竟是什么关系呢? 头结点: 头结点是链表里面第一个结点,他的数据域能够不存听任何信息(有时候也会存放链表的长度等等信息),他的指针区域存放的是链表中第一个数据元素的结点(就是传说中的首元结点)存放的地址。 一、防止单链表是空的而设的.当链表为空的时候,带头结点的头指针就指向头结点.若是当链表为空的时候,单链表没有带头结点,那么它的头指针就为NULL. 二、是为了方便单链表的特殊操做,插入在表头或者删除第一个结点.这样就保持了单链表操做的统一性! 三、单链表加上头结点以后,不管单链表是否为空,头指针始终指向头结点,所以空表和非空表的处理也统一了,方便了单链表的操做,也减小了程序的复杂性和出现bug的机会。 四、对单链表的多数操做应明确对哪一个结点以及该结点的前驱。不带头结点的链表对首元结点、中间结点分别处理等;而带头结点的链表由于有头结点,首元结点、中间结点的操做相同 ,从而减小分支,使算法变得简单 ,流程清晰。对单链表进行插入、删除操做时,若是在首元结点以前插入或删除的是首元结点,不带头结点的单链表需改变头指针的值,在TurboC 算法的函数形参表中头指针通常使用指针的指针(在C+ +中使用引用 &);而带头结点的单链表不需改变头指针的值,函数参数表中头结点使用指针变量便可,对初学者更易接受。
首元结点:
首元结点就是链表里面第一个元素的结点,也就是a1的结点。在存在头结点的状况下,头结点的指针区域指的就是首元结点。
头指针:
若是链表里有头结点,头指针就指向头结点;若是没有头结点,头指针就指向首元结点。
这三个概念对单链表,双向链表,循环链表均适用。 ②若是用C语言描述单链表以下: typedef struct node{ DataType data;//节点的数据域 struct node *next;//节点的指针域 }ListNode; typedef ListNode *LinkList; ListNode *p;//p是节点 LinkList head;//head是头指针 注意最后一句:"LinkList head",这是定义了一个头结点,前面已经用typedef定义了LinkList,既“typedef ListNode *LinkList;”这句,这就说明LinkList head 定义后 head其实是一个LinkNode类型的指针,这是咱们单单看最后两句的定义而忽略其字面意思而言的话,p和head其实是同一种类型的变量,都是ListNode类型的指针,只不过最后一句LinkList类型是用typedef定义的,因此没有“*”号。 由于删除或者插入操做有时会修改实参的指针(好比头结点为空的时候插入节点,这是就修改了头结点),那么就必须将相应的形参说明为指针的指针,函数电泳时将实参指针的地址传递给相应的形参。例如:刚刚初始化的时候头结点head为空,若是这时插入节点p(由上可知p是ListNode*类型的,是个指针,把它当作地址)时应该是head=p;这就改变了head的地址,因此在传参数的时候,函数的形参通常都是"Insert(LinkList *head,DataType x)", 方便在第1个位置进行插入、删除操做时同其余位置同样。加了头结点以后,插入、删除都是在后继指针next上进行操做,不用动头指针;若不加头指针的话,在第1个位置插入或者删除第1个元素时,须要动的是头指针。例如:在进行删除操做时,L为头指针,p指针指向被删结点,q指针指向被删结点的前驱,对于非空的单链表: 1.带头结点时 删除第1个结点(q指向的是头结点):q->next=p->next; free(p); 删除第i个结点(i不等于1):q->next=p->next;free(p); 2.不带头结点时 删除第1个结点时(q为空):L=p->next; free(p); 删除第i个结点(i不等于1):q->next=p->next;free(p); 结论:带头结点时,不论删除哪一个位置上的结点,用到的代码都同样;不带头结点时,删除第1个元素和删除其它位置上的元素用到的代码不一样,相对比较麻烦。