算法通关村第一关----链表经典问题之双指针专题笔记


在与链表有关的算法题中,经常会使用双指针去解决这些问题,而双指针中最常用到的是快慢指针,通过两个不同移动速率的指针来遍历链表。这种策略通常用于解决需要同时遍历链表中两个不同位置的问题。

快慢指针常见的用途包括:

  • 1.判定链表是否有环: 使用快慢指针,如果存在环,快指针最终会追上慢指针。
  • 2.找到链表的中间节点: 使用快慢指针,快指针每次移动两步,慢指针每次移动一步,当快指针到达链表末尾时,慢指针正好在中间。
  • 3.判断回文链表: 使用快慢指针找到中间节点,然后反转后半部分链表,再逐一比较前半部分和后半部分的值是否相同。

1. LeetCode的876题:链表的中间结点

题目描述:

给你单链表的头结点 head ,请你找出并返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。

在这里插入图片描述

  • 利用快慢指针来解决,快指针一次循环走2步,慢指针一次循环走1步
    • 当链表结点个数为奇数时,当快指针到最后一个结点时,慢指针一定是在链表的中间位置。例如{1,2,3,4,5}慢指针走一步到2时,快指针走两步到3慢指针再走一步到3时,快指针正好到5尾结点。
    • 当链表结点个数为偶数时,其中间两个结点均为中间结点,但题目要求返回第二个结点,因此快指针则要遍历到末尾结点所指向的null为止。我们可以假设总共循环了n次,即快指针指向了位于2n+1的null,慢指针指向了位于n+1的结点,由于链表结点个数为2n所以前面一个的中间结点即为n,则此时的慢指针指向n+1即为第二个中间结点。
public ListNode middleNode(ListNode head) {
	//例如{1,2,3,4,5}慢指针走一步到2时,快指针走两步到3  
	//慢指针再走一步到3时,快指针正好到5尾结点  
	if (head == null){  
	return null;  
	}  
	ListNode fast = head,slow = head;
	//注意,此时的一定要判断fast.next是否为空
		//否则后面fast=fast.next.next的时候会出现空指针异常
	while(fast != null && fast.next != null){
		slow = slow.next;
		fast = fast.next.next;
	}
	return slow;
}

2. 寻找倒数第k个元素

题目描述:

输入一个链表`,输出该链表中倒数第k个节点。本题从1开始计数,即链表的尾节点是倒数第1个节点。
示例
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.

这题也是利用快慢指针来解决,只需要让fast指针先走k个结点,后slow再与fast一同走且每次循环走的步数相同。那么当fast走到null时,其与slow相差的结点个数依旧为k,再从链尾往前看,此时的slow即为倒数第k个节点。

public static ListNode getKthFromEnd(ListNode head, int k) {  
	ListNode fast = head;  
	ListNode slow = head;  
	//fast先走k步
	while (fast != null && k > 0) {  
		fast = fast.next;  
		k--;  
	} 
	//再一同走直到fast到null
	while (fast != null) {  
		fast = fast.next;  
		slow = slow.next;  
	}  
	return slow;  
}

3. LeetCode的61题:旋转链表

题目描述

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。

、
通过示例我们可以观察到rotate 2的链表是由第一行的链表截取{1,2,3}和{4,5}两部分,再调换顺序成{4,5}{1,2,3}后拼接得到的。

因此可以先找到倒数第k个节点所在的位置(这个上一题已经实现了),后以这个节点为头节点,将后面这段的尾节点的next值指向前一段的头节点,从而调换拼接成新的链表。

当然,这个倒数第k个节点的k一定要小于链表的len,因此k=k%len。当k == 0时,则不需要进行旋转,直接返回当前头节点。否则根据上面那题的方法找到倒数第k个节点,并调换再去拼接。

public ListNode rotateRight(ListNode head, int k) {
        //这里要判断下k是否等于0
        if(head == null || k == 0){
            return head;
        }
		ListNode fast = head,slow = head,temp = head;
		
        int len = 0;
        //计算当前链表的长度
        while(head != null){
            head = head.next;
            len++;
        }
        
        //保证k小于len
        k %= len;
        if(k == 0){
            return temp;
        }
        //找到倒数第k个节点
        while (fast != null && k > 0) {
            fast = fast.next;
            k--;
        }
        //此时的尾节点就为fast,slow为倒数k个节点前面一个
        while (fast.next != null) {
            fast = fast.next;
            slow = slow.next;
        }
        
        //将slow的后一个节点作为头结点
        ListNode res = slow.next;
        //一定要将翻转完的尾节点的next置为null
        slow.next = null;
        //将两段链表拼接起来
        fast.next = temp;
        //返回拼接后的链表
        return res;
    }
  • 26
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值