目录
Leetcode 24、两两交换链表中的节点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
思路:
这道题目正常模拟就可以了。建议使用虚拟头结点,这样会方便很多,要不然每次针对头结点(没有前一个指针指向头结点),还要单独处理。
接下来就是交换相邻两个元素了,此时一定要画图,不画图,操作多个指针很容易乱,而且要操作的先后顺序。
初始时,cur指向虚拟头结点,然后进行如下三步:
操作之后,链表成为如下:
然后就是更新cur节点和head节点了,持续这个过程。
func swapPairs(head *ListNode) *ListNode {
//定义一个虚拟头结点
dummy := &ListNode{}
dummy.Next = head
//复制一个虚拟头结点方便遍历。
cur := dummy
for head != nil && head.Next != nil { // 至少保证有两个结点,才进行交换啊
cur.Next = head.Next //1.虚拟头结点连接到第二个结点
temp := head.Next.Next //2.temp保存一下第三个结点
head.Next.Next = head //3.原第二结点的next指向头结点
head.Next = temp //4.头结点的next指向第三个结点
//接下来就是更新虚拟头结点和头结点的位置了
//虚拟头结点是连接到要交换结点的
//头结点就是要交换结点的第一个结点
cur = head //可以画个图会比较清洗
head = temp
}
return dummy.Next //最终返回虚拟节点连接到的头结点就可以了
}
Leetcode 19、删除链表的倒数第N个节点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例 1:
思路:
// 注意,链表结点从1开始算。
双指针的经典应用,如果要删除倒数第n个节点,让fast移动n+1步,然后让fast和slow同时移动,直到fast指向链表末尾,然后slow就位于要删除结点的前一个结点,那么就直接slow.Next = slow.Next.Next。
思路是这样的,但要注意一些细节。
1、首先这里我推荐大家使用虚拟头结点,这样方便处理删除实际头结点的逻辑
2、定义fast指针和slow指针,初始值为虚拟头结点,如图
fast首先走n + 1步 ,为什么是n+1呢,因为只有这样同时移动的时候slow才能指向删除节点的上一个节点(方便做删除操作),如图:
fast和slow同时移动,直到fast指向末尾,如题:
删除slow指向的下一个节点,如图
此时不难写出如下Go代码:
//本题的结点的下标是从1开始的哦
//要删除倒数第n个结点的步骤如下:
//1.定义slow和fast两个虚拟头结点。
//2.然后fast先走n+1步,然后slow也开始走,走到fast == nil
//3.此时slow在n-1个结点位置。就可以slow.Next = slow.Next.Next跳过(删除)第n个结点就好了
func removeNthFromEnd(head *ListNode, n int) *ListNode {
dummy := &ListNode{
Next: head,
}
fast := dummy
slow := dummy
i := 0
for fast != nil {
fast = fast.Next //要删除n的话,fast节点就走先走n步
if i > n {
slow = slow.Next //当fast节点走了n步之后,slow节点也开始走
}
i++ // 走到fast结束,slow所处位置就在要删除节点的前一个节点
}
slow.Next = slow.Next.Next //跳过要删除的节点就等于删掉了
return dummy.Next
}
面试题 02.07 链表相交
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。题目数据 保证 整个链式结构中不存在环。
图示两个链表在节点 c1 开始相交:
注意,函数返回结果后,链表必须 保持其原始结构 。
思路:
简单来说,就是求两个链表交点节点的指针。 这里同学们要注意,交点不是数值相等,而是指针相等。为了方便举例,假设节点元素数值相等,则节点指针相等。
看如下两个链表,目前curA指向链表A的头结点,curB指向链表B的头结点:
我们求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置(也就是求出长度差值后,让长度大的移动到和另一个一样长度的开始。),如图:
此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。
func getIntersectionNode(headA, headB *ListNode) *ListNode {
//计算headA与headB的各自的长度
temp_A, temp_B := headA, headB
count_A, count_B := 0,0
for temp_A != nil {
temp_A = temp_A.Next
count_A++
}
for temp_B != nil {
temp_B = temp_B.Next
count_B++
}
//将headA和headB中长的确定为fast,短的确定为slow
var step int
fast, slow := headA, headB
if count_A > count_B {
step = count_A - count_B
fast,slow = headA,headB
} else {
step = count_B - count_A
fast,slow = headB, headA
}
//让fast先走step步,然后此时fast和slow就在同一起跑线了
for i:=0;i<step;i++{
fast = fast.Next
}
//开始比较,如果不相等则比较他俩对应下一个
for fast != slow {
fast = fast.Next
slow = slow.Next
}
//相等(相交)了,就可以返回fast或者slow了
if fast == slow {
return fast
}
//没有相交就返回nil
return nil
}
时间复杂度:O(n + m)
空间复杂度:O(1)
Leetcode 142、环形链表II
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
说明:不允许修改给定的链表。
思路:
主要考察两知识点:
- 判断链表是否环
- 如果有环,如何找到这个环的入口
判断链表是否有环
可以使用快慢指针法,分别定义 fast 和 slow 指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。
如果有环,如何找到这个环的入口?
从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。
1、用slow和fast = head
slow一次走一步
fast一次走两步
2、当他们相遇则说明有环路
此时再从head 出发一个new_index,一次一步,同时slow也继续走。这里
当这两个相遇的时候,就是环路入口啦
*/
func detectCycle(head *ListNode) *ListNode {
slow,fast := head,head
for fast != nil && fast.Next != nil{
slow = slow.Next
fast = fast.Next.Next
if slow == fast { //如果相遇则有环
index1 := head //准备从head再出发一个index1
index2 := slow //准备从slow再出发一个index2
for index1 != index2 {
index1 = index1.Next
index2 = index2.Next
}
return index1 //直到index1和index2相遇了,返回其中一个即可
}
}
return nil
}
//当然上面的还可以优化一下,比如index2可以不要,直接拿原来的slow进行移动就可以了。
if slow == fast { //如果相遇则有环
index1 := head //准备从head再出发一个index1
for index1 != slow {
index1 = index1.Next
slow = slow.Next
}
return index1 //直到index1和slow相遇了,返回其中一个即可
}
总结回顾:
1、leetcode24两两交换链表中的节点:重点看这个图。然后就是移动cur和head的位置了
2、Leetcode 19、删除链表的倒数第N个节点:定义slow和fast,fast先走n+1,然后slow和fast同时走,当fast==nil的时候,slow位于要删除结点的前一个结点,此时slow.next - slow.next.next就把倒数第n个结点删掉了。
3、面试题 02.07 链表相交:这道题要点是各自计算链表的长度,找出长度的差额,让长度长的先走完这个差额,然后他俩就在同一起步线上了,然后就比较,如果不同就继续比较他们的next结点。
4、Leetcode 142、环形链表II:这道题的重点是判断有没有环路,有环路了如何找到这个环路的入口。首先定义slow和fast,slow每次走一步,fast每次走两步,当他俩相遇的时候说明you环路,此时从头结点和相遇结点同时发出一个结点开始移动,当新发出的这两个结点相遇的时候就是环路的入口。
1、用slow和fast = head
slow一次走一步
fast一次走两步
2、当他们相遇则说明有环路
此时再从head 出发一个new_index,一次一步,同时slow也继续走。这里
当这两个相遇的时候,就是环路入口啦
*/
func detectCycle(head *ListNode) *ListNode {
slow,fast := head,head
for fast != nil && fast.Next != nil{
slow = slow.Next
fast = fast.Next.Next
if slow == fast { //如果相遇则有环
index1 := head //准备从head再出发一个index1
index2 := slow //准备从slow再出发一个index2
for index1 != index2 {
index1 = index1.Next
index2 = index2.Next
}
return index1 //直到index1和index2相遇了,返回其中一个即可
}
}
return nil
}
//当然上面的还可以优化一下,比如index2可以不要,直接拿原来的slow进行移动就可以了。
if slow == fast { //如果相遇则有环
index1 := head //准备从head再出发一个index1
for index1 != slow {
index1 = index1.Next
slow = slow.Next
}
return index1 //直到index1和slow相遇了,返回其中一个即可
}
谢谢观看。
内容参考啊 :代码随想录