大家好啊O(∩_∩)O~~,可能大家是被标题吸引进来的吧,在这里先道一声歉,博主会尽力不做标题党,达成点进文章的大家的期待的!如果真的让您有收获的话,还请点个三连支持一下呗~今后会持续更新优质内容,为您答疑解惑的~~~~
话不多说,马上开始!另外,别忘记看结尾处的知识总结喔(⊙o⊙)~~
———————————————————————————————————————————
目录
1. 移除链表元素
题目:
分析:
首先要仔细分析题目所给出的几个示例
可以分为大致两种情况:
1.要删除的节点在头部
2.要删除的节点不在头部
那么该如何进行删除操作呢?
如下图:
这是一个最常规的状况:
要删除的节点不在头部,此时cur 指向的结构体内存储的数值就是要删除的值。我们需要让prev->next==cur->next ,free掉cur ,然后让cur=prev->next。
当然这里可以采用的方式有很多,比如可以定义一个临时变量记住要删除的结点的位置,之后让cur=cur->next ,prev->next=cur ,再free。
如果要删除的节点在头部呢?
很简单,只需要让head=head->next ,然后free即可。
如果不需要进行删除操作的话,只需要让prev和cur迭代下去就可以:prev=cur,cur=cur->next。
将以上情况包装进一个循环即可,循环的条件是cur不为NULL。
关于链表为空这种特殊情况,循环不会进入,会直接返回head,也就是NULL,所以不必另外考虑了
代码实现:
——————————————————————————————————————————
2.反转链表
题目:
分析:
第一种方法:
使用头插。只需要一个新的头,然后遍历原链表,依次头插即可。不过在头插的时候,需要有一个指针next来记录cur的下一个位置,协助链表的遍历。还要注意一下特殊情况:链表为空。
代码:
不过在这里链表为空的情况也可以被解决:如果链表为空,循环不会进入,会直接返回newhead,此时newhead==NULL,满足题意。
分析:
第二种方法:
不使用新的头,而是直接在原地反转链表,此时需要三个指针,一个是需要改变指向的指针n2,一个是要被指向的节点的指针n1,另一个指针n3是要记住下一个节点,防止在反转的时候与链表的后半段丢失联系。
当n2为空时,结束循环,返回n1。
代码:
———————————————————————————————————————————
3.链表的中间结点
题目:
分析:
暴力遍历链表的节点数量可以解决,但是效率很低,所以在此不介绍
我们要用快慢指针的方法来解决:
所谓快慢指针,顾名思义就是两个指针,一个指针走的快,一个指针走的慢。
针对此题,我们要让快指针fast一次走两步,慢指针slow一次走一步,这样在快指针走到链表末尾的时候,慢指针所指向的就是链表的中间结点了。
但是还是要分情况讨论一下:
1.链表节点的数量为偶数
可以看到当fast指向NULL的时候,slow正好指向了第二个中间节点。
2.链表节点数量为奇数
可以看到,当fast->next指向NULL的时候,slow指向中间节点,并且此时必须退出循环,因为如果继续迭代fast就会对空指针进行解引用,会导致程序崩溃。
3.链表为空
此时不会进入循环,可以被归到常规情况里面
———————————————————————————————————————————
对以上情况进行了讨论后,while循环的条件就应该是fast!=NULL&&fast->next!=NULL。出循环后返回slow就好。
——————————————————————————————————————————
4.链表中倒数第k个结点
题目:
分析:
这题也可以暴力解决,但是太挫了。
我们采用双指针法:让快指针先走k步,慢指针=head,当快指针为空时,慢指针就指向倒数第k个结点了。
不过别忘了还有特殊情况:
1.如果链表本身为空,直接返回NULL
2.如果k的值没有实际意义,比方说:k的值大于链表的长度,此时是不存在倒数第k个节点的。在让快指针走k步的时候,就会对空指针解引用,我们要在这之前返回NULL。
代码:
———————————————————————————————————————————
5.合并两个有序链表
题目:
分析:
大体思路:我们需要重新创建一个头指针,然后遍历两个链表,比较数值大小后,依次将两个链表的每个节点尾插到新的链表中去,返回新的链表的头指针即可。
细致分析的话要分为两种情况:
第一种:
两个链表都不是空链表,在遍历并插入的时候,必然会有一个链表先被遍历完,此时需要对两个链表是否为空进行判断,如果某个链表为空,那么就将另一个链表剩下的节点尾插到新链表。
第二种:
如果有任何一个链表是空的,那么直接返回另一个链表的头指针即可。
———————————————————————————————————————————
代码:
不带哨兵位的头结点:
带哨兵位的头结点:
————————————————————————————
6.链表分割
题目:
分析:
其实我觉得题目的描述是有问题的,不应该是“不能改变原来的数据顺序”,而应该是“不能改变原来的相对数据顺序”,第一次做这个题的时候我就没读明白,出题者语文不好啊O(∩_∩)O~。
哈哈哈哈不闹了。
此题如果只创建一个新的头指针的话,是不好办的。
我们要创建两个头指针head1,head2,尾指针tail1,tail2 : head1用于尾插小于x的值,head2用于尾插大于等于x的值。
最后再将两个链表链接并返回即可。
特殊情况:
如果链表为空,直接返回空即可
另外,还有许多的情况,比如:如果原链表中没有小于x的值的话,那么head1就是空的,进行链接操作(tail1->next=head2)的时候,就会对空指针(tail1==NULL)进行解引用导致崩溃。
所以为了免除这些麻烦,不如直接采用带哨兵位头结点的结构。
代码(有缺陷):
但是运行结果却是:
这简直离谱啊,我们的程序竟然使用了超过限制的内存,一般情况下,所给的内存是完全够用的啊
这个是另一个题占用的内存,只有460KB,这只能说明,我们的代码出错了,很有可能出现了死循环的情况。
这个时候,需要对未通过的例子进行细致的分析,从而找出错误,一定要细致哦!
比如说这个例子:
我们以为是这样:
但实际上是这样:
这样就造成了死循环,那么如何解决呢?
我们只需要让大于等于x的链表的最后一个节点指向NULL就可以了,于是:
———————————————————————————————————————————代码:
———————————————————————————————————————————
7.链表的回文结构
题目:
分析:
找到链表的中间节点,并将此中间节点当做头指针来反转之后的节点,如下图所示:
偶数个节点:
奇数个节点:
可以看到都是可以的。
反转之后,只要依次对比两个链表的数值即可。
如果链表为空或者只有一个节点都直接返回空即可。
代码:
——————————————————————————————————————————
8.相交链表
题目:
分析:
将两个链表遍历到尾节点,并在此过程中计算两个链表的节点数量n1,n2,如果尾节点指针相同,就说明两个链表相交,不相同则直接返回NULL。
如果两个链表相交的话,让比较长的链表先走|n2-n1|步,之后同步走,直到二者某个节点的地址相同,返回即可。
代码:
——————————————————————————————————————————
知识总结(重点):
1.做题的时候,经常会创建新的链表,只要是为了效率,不要吝惜空间。
2.经常会使用多指针法,快慢指针法。
3.有的时候使用带哨兵位的头结点的结构,可以大大简化代码
4.画图一定要足够细致。
5.如果发生了空间不足的错误,很有可能是某个地方发生了死循环。
6.要预防对空指针的解引用操作,多考虑特殊的情况
7.如果发生了错误,不要慌,对着没通过的例子进行逐步细致的分析即可
8.
可以采用这种妙法,让shortlist必然是短的链表,longlist必然是长的链表。
谢谢大家,都看到这里了,如果有收获的话,给个三连吧!