在不同的数据结构中链表是否带头结点的分析

一.链表

学习数据结构链表的时候,就有区分 带头结点的链表和不带头结点的链表

当时写完带头结点的链表的基本操作算法后,又写了一遍不带头结点的链表的基本操作。

发现是否带头结点的区别主要体现在2个操作的算法上:插入和删除

不带头结点的链表的插入和删除操作因为涉及对第一个结点插入删除,会改变头指针的值,需要对第一个结点单独分析,链表头指针形参上也因此上加入引用或者指针。

所以相对带头结点的链表操作上不带头结点的链表会更加麻烦。

插入算法上的差异:

 1 //带头结点的链表
 2 int insert(LINK H,int pos,STU x)/*插入*/
 3 {
 4   LINK p,s;int k = 0;
 5   p = H;
 6   while(p != NULL && k < pos-1)
 7   {
 8       p = p->next;
 9       k++;
10   }
11   if(p==NULL || k > pos-1)
12   {
13       printf("the position is illegal...insert fained\n");
14       return 0;
15   }
16   s = new NODE;
17   s->data = x;
18   s->next = p->next;
19   p->next = s;
20   printf("Insert finished...\n");
21   return 1;
22 }
23 //不带头结点的链表
24 int insert(LINK &H,int pos,STU x)//插入
25 {
26     LINK p = H,s;
27     int k=1;
28     if(pos == 1)//插入pos是1的情况
29     {
30         s = new NODE;
31         s->data = x;
32         s->next = H;
33         H = s;
34         return 1;
35     }
36     while(p != NULL && k < pos-1)
37     {
38         p = p->next;
39         k++;
40     }
41     if(p == NULL || k > pos-1)
42     {
43         printf("the position is illegal,insert failed....\n");
44         return 0;
45     }
46     s = new NODE;
47     s->data = x;
48     s->next = p->next;
49     p->next = s;
50     return 1;
51 }

从上面的代码可以清晰的看到不带头结点的链表的插入算法的区别:

1.形参上头结点使用了引用,插入位置为第一个位置时需要对头指针的值进行修改。

2.插入需要对第一个结点进行单独分析,相比带头结点的算法多了这段代码:

28   if(pos == 1)//插入pos是1的情况
29     {
30         s = new NODE;
31         s->data = x;
32         s->next = H;
33         H = s;
34         return 1;
35     }

删除算法上的差异:

 1 //带头结点的链表的删除算法
 2 int deletenode(LINK H,int pos)/*删除*/
 3 {
 4     LINK p,q;int k = 0;
 5     p = H;
 6     if(H == NULL || H->next == NULL)
 7     {
 8       printf("链表为空,请先创建链表...\n");
 9       return 0;
10     }
11     while(p->next != NULL && k < pos-1)
12     {
13         p = p->next;
14         k++;
15     }
16     if(p->next == NULL || k > pos-1)
17     {
18         printf("位置不合法,删除失败...\n");
19         return 0;
20     }
21     q = p->next;
22     p->next = q->next;
23     free(q);
24     printf("删除完成....\n");
25     return 1;
26 }
27 //不带头结点的删除算法
28 int deletenode(LINK &H,int pos)//删除
29 {
30     LINK p = H,q;int k = 1;
31     if(H == NULL) //空链表不执行删除操作
32     {
33         printf("this is an empty link list,can not execute the delete function\n");
34         return 0;
35     }
36     if(pos == 1)
37     {
38         H = H->next;
39         delete p;
40         return 1;
41     }
42     while(p->next !=NULL && k < pos-1)
43     {
44         p = p->next;
45         k++;
46     }
47     if(p->next == NULL || k > pos-1)
48     {
49         printf("the position is illegal,delete failed...\n");
50         return 0;
51     }
52     q = p->next;
53     p = q->next;
54     delete q;
55     return 1;
56 }
57 //以两个算法的区别和插入算法类似,都是因为第一个结点特殊考虑导致

差异原因类似插入算法,此处不再赘述。

二:栈和队列

链栈:实际上就是链表的一种特殊形式,对插入和删除结点进行了约束和明文规定,先进后出,既插入和删除都只能在栈顶完成,栈底不允许操作。

链队列:也是一种链表的特殊形式,与链栈不同的是,队列要求入队只能在队尾,出队只能在队头,也是对插入和删除进行了约束。

还记得上林老师得数据结构这课时,老师只是说了链栈不带头结点,链队列要带头结点,当时有些迷惑,不太明白,只是按部就班的码代码时按要求做了。

后来百度了一下为什么,发现百度上没有相关的东西,只是说到链栈就是不带头结点,链队列就得带头结点。

下面的是一些自己的思考,正确性尚待考证,但是估计八九不离十吧。

首先链栈,因为链栈只涉及到栈顶的操作,既相当于插入和删除都是在栈顶,所以带不带头结点都无所谓,算法都只需要对第一个结点的情况进行考虑

但是出于空间的考虑,所以链栈不带头结点,并且认为链表头为栈顶,入栈就是链表的头插,出栈就是删除头结点。

图为入栈示意图:

嗯,也正是为了方便,所以链栈里面的插入操作都是头插

然后链队列,链队列一般有两种存储结构:

1.双头不循环链队列

2.带尾指针的循环队列

无论链队列是那种存储结构,每插入和删除一个结点都需要对头指针进行修改,并且都需要带上头结点。

链队列带上头结点是为了操作方便,否则当不带头结点之后,可以想象每当处理第一个结点时,操作会变得相当的复杂,并且头指针还得赋值为NULL。

比如:第一种双头不循环的队列,出队列时,当队列里面只有一个结点时,Q.front,Q.rear都会和之前的出队列操作不同。

同理尾指针的循环队列也是。

总的来说,既带头结点可以避免以为因为第一个结点的操作而带来的特殊情况,方便编程。

嗯嗯,所以在不同的实际需求情况下,数据结构需要灵活变化,有时候会是几种数据结构的组合,具体情况要具体分析,体会到是否带头结点的差异。

才能构造出好的结构,写出好的程序。

时间: 2024-10-10 03:24:00

在不同的数据结构中链表是否带头结点的分析的相关文章

链表不带头结点的实现

首先要强调的是,ADT没有变化,只是实现上的变化. 不带头结点的实现比带头结点的实现稍微复杂一些,尤其体现在插入(第58-60行)和删除(第88-91行),要单独处理表头改变的情况.而带头结点的链表表头不会变化,总是指向头结点. 其他操作的不同之处同学们自行比较. 下面的问题会帮助你的理解: 第83行 while(p && j < i - 1) {   而教材上带头结点的实现为while(p->next && j < i - 1) { 为什么会有这种区别?

C语言实现单链表(带头结点)的基本操作

我在之前一篇博客<C语言实现单链表(不带头结点)的基本操作>中具体实现了不带头结点的单链表的11种操作:如计算链表长度.初始化.创建链表.清空链表等等.但是在实际使用中,带头结点的单链表往往比不带头结点的单链表用的更多,使用也更为方便.因为不用单独考虑第一个节点的情况了,第一个节点和其他后续节点的处理全都一样了,简化操作.这篇博客将会来实现带头结点的单链表的11种操作.代码上传至: https://github.com/chenyufeng1991/LinkedList_HeadNode  .

不带头结点的链表的增删改查

这两天都在学习数据结构中的链表操作,觉得有很多东西想跟大家分享,通常写链表一般会创建一个带头结点的链表,带头结点链表非常容易的进行链表的遍历,而不带头结点的链表,一马虎就会指错,所以在这里跟大家分享下我进行没有头结点的创建时的心得! 1.创建无头结点链表(头插) 2.删除中间节点(不是第一个也不是最后一个) 1 #include<stdio.h> 2 #include<malloc.h> 3 typedef char ElemType; 4 typedef struct node

带头结点的单链表操作说明

一.单链表简介 相对于以数组为代表的"顺序表"而言,单链表虽然存储密度比较低(因为数据域才是我们真正需要的,指针域只是用来索引,我们并不真正需要它),但是却具有灵活分配存储空间.方便数据元素的删除.方便元素插入等优点 单链表是线性表链式存储的一种,其储存不连续.单链表的数据结构中包含两个变量:数据和指向下一结点的指针.一个结点只知道下一个结点的地址.一个单链表必须有一个头指针,指向单链表中的第一个结点.否则链表会在内存中丢失. 一般的链表可以不带头结点,头指针直接指向第一个节点,如下图

对带头结点的单链表的简单操作

#pragma once #include<stdio.h> #include<stdlib.h> #include<assert.h> #include<memory.h> #define DataType int           //int 可以改写为其它数据类型 typedef struct Node { DataType data; struct Node *next; }Node,*pNode;          //定义结点结构体      

数据结构中的堆栈和内存中的堆栈

内存常用的区域分类:栈区(stack).堆区(heap).全局区(static区).文字常量区.程序代码区. 栈区:由编译器自动分配和释放,遵循”后进先出“的规则.在函数调用时,第一个进栈的是主函数中的下一条指令地址,然后是函数的各个参数(大多数C编译器中,参数从右往左入栈),然后是函数的局部变量.静态变量不入栈. 堆区:一般由程序员分配和释放,若程序员不释放,程序结束时”可能“由操作系统回收.一般在堆的头部用一个字节存放堆的大小.分配方式类似链表. 数据结构中的堆栈: 栈:遵循”后进先出“规则

关于内存中栈和堆的区别(非数据结构中的堆和栈,区别)

本文转载自:http://blog.csdn.net/fenghuayoushi/article/details/6074666 ----------------------------------------------------------------------------------------------------------------------- 下面内容转自:http://www.cnblogs.com/gesenkof99/archive/2009/04/14/14356

数据结构中的堆和栈 与 内存分配中的堆区和栈区 分析

比較全面的总结了诸多版本号,知识无国界.感谢各位的辛勤劳作. 在计算机领域,堆栈是一个不容忽视的概念,我们编写的C/C++语言程序基本上都要用到.但对于非常多的初学着来说,堆栈是一个非常模糊的概念. (1) 数据结构的栈和堆 首先在数据结构上要知道堆栈,虽然我们这么称呼它,但实际上堆栈是两种数据结构:堆和栈. 堆和栈都是一种数据项按序排列的数据结构. 栈就像装数据的桶或箱子 我们先从大家比較熟悉的栈说起吧.它是一种具有后进先出性质的数据结构,也就是说后存放的先取.先存放的后取.这就如同我们要取出

[Data Structure] 数据结构中各种树

数据结构中有很多树的结构,其中包括二叉树.二叉搜索树.2-3树.红黑树等等.本文中对数据结构中常见的几种树的概念和用途进行了汇总,不求严格精准,但求简单易懂. 1. 二叉树 二叉树是数据结构中一种重要的数据结构,也是树表家族最为基础的结构. 二叉树的定义:二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒.二叉树的第i层至多有2i-1个结点:深度为k的二叉树至多有2k-1个结点:对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0=