重刷数据结构,小题大做,——难道非要头结点吗?
来源:互联网 发布:网红喵大仙的淘宝店 编辑:程序博客网 时间:2024/04/30 07:44
数据结构教材上讲线性表的链式表示时,通常都会引入一个头结点。
带头结点的链表示意图如下:
1.空链表
2.有三个结点的单链表
按照书上的说法,引入头结点有一下两个优点:
无头结点版本:
- 由于开始结点的位置被存放在头结点的指针域中,所以在链表的第一个位置上的操作和在表的其他位置上的操作一致,无须进行特殊处理。
- 无论链表是否为空,其头指针是指向头结点的非空指针,因此空表和非空表的处理也就一致了。
上面的两点的意思是:1.不管是在第一个位置还是其它位置插入一个新的结点,都是在一个结点之后插入(在第一个位置插入新结点,是在头结点后插入);如果没有头结点,在第一个位置插入结点是直接给头指针赋值,而在其它位置插入结点是将结点插入到一个结点之后,这两种处理是不同的。删除结点也类似;
2.空表和非空表中都有结点,所以对空表的处理也就一致了。
你可能会疑惑:难道非得要头指针吗?难道没有头结点,插入、删除、空表的处理就不一致了吗?(貌似有一些同学和我有同样的疑问)
不一致问题的来源
教材给出的插入和删除操作的算法如下:
插入结点
s->next = p->next;
p->next = s;
删除结点
p->next = q->next;
free(q);
从上面可以看出,插入和删除结点的核心操作在于改变结点的next指针域指向。而上面都是通过指向结点的指针来改变结点的next域的,比如p->next = q->next。在没有头结点的情况下,如果要插入和删除第一个结点,需要修改头指针。而头指针不属于任何结点,不能通过修改某个结点的next指针域来修改头指针,所以得特殊处理,这就带来了操作的不一致性。头指针、结点的next指针域,它们虽然都是同一种类型的指针,但得分开处理。
所以问题的来源在于:通过指向结点的指针来访问结点的next指针域,这种方法不适合头指针。
书上的解决方法
书上的解决方法就是引入头结点。这样即使是插入或删除第一个结点,而已不需要修改头指针,而只需要通过头指针修改头结点的next指针域。这样操作都一致了。
新的解决方法
前面说了,问题的来源是“通过指向结点的指针来访问结点的next指针域,这种方法不适合头指针” 。这种访问方法把头指针和结点的next指针当做两个不同的东西。那我们为什么不把头指针和结点的next指针域看做同一种东西呢?它们本就是同样的数据类型啊!这样不是可以一致处理了。把它们都看作指向结点的指针,就可以通过指向指针的指针来修改和访问了。新的算法如下:
插入
s->next = *p; //p指向指针的指针
*p = s;
删除
*p = q->next; //p指向指针的指针
free(q);
小弟通过上面的思路写代码证实了,这种方法可以达到同样的效果。不需要添加头结点,而且操作一致。
代码
下面是两份代码,一份是教材上有头结点的版本,一份是本文提出的无头结点的版本。代码都通过测试!
/**有头结点版本*作者:善良超哥哥*时间:2014-8-16*///LinkList.h#ifndef _LIMKLIST_H_#define _LINKLIST_H_typedef struct LNode{chardata;struct LNode *next;}LNode;typedef struct{LNode*head;//指向第一个结点的指针intlen;//链表长度}LinkList;//创建链表,尾插法void CreateList(LinkList *L);//在第i个位置插入元素eint ListInsert(LinkList *L,int i,char e);//删除第i个位置的元素,并用*e返回int ListDelete(LinkList *L,int i,char *e);//遍历打印链表中的所有元素void ListPrint(LinkList *L);#endif
//LinkList.c/*有头结点的链表实现*/#include "LinkList.h"#include <stdio.h>#include <stdlib.h>//创建链表,尾插法void CreateList(LinkList *L){L->head = malloc(sizeof(LNode));//创建头结点L->head->next = NULL;L->len = 0;//开始输入字符,创建链表,以#结束char c;LNode *p = L->head;while((c = getchar()) != '#'){LNode *s = malloc(sizeof(LNode));s->data = c;p->next = s;p = s;++(L->len);}p->next = NULL;}//在第i个位置插入元素eint ListInsert(LinkList *L,int i,char e){if(i < 1 || i > L->len || L == NULL){return 0;//失败}LNode *p = L->head;for(int j = 1; j < i; j++){p = p->next;}LNode *s = malloc(sizeof(LNode));s->data = e;s->next = p->next;p->next = s;++(L->len);return 1;}//删除第i个位置的元素,并用*返回int ListDelete(LinkList *L,int i,char *e){if(L == NULL || i < 1 || i >= L->len){return 0;//失败了}LNode *p = L->head;for(int j = 1; j < i; j++){p = p->next;}//删除p指针指向的结点的下一个结点LNode *q = p->next;p->next = q->next;free(q);--(L->len);return 1;}//遍历打印链表中的所有元素void ListPrint(LinkList *L){LNode *p = L->head->next;while(p!=NULL){printf("%c",p->data);p = p->next;}printf("\n");}
/**无头结点版本*作者:善良超哥哥*时间:2014-8-16*///LinkList.h#ifndef _LIMKLIST_H_#define _LINKLIST_H_typedef struct LNode{chardata;struct LNode *next;}LNode;typedef struct{LNode*head;//指向第一个结点的指针intlen;//链表长度}LinkList;//创建链表,尾插法void CreateList(LinkList *L);//在第i个位置插入元素eint ListInsert(LinkList *L,int i,char e);//删除第i个位置的元素,并用*e返回int ListDelete(LinkList *L,int i,char *e);//遍历打印链表中的所有元素void ListPrint(LinkList *L);#endif
//LinkList.c
#include "LinkList.h"
#include <stdio.h>
#include <stdlib.h>
//创建链表,尾插法
void CreateList(LinkList *L)
{
L->head = NULL;
L->len = 0;
//开始输入字符,创建链表,以#结束
char c;
LNode **p = &(L->head);
while((c = getchar()) != '#')
{
LNode *s = malloc(sizeof(LNode));
s->data = c;
(*p) = s;
p = &(s->next);
++(L->len);
}
(*p) = NULL;
}
//在第i个位置插入元素e
int ListInsert(LinkList *L,int i,char e)
{
if(i < 1 || i > L->len || L == NULL)
{
return 0;//失败
}
LNode **p = &(L->head);
for(int j = 1; j < i; j++)
{
p = &((*p)->next);
}
LNode *s = malloc(sizeof(LNode));
s->data = e;
s->next = *p;
*p = s;
++(L->len);
return 1;
}
//删除第i个位置的元素,并用*返回
int ListDelete(LinkList *L,int i,char *e)
{
if(L == NULL || i < 1 || i >= L->len)
{
return 0;//失败了
}
LNode **p = &(L->head);
for(int j = 1; j < i; j++)
{
p = &((*p)->next);
}
//删除p指针指向的结点
LNode *q = *p;
*p = q->next;
*e = q->data;
free(q);
--(L->len);
return 1;
}
//遍历打印链表中的所有元素
void ListPrint(LinkList *L)
{
LNode *p = L->head;
while(p!=NULL)
{
printf("%c",p->data);
p = p->next;
}
printf("\n");
}
算法对比
0 0
- 重刷数据结构,小题大做,——难道非要头结点吗?
- 数据结构——单链表的头指针、头结点…
- 数据结构——链表(头指针、头结点)
- SDNUOJ刷题杂谈(—)小题大做
- 数据结构—头指针和头结点分析
- 数据结构——单链表头指针与头结点
- 数据结构——链表(头结点循环链表)
- 数据结构头结点链表
- 数据结构-线性表-头指针&头结点
- 数据结构-线性表-头指针&头结点
- 数据结构中的头结点和头指针
- 数据结构之—线性表之—浅谈单链表有头结点和无头节点
- 数据结构与算法分析——带有头结点的单链表的实现(C语言)
- 头指针、头结点、首元结点(数据结构)
- 难道非要买正版的闪客精灵才可以解析FLASH吗
- 难道ubuntu和fedora非要搞垮linux?
- 链表——头结点式1
- 链表——头结点式2
- gdb 多线程调试
- 中断 陷阱 软中断
- Java中equals和==的区别
- POJ 1207 The 3n + 1 problem(水题)
- ld_preload && ld_debug
- 重刷数据结构,小题大做,——难道非要头结点吗?
- 漫步校园
- 变长数据结构及其应用
- Oracle层次查询的基本用法
- linux内核的生成过程, vmlinux调试分析
- 【140817】类似红警画面的VC游戏源码,一个模块
- ISO给UIImageView增加点击事件
- Dividing+POJ+01背包问题
- 我的太鼓