介绍
1、链表问题算法难度不高,但考察代码实现能力。
2、链表和数组都是一种线性结构。
①数组是一段连续的存储空间。
②链表空间不一定保证连续,为临时分配的。
3、链表分类
①根据指针的方向可以分为单向链表和双向链表
②根据有无循环可以分为普通链表和循环链表
链表问题代码实现的关键点:
1、链表调整函数的返回值往往是节点类型。
2、解决链表问题一定要花时间考虑哪些指针变了,一般可以用画图发辅助解决,在变化指针指向之前要保存环境,以免断线。
3、注意边界条件的处理:头节点、尾节点、空节点等特殊节点的处理
链表插入和删除的注意事项
1、要特殊处理空链表和长度为1的链表
2、插入之前要同时找到插入点之前和之后的位置
3、删除时要注意头尾节点及空节点,需要特殊考虑
4、单链表翻转
①为空和为1时要特殊处理
②大量链表问题可以使用额外数据结构来简化过程,但往往空间复杂度为O(1)即可解决
经典题
-
已知有序循环单链表,要求插入num并保持有序
1、如果被插入的链表为空,这node自成循环链表
2、如果不为空,用prior和current记录从head开始的相邻的两个节点,如果node在它们之间,则插入,否者prior和current指向下一个节点,如果循环一圈仍未找到插入位置,则将节点插入到head和rear之间(这时需要注意头结点的选择) -
给定链表中节点node,但头节点未知,如何删除node。要求:时间复杂度为O(1)
1、如果是双向链表,则可通过prior找到前一个节点,删除比较容易
2、如果是单向链表,则一般通过替换法解决,即将需要删除节点的值替换为下一个节点的值,然后通过删除下一个节点来等效删除待删节点
替换法的问题:1)如果需要删除的节点为尾节点,则该方法实效,2)工程上节点比较复杂时,复试节点的开销比较大,3)工程上有些情况禁止使用替换法,因为节点可能为外界提供服务,如果发生替换,则会给外界带来问题。
替换法的一个不可行的替代方法:当要删除尾节点时,可能会考虑从内存的角度将该区域置空来达到与删除该节点等效的效果,但这是不可行的,因为内存中空是特殊的区域,如果想将某区域置空,则必须找到指向该区域的指针,而本题中这个指针是不可见的。 -
给定一个链表的的头节点,再给定一个数num,类似于荷兰国旗问题,将小于num的节点放在左边,等于num的节点放在中间,大于num的节点放在右边
简单做法:将链表所有节点放在一个数组中,然后解决类似于荷兰国旗的问题,最后还得重构链表
推荐方法:时间复杂度为O(1),将head分为3个链表,三个链表中的元素分别大于,等于和小于num -
给定两个有序链表,请打印它们的公共值部分
1、首先应该判断是否为空,只要有一个链表为空,就返回空
2、都不为空时,需要同时遍历两个链表,根据当前节点大小来分别调整遍历顺序,这样寻找第一个公共节点,之后输出公共节点,以后只要有一个条链表结束操作结束。 -
给定单链表,请将该链表以k个节点为单位逆序调整,如果剩余节点不足k个则不做调整
首先判断:如果链表为空或长度为1或k<2则不需要调整。
1、时间复杂度为O(n), 空间复杂度为O(k):利用栈积累k个元素后出栈,一直这么继续下去,直到完成逆序
可能存在的问题有:1)不足k,如何处理,2)后一组需要和之前组连接,而第一组不需要,所以要特殊处理第一组,3)每一组出栈后需要链表next指针重连操作,4)整个功能会改变链表头节点,所以函数需要返回节点类型,最后返回调整后的头节点
2、时间复杂度为O(n),空间复杂度为O(1):这个方法和方法一类似,但是此处不借助栈实现,实现更复杂点 -
给定链表头head,和数number,把所有等于number的节点删掉
整个过程看成构造链表的过程,只接值不等于number节点,此题要考虑边界情况 -
判断一个链表是否为回文
1、空间复杂度O(n):申请一个栈,遍历链表并将值入栈,遍历完成后,进行出栈,出栈同时重新遍历遍历链表,比较每个出栈值和遍历的当前节点是否相等,如果有一个不等就不是回文。
2、空间复杂度O(n / 2):申请一个栈,这次需要两个指针遍历链表,慢指针步长为1,快指针步长为2,慢指针遍历到的节点值要同时入栈,1)当链表有奇数个节点时,快指针到达尾节点表示慢指针到达中间节点,将中间节点出栈后的剩余节点出栈的同时慢指针继续遍历链表比较每个出栈值和遍历的当前节点是否相等,如果有一个不等就不是回文。2)当链表有偶数个节点时,快指针到达尾节点的前一个节点即表示慢指针到达中间节点,此时开始出栈操作,节点出栈的同时慢指针继续遍历链表比较每个出栈值和遍历的当前节点是否相等,如果有一个不等就不是回文。
3、空间复杂度为O(1):还需要两个指针遍历链表,慢指针步长为1,快指针步长为2,1)当链表有奇数个节点时,快指针到达尾节点表示慢指针到达中间节点,此时将链表的右半部分逆序,然后同时从头结点和尾节点开始遍历链表,比较当前节点是否相等,如果有一个不等就不是回文。2)当链表有偶数个节点时,快指针到达尾节点的前一个节点即表示慢指针到达中间节点,此时将链表的右半部分逆序,然后同时从头结点和尾节点开始遍历链表,比较当前节点是否相等,如果有一个不等就不是回文。 -
复制含有random指针的链表,含有random指针是指,链表中除了含有指向下一个节点的next指针外还含有指向任意节点的random指针。
1、如果链表为空直接返回空
2、如果不为空,我们在遍历链表的同时复制每个节点,并把它们连在该节点和下一个节点之间,再次遍历链表利用这个特殊对应关系就可找到在复制的链表中random该指向哪个节点 -
判断单链表是否有环,如果环就返回第一个入环节点,如果无环就返回空。要求:时间复杂度为O(n),空间复杂度为O(1)
1、如果没有空间复杂度的限制:此时用hash表解决,具体为:依次让节点入hash,如果过程中发现hash中已经存在这个键,那么说明有环,且第一个重复出现的键所代表的节点就是第一个入环节点。
2、本题解法:用快慢指针遍历链表。如果链表有环,那么快慢指针一定会在环中某个位置相遇,当快慢指针相遇时,让快指针再从头结点重新遍历链表,当它和慢指针再次相遇,那么遇上的这个节点就是第一个入环节点。 -
判断两无环单链表是否相交,如果相交则返回第一个相交节点,如果不相交则返回空。要求:时间复杂度O(m + n),空间复杂度O(1)
1、如果无空间复杂度限制:用hash解决,具体就是先将一条链入hash,然后遍历另一条链,遍历过程中在hash表中查找该节点表示的键是否已经在hash表中出现,第一个重复出现的键即为第一个相交节点,无相交节点则不相交
2、本题思路:遍历这两条链,统计各自长度为m、n,假设m >= n,则让长度为m的链先遍历m - n个节点,再和长度为n的节点同步遍历,如果遍历过程中出现相同节点,那么出现的第一个相同节点就是头一个相交节点。 -
判断连个有环单链表是否相交,如果相交则返回第一个相交节点,,如果不相交则返回空。要求:时间复杂度O(m + n),空间复杂度O(1)
1、第一种情况是:两个链有相同的入环节点,利用“ 判断两无环单链表是否相交……”的方法,与之不同的是在统计长度时,此处应以两链表的入环节点为结束点。
2、第二种情况是:两个链有不同的入环节点,这种情况分两个情况:1)两个链的环是重叠的,只不过入环点不是同一个节点,此时采用“判断单链表是否有环……”的方法,返回其中任意一条链的入环节点即可。2)两条链的环不重叠,此时不存在相交节点。 -
给定两单链表,判断是否相交,如果相交则返回第一个相交节点,如果不相交则返回空。
1、先判断有环无环,有环链和无环链一定不相交(无环单链表如果相交,那么尾节点一定是同一个节点)
2、如果都无环,则判断无环单链表是否相交。
3、如果都有环,则判断有环单链表是否相交。