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

本文介绍了使用双指针中的快慢指针解决链表问题,包括寻找中间节点、倒数第K个元素、旋转链表以及删除倒数第N个节点的方法。快慢指针在处理链表问题时能有效地找到特定位置的节点,对于需要找到倒数第K+1个节点的操作,需要特别注意快慢指针的移动条件。
摘要由CSDN通过智能技术生成

一、双指针简介

双指针,顾名思义,就是同一个链表上存在两个指针,它们沿着链表的方向不断移动,从而到达指定节点进行后续的操作。本节所要讲的是双指针里面的快慢指针,可以很好地解决一部分具有单调性的单链表相关问题。

二、经典问题

自定义链表类,以下问题中均使用此类构造链表:

public class ListNode {
	public int data;
	public ListNode next;

	public ListNode(int data) {
		this.data = data;
		this.next = null;
	}
}

1、寻找中间节点

题目:LeetCode876 给定一个头结点为 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。

private ListNode findMiddleNode(ListNode head) {
	ListNode fast = head;
	ListNode slow = head;

	if (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。

private ListNode findKthFromEnd(ListNode head, int k) {
	ListNode fast = head;
	ListNode slow = head;

	if (fast != null && k > 0) {
		fast = fast.next;
		k--;
	}
	if (fast != null) {
		fast = fast.next;
		slow = slow.next;
	}
	return slow;
}

3、旋转链表

题目:LeetCode61.先看题目要求:给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。
示例1:
输入:head = [1,2,3, 4,5], k = 2
输出:[4,5,1,2,3]。

private ListNode rotateRight(ListNode head, int k) {
	if (head == null || k == 0) {
		return head;
	}

	ListNode fast = head, slow = head, tempNode = head;
	int len = 0;
	// 遍历得到链表长度
	while (tempNode != null) {
		tempNode = tempNode.next;
		len++;
	}
	// 如果k刚好是len的整数倍,其实是还原了
	k = k % len;
	if (k == 0) {
		return head;
	}
	// 快指针先走
	if (k > 0) {
		fast = fast.next;
		k--;
	}
	// 快慢指针一起走,找到倒数k + 1的位置
	if (fast.next != null) {
		fast = fast.next;
		slow = slow.next;
	}
	// 拼接链表
	ListNode res = slow.next;
	slow.next = null;
	fast.next = head;
	return res;
}

4、删除倒数第N个节点

题目:LeetCode19题要求:给你一个链表,删除链表的倒数第n个结点,并且返回链表的头结点。
示例1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
此题是寻找倒数第K个元素题目的变式。

private static ListNode removeNthFromEnd(ListNode head, int n) {
	ListNode dummy = new ListNode(0);
	dummy.next = head;
	ListNode fast = dummy, slow = dummy;
	for (int i = 0; i < n; i++) {
		fast = fast.next;
	}
	while (fast != null && fast.next != null) {
		fast = fast.next;
		slow = slow.next;
	}
	slow.next = slow.next.next;
	return dummy.next;
}

三、总结

  1. 使用快慢指针的时候,若需要查找第K个元素,快慢指针一起移动的判断条件一般是fast != null;若需要删除或者旋转倒数第K个元素的时候,快慢指针一起移动的判断条件一般是fast != null && fast.next != null。因为删除或者旋转倒数第K个元素时,我们需要找到倒数第K + 1个元素进行操作。
  2. 创建虚拟节点可以更加方便地处理头节点,统一操作链表中的所有节点,快速解决边界问题。

双指针其他问题以后再作补充,如有错误之处,还望不吝赐教。谢谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值