链表相关思路总结

本文详细介绍了如何使用快慢指针法解决链表旋转问题,例如给定链表按k个位置右移。通过分析关键结点,建立虚拟头结点,利用快慢指针寻找合适的位置并改变结点指向,实现链表的旋转。示例代码展示了具体实现过程。

链表题的典型思路就是找到关键的结点,然后改变相关结点的指向即可。

总结:
  1. 如果要改变头结点的,如需要删除头结点时,则为了使操作所有结点的方式统一,我们会选择建立虚拟头结点dummy

  2. 快慢指针法:

    ① 快指针一次走两步,慢指针一次走一步。

    ② 快指针先走k步,然后快慢指针一起走。

    ③ 上述两个策略相结合。

  3. 头插法,尾插法等。

61. 旋转链表

给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。

示例 1:

输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1 步: 5->1->2->3->4->NULL
向右旋转 2 步: 4->5->1->2->3->NULL

示例 2:

输入: 0->1->2->NULL, k = 4
输出: 2->0->1->NULL
解释:
向右旋转 1 步: 2->0->1->NULL
向右旋转 2 步: 1->2->0->NULL
向右旋转 3 步: 0->1->2->NULL
向右旋转 4 步: 2->0->1->NULL
思路:
  1. 先分析关键的结点,本题中右移k个位置,k可能大于链表的总长度,故实际只需要右移k%总长度即可。
  2. 假设k = 2,则我们需要找到的关键结点有两个,第一个是倒数第k+1个位置3,右移后应该指向null,而3的下一个结点4应该成为新的头结点,第二个关键节点是结点5,移动后应该指向原来的头结点1。如图

注意:快慢指针同时走,快指针指向尾结点时,尾结点指的是fast.next = null的结点,而不是fast = null的结点,即图中的5,而不是null。此时,因为慢指针还要走n步才走到尾结点,所以慢指针指向的是倒数第n+1个结点,因为还要走n步才到尾结点,说明后面还有n个结点,所以它自身是倒数第n+1个结点,即倒数第n个结点的前一节点。这里说这么多,是因为这里需要理解记忆,之后便可以一次写对,而不是每次遇到链表问题时都去思考到底快慢指针指在哪个结点了。19.删除链表的倒数第N个结点 这篇文章有更为详细的描述,更好理解,所以强烈建议看完19题再看该题。

在这里插入图片描述

Java代码

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode rotateRight(ListNode head, int k) {
        //由于k可能大于链表的总长度,然后循环右移
        //所以不妨先计算出总长度,然后右移(k%总长度)个位置
        if(head == null) return head;
        ListNode cur = head;
         int count = 0;//记录总长度
        while(cur != null){
            count++;
            cur = cur.next;
        }
        int n = k % count;
        ListNode fast = head;
        ListNode slow = head;
        //快指针先走n步
        for(int i = 0;i < n;i++){
            fast = fast.next;
        }
       //快慢指针同时走,分别指向两个关键结点
        while(fast.next != null){
            fast = fast.next;
            slow = slow.next;
        }
        //改变相关结点指向,实现最终目的
        fast.next = head;
        head = slow.next;
        slow.next = null;
        return head;
    }
}

在这里插入图片描述

Go代码

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func rotateRight(head *ListNode, k int) *ListNode {
    //由于k可能大于链表的总长度,然后循环右移
    //所以不妨先计算出总长度,然后右移k%总长度个位置
    if head == nil {
        return nil
    }

    m := 0 // 链表节点总个数
    cur := head
    for cur != nil {
        m++
        cur = cur.Next
    }

    n := k % m // 需要右移n个节点
    // 那么需要找到倒数第n个节点的前一节点,和尾节点两个关键节点,思路和19题删除倒数第n个节点一模一样了
    // 快慢指针,注意:倒数第n个节点可能是第一个节点,又要找他的前一节点,所以设置一个虚拟头节点
    dummy := &ListNode{}
    dummy.Next = head
    slow,fast := dummy,dummy
    // 快指针先走n步
    for i := 0;i < n;i++ {
        fast = fast.Next
    }
    
    // 快指针最后指向了尾结点,慢指针指向了倒数第n个节点的前一节点,刚好是我们需要的两个关键节点
    for fast.Next != nil {
        fast = fast.Next
        slow = slow.Next
    }

    // 如果slow等于dummy,说明根本不需要移动(k是m的整数倍的情况)
    if slow == dummy {
        return head 
    }

    // 画个图就好理解了,尾节点需要连接头节点,然后倒数第n个节点成为新的头节点
    fast.Next = head
    head = slow.Next
    slow.Next = nil

    return head

}

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值