代码随想录day4|Leetcode 24、两两交换链表中的节点|Leetcode 19、删除链表的倒数第N个节点|面试题 02.07 链表相交|Leetcode 142、环形链表II|Golang

目录

Leetcode 24、两两交换链表中的节点

Leetcode 19、删除链表的倒数第N个节点

面试题 02.07 链表相交

Leetcode 142、环形链表II

总结回顾:


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相遇了,返回其中一个即可
        }

谢谢观看。

 内容参考啊 :代码随想录

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值