数据结构与算法之----链表

时间:2021-09-07 09:47:23

11.单链表的读取

获得链表第i个数据的算法思路

1.声明一个结点p指向链表的第一个结点,初始化j从1开始;

2.当j<i时,就遍历链表,让p的指针向后移动,不断指向下一结点,j+1;

3.若到链表末尾p为空,则说明第i个元素不存在;

4.否则查找成功,返回结点p的数据

说白就是从头开始找,知道第i个元素为止.时间复杂度取决于i的位置,当i为1时不需要遍历.当i=n时则遍历n-1次才可以.因此最坏情况的时间复杂度是O(n).

不能用for循环的原因是单链表的结构中并没有严格定义表长,所以不知道要循环多少次.

核心思想就是:工作指针后移. (不是很懂)

 

12.单链表的插入

假设存储元素e的结点为s,要实现结点p,p->next和s之间的逻辑关系的变化,

p(ai 地址) ——— > ai+1 地址 

s(e 地址) 想插入到ai和ai+1之间 

思路:顺序存储结构在插入或删除数据元素很复杂的情况下我们引用了链表,在插入结点时如题所示我们只需要改动p结点的指针域和s结点的指针域就OK,让p->next指向s 让s->next指向ai+1.

s->next = p->next; //原本p的指针域指向的是ai+1,但是由于题目需求,让s的指针域指向ai+1.所以赋值给s->next

p->next= s;  //p的指针域应该指向s,所以赋值

提问:如果这两句代码更换位置呢?

如果先写p->next = s的话,的确能做到在p后面是s,但是问题是s结点的指针域无法指向ai+1,因为原来指向的指针被覆盖了

 

13.单链表的删除

p(a1 地址) ——— > q(a2 地址) ——— > m(a3 地址)

删除q结点

思路:直接把q->next赋值给p->next,然后q->data 置为0?

p->next = q->next;

q->data = 0;

 

14.效率问题

对于插入或删除数据越频繁的操作,单链表的优势就更明显

 

15.单链表的整表创建

思路:初始化一个单链表,让头结点的指针域的指针为NULL .然后依次插入元素.

头插法建立单链表 : 从空表开始生成新的结点.读取数据存放到新结点的数据域中.然后将新结点插入到当前链表的表头上,直到结束为止.

1.先让新结点的next指向头之后

2.让表头的next指向新结点

存在的问题:生成的链表中结点的次序和插入时的次序是相反的

尾插法建立单链表 : 区别就是在结尾插入新的结点

 

16.单链表的整表删除

思路:依次删除结点

代码:(*L)->next  = NULL;

 

17.单链表结构与顺序存储结构的优缺点

存储分配方式:

单链表: 采用链式存储结构,用一组任意的存储单元存放线性表的元素

顺序存储: 一段连续的存储单元依次存储线性表中的数据元素

时间性能:

    查找:

单链表: O(n)

顺序存储: O(1)

    插入和删除:

单链表: 在计算出某个位置的指针之后,插入和删除的时间复杂度仅为O(1)

顺序存储: 需要平均移动表长一半的元素,时间复杂度为O(n)

空间性能:

单链表: 不需要分配存储空间,只要有就能分配(即使是零碎空间),元素个数不受限制

顺序存储: 需要预先分配存储空间.大了造成空间浪费,小了容易发生溢出.

 

综上所述:

1.如果线性表需要频繁查找而不是大量进行插入和删除操作时,最好采用顺序存储结构(用户个人信息,除了一开始是插入数据外,剩下的都是读取数据.最好用顺序存储结构)

2.如果线性表需要频繁插入和删除操作时,最好采用单链表结构(游戏中装备列表随时更换,大量进行增加或删除操作,最好用单链表)

3.如果线性表中的元素个数变化比较大或者根本不知道有多大时,最好采用单链表结构.这样做不需要考虑到存储空间大小的问题

4.如果事先知道线性表的大致长度,最好采用顺序存储结构

 

18.静态链表

用数组描述的链表叫做静态链表.

我们对数组的第一个和最后一个元素做特殊处理,它们的data不存放数据

通常把未使用的数组元素成为备用链表

静态链表其实就是为了给没有指针的编程语言设计的一种实现单链表功能的方法

 

游标 5  2  3   4  5  6  7 …   1

数据      A  C  D  E          …

下标  0  1   2  3  4  5  6 …  999

 

游标和下标对应

 

19.腾讯单链表面试题

问题:快速找到未知长度单链表的中间结点

思路:

普通方法:

就是遍历一遍单链表以确定单链表的长度L.然后再次从头结点出发循环L/2次找到单链表的中间结点.算法复杂度为0(L+L/2) = O(3/2L);

高级方法:

利用快慢指针原理:设置两个指针都指向单链表的头结点.其中快指针的移动速度是慢指针的2倍.当快指针指向末尾结点的时候,慢指针一定会在中间结点.这也是标尺的思想

代码:

LinkList search,mid;

mid = search = L;

while(search->next != NULL)

{

if(search->next->next != NULL)

{

search = search->next->next;

mid = mid->next;

}

else

{

search = search->next;

}

}

*e = mid->data;

return OK;

 

20.循环链表

正常的单链表是不可逆的,如果我们不能从头结点出发,那么我们无法访问到全部结点.所以针对这个问题,我们只需要将单链表中的终端结点的指针从指向空指针改为指向头结点,就OK了.

将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表行成一个环,这种头尾相接的单链表称为单循环链表,简称循环链表