本文章主要结合习题来回顾一下线性表中链表的相关知识,这方面还可以看我另一篇文章
线性表——Data Structure(C语言描述)
单链表
typedef struct LNode{ //定义单链表结点类型
ElemType data; //数据域
struct LNode *next; //指针域
}LNode,*LinkList;
双链表
typedef struct DNode{ //定义双链表结点类型
ElemType data; //数据域
struct DNode *prior,*next; //前驱和后继指针
}DNode,*DLinklist
循环单链表
循环单链表和单链表的区别在于,表中最后一个结点的指针不是NULL,而改为指向头结点,从而整个链表形成一个环。
在循环单链表中,表尾结点*r的next域指向L,故表中没有指针域为NULL的结点,因此,循环单链表的判空条件不是头结点的指针是否为空,而是它是否等于头指针。
循环单链表的插入、删除算法与单链表的几乎一样,所不同的是如果操作实在表未进行,则执行的操作不相同,以让单链表继续保持循环的性质。当然,正式因为循环单链表是一个“环”,因此,在任何一个位置上的插入和删除操作都是等价的,无须判断是否是表尾
在单链表中只能从表头结点开始往后顺序遍历整个链表,而循环单链表可以从表中仁义街店开始遍历整个链表。有时对单链表常做的操作是在表头和表尾进行的,此时可对循环单链表不设头指针而仅设尾指针,从而使得操作效率更高。其原因是若设的是头指针,对表尾进行操作需要O(n)的时间复杂度,而如果设的是尾指针r,r->next即为头指针,对于表头与表尾进行操作都只需要O(1)的时间复杂度。
循环双链表
由循环单链表的定义不难推出循环双链表,不同的是在循环双链表中,头结点的prior指针还要指向表尾结点。
在循环双链表L中,某结点*p为尾结点时,p->next==L,当循环双链表为空表时,其头结点的prior域和next域都等于L
静态链表
静态链表是借助数组来描述线性表的链式存储结构,结点也有数据域data和指针域next,与前面所讲的链表中的指针不同的是,这里的指针是结点的相对地址(数组下标),又称游标。和顺序表一样,静态链表也要预先分配一块连续的内存空间。
另外这里有一点需要注意的是,在动态链表中,结点的申请和释放分别借用malloc()和free()两个函数来实现。在惊天链表中,操作的是数组,不存在像动态链表的结点申请和释放问题,所以我们需要自己实现这两个函数,才可以做插入和删除的操作
#define MaxSize 50
typedef struct{
ElemType data;
int next;
}SLinkList[MaxSize];
注意,题中如果没有强调有序顺序表,那么默认无序 比如下面的第四题
一维数组可以是静态分配的,也可以是动态分配的。在静态分配时,由于数组的大小和空间实现已经固定,一旦空间占满,再加入新的数据将产生溢出,就会导致程序崩溃。
而动态分配时,储存数组的空间实在程序执行过程中通过动态存储分配语句分配的,一旦数据空间占满,可以另外开辟一块更大的存储空间,用以替换原来的存储空间,从而达到扩充存储数组空间的目的,而不需要一次性地划分所有所需空间给线性表。
顺序表和链表的比较
1。存取方式
顺序表可以顺序存取,也可以随机存取,链表只能从表头顺序存取元素
2。逻辑结构与物理结构
采用顺序存储时,逻辑上系那个林的元素,其对应的物理存储位置也相邻。而采用链式存储时,逻辑上相邻的元素,其物理存储位置则不一定相邻,其对应的逻辑关系是通过指针链接来表示的。这里请注意区别存取方式和存储方式。
3。查找、插入和删除操作
对于按值查找,当顺序表在无序的情况下,两者的时间复杂度均为O(n);而当顺序表有序时,可用折半查找,此时时间复杂度为O(log2n)。
对于按号查找,顺序表支持随机访问,时间复杂度为O(1),而链表的平均时间复杂度为O(n)。顺序表的插入、删除操作,平均需要移动半个表长的元素。链表的插入、删除操作,只需要修改相关结点的指针域即可。由于链表每个结点带有指针域,因而在春粗空间上比顺序存储要付出较大的代价,存储密度不够大。
4。空间分配
顺序存储在静态存储分配情况下,一旦存储空间装满就不能扩充,如果再加入新元素将出现内存溢出,需要预先分配足够大的存储空间。预先分配过大,可能会导致顺序表后部大量闲置;预先分配过小,又会造成一处。动态存储分配虽然存储空间可以扩充,但是需要移动大量元素,导致操作效率降低,而且若内存中没有更大块的连续存储空间将导致分配失败。链式存储的结点空间只需要在需要的时候申请分配,只要内存有空间就可以分配,操作灵活、高效。
在实际中应该怎样选取存储结构呢?
1。基于存储的考虑
对线性表的长度或存储规模难以估计时,不宜采用顺序表;链表不用实现估计存储规模,但链表的存储密度较低,显然链式存储结构的存储密度是小于1的。
2。基于运算的考虑
在顺序表中按序号访问ai的时间复杂度为O(1),而链表中按需要访问的时间复杂度为O(n),所以如果经常做的运算是按需要访问数据元素,显然顺序表优于链表。
在顺序表中做插入、删除操作时,平均移动表中一半的元素,当数据元素的信息量较大且表较长时,这一点是不能忽视的;在链表中做插入、删除操作时,虽然也要找插入位置,但操作主要是比较操作,从这个角度考虑显然后者优于前者。
3。基于环境的考虑
顺序表容易实现,任何高级语言中都有数组类型;链表的操作是基于指针的,相对来说,前者实现较为简单,这也是用户考虑的一个因素。
总之,两种存储结构各有长短,选择哪一种由实际问题的主要因素决定。通常较稳定的线性表选择顺序存储,而频繁做插入、删除操作的线性表(即动态性较强)宜选择链式存储。
关于线性表的顺序存储结构和链式存储结构的描述中,正确的是()
1。线性表的顺序存储结构优于其链式存储结构
2。链式存储结构比顺序存储结构更能方便的表示各种逻辑结构
3。如频繁使用插入和删除结点操作,顺序存储结构更优于链式存储结构
4。顺序存储结构和链式存储结构都可以进行顺序存取
A. 123 B. 24 C. 23 D.34
关于线性表说法正确的是()
1。顺序存储方式只能用于存储线性结构
2。取线性表的第i个元素的时间与i的大小有关
3。惊天链表需要分配较大的连续空间,插入和删除不需要移动元素
4。在一个长度为n的有序单链表中插入一个新节点并仍保持有序的时间复杂度为O(n)
5。若用单链表来表示队列,则应该选用带尾指针的循环链表
A. 12 B. 1345 C. 45 D.345
设现行宝中有2n个元素,()在单链表上实现要比在顺序表上实现效率更高。
A.删除所有值为x的元素
B.在最后一个元素的后面插入一个新元素
C.顺序出处前k个元素
D.交换第i个元素和第2n-i-1个元素的值(i=0,…n-1)
给定有n个元素的一维数组,建立一个有序单链表的最低时间复杂度是()
A.O(1)
B.O(n)
C.O(n^2)
D.O(nlog2n)
单链表中,增加一个头结点的目的是为了()
A.使单链表至少有一个节点
B.标识表结点中首结点的位置
C.方便运算的实现
D.说明单链表是线性表的链式存储
在一个长度为n的带头结点的单链表h上,设有尾指针r,则执行()操作与链表的表长有关。
A.删除单链表中的第一个元素
B.删除单链表中最后一个元素
C.在单链表第一个元素前插入一个新元素
D.在单链表最后一个元素后插入一个新元素
对于一个头指针为head的带头结点的单链表,判定该表尾空表的条件是(),对于不带头结点的单链表,则判定空表的条件为()
A.head==NULL
B.head->next==NULL
C.head->next ==head
D.head!=NULL
某线性表中最常见的操作是在最后一个元素之后花擦入一个元素和删除第一个元素,用()存储方式最省时间。
A.单链表
B.仅有头指针的单循环链表
C.双链表
D.仅有尾指针的单循环链表
在双链表中向p所指的结点之前插入一个结点q的操作为()
A.p->prior=q;q->next=p;p->prior->next=q;q->prior=p->prior;
B.q->prior=p->prior;p->prior->next=q;q->next=p;p->prior=q->next;
C.q->next=p;p->next=q;q->prior->next=p;q->next=p;
D.p->prior->next=q;q->next=p;q->prior=p->prior;p->prior=q;
在双向链表存储结构中,删除p所指的结点时必须修改指针()
A.p->llink->rlink=p->rlink;p->rlink->llink=p->llink;
B.p->llink=p->llink->llink;p->llink->rlink=p
C.p->rlink->llink=p;p->rlink=p->rlink->rlink;
D.p->rlink=p->llink->llink;p->llink=p->rlink->rlink;
单头结点的双循环链表L为空的条件是()
A.L->prior==L&&L->next==NULL
B.L->prior==NULL&&L->next==NULL
C.L->prior==NULL&&L->next==L
D.L->prior==L&&L->next==L
一个链表最常用的操作实在末尾插入结点和删除结点,则选用()最节省时间。
A.带头结点的双循环链表
B.单循环链表
C.带尾指针的单循环链表
D.单链表
静态链表中指针表示的是()
A.下一个元素的地址
B.内存储器地址
C.下一个元素在数组中的位置
D.左链接或右链接指向的元素的地址
B
两种存储结构有不同的适用场合,不能说谁坏,1错误,链式存储用指针表示逻辑结构,而指针的设置是任意的,雇可以很方便的表示各种逻辑结构;顺序存储只能用物理上的邻接关系来表示逻辑结构,2正确。在顺序存储中,插入和删除结点需要大量的移动元素,效率较低,3的描述刚好相反。顺序存储结构既可以随机存取也能顺序存取,而链式结构只能进行顺序存取,4正确
D
顺序存储方式同样适用图和树的存储,如满二叉树的顺序存书,1错误。若采用线性表采用顺序存储方式,则取第i个元素的时间与i的大小无关,2错误。3是静态链表的特有性质。单链表只能顺序查找插入位置,时间复杂度为O(N),若为顺序表,可以采用折半查找,时间复杂度可降至(log2n),4正确。队列需要在表头删除元素,表尾插入元素,故采用带位置真的循环单链表较为方便,插入和删除的时间复杂度都是O(1),5正确
A
A中对于单链表和顺序表上实现的时间复杂度都为O(N),但后者要移动很多元素,所以在单链表上实现效率更高。B和D效率刚好相反,C无区别
D
若先建立链表,然后依次直接插入建立有序表,则每插入一个月uansu就需遍历链表寻找插入位置,这就是链表的插入排序,时间复杂度为O(n^2),若先将数组排好序,然后建立链表,建立的时间复杂度为O(n)而数组排序的最少时间复杂度为O(nlog2n),故时间O(nlog2n),选D
C
单链表设置头结点的目的是为了方便运算的实现,主要好处体现在:第一,有头结点后,插入和删除数据元素的算法同意了,不再需要判断是否在第一个元素之前插入或删除第一个元素;第二,无论链表是否为空,其头指针是指向头结点的为空指针,链表的头指针不变,因此空表和费控表的处理也就统一了。
B
删除单链表的最后一个结点需要置其前驱结点的指针域为NULL,故需要的时间为O(n),与表长有关。其他操作与表长无关,可以自行模拟
B,A
在带头结点的单链表中,头指针指向头结点,头结点的next域指向第一个元素结点,head->next==NULL表示该单链表为空。在不带头结点的单链表中,head直接指向第一个元素结点,head==NULL表示该单链表为空。
D
在最后一个元素之后插入元素,需要先找到最后一个元素,故A B C的时间复杂度为O(n)。B因为没有特殊的指针指示头结点或者尾结点。故需从某一结点向固定的方向顺序依次搜索插入和删除的位置,时间复杂度也为O(n)。D的两种算法的时间复杂度都是O(1)
D
为了在p之前插入结点q,可以将p的前一个结点的next域指向q,将q的next域指向p,将q的prior域指向p的前一个结点,将p的prior域指向q。仅D满足条件。
A
与上题类似,只不过这里是删除一个结点,注意将P的前、后两结点链接起来。关键是要保证在链表的修改过程中不断链。
请注意对比上面两题,弄清楚双链表的插入和删除的方法。
D
循环单链表L判空的条件是头结点(头指针)的prior和next域都指向它自身
A
在链表的末尾插入和删除一个结点时,需要修改其相邻结点的指针域。而寻找尾结点以及尾结点的前驱结点,只有带头结点的双循环链表所需要的时间最少
C
静态链表中的指针又称游标,指示下一个元素在数组中的下标。