JJava单链表链表中的双指针
- 前言
- 1. 链表环检测
- 1.1 思路
- 1.2 代码
- 2. 找环入口
- 2.1 思路
- 2.2 代码
- 3. 删除链表倒数第n个结点
- 3.1 思路
- 3.2 代码
- 4. 回文链表
- 4.1 思路
- 4.2 代码
- 总结
前言
链表的很多操作都使用了双指针的思路,今天简单记录几个常用的单链表操作。
下面这段代码是这篇文章中链表的基本结构。
class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
next = null;
}
}
1. 链表环检测
链表中的环简单来说就是链表最后一个结点的next指针并不是null,而是指向了链表中的某一个结点,形成了一个环。
1.1 思路
使用两个指针,第一个指针每次向后走一个结点,第二个指针每次向后走两个结点。如果没有环,第二个指针将先指向最后一个结点或者指向最后结点的next。如果有环,第一个指针与第二个指针将在环中相遇。
1.2 代码
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null || head.next.next == null) {
return false;
}
ListNode slow = head;
ListNode fast = head.next.next;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
return true;
}
}
return false;
}
2. 找环入口
如果链表存在环,找到链表的环入口。也就是最后一个结点指向的结点。
2.1 思路
使用两个指针,第一个指针每次向后走一个结点,第二个指针每次向后走两个结点。如果没有环,第二个指针将先指向最后一个结点或者指向最后结点的next,返回null,如果有环,第一个指针与第二个指针将在环中相遇,相遇后,将其中一个指针执行head,每次向后走一个结点,另个指针从第一次相遇结点每次向后走一个结点,当两个指针再次相遇时,就是环入口。
2.2 代码
public ListNode detectCycle(ListNode head) {
if (head == null || head.next == null || head.next.next == null) {
return null;
}
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
slow = head;
while (slow != fast) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
}
return null;
}
3. 删除链表倒数第n个结点
先决条件,给定的n是有效的。
3.1 思路
声明一个哨兵结点,将哨兵结点的next指针指向head;两个指针同时指向哨兵结点,第一个指针先向后走n步,之后两个指针同时向后遍历,当第一个指针为null时,第二个指针的next结点就是链表倒数第n个结点,将其删除。返回哨兵结点的next结点,也就是链表的头结点。
3.2 代码
public ListNode removeNthFromEnd(ListNode head, int n) {
if(head == null){
return head;
}
ListNode sentry=new ListNode(0);
sentry.next=head;
ListNode prevNode = sentry;
ListNode currNode = sentry;
for(int i = 0;i <= n; i++){
currNode = currNode.next;
}
while(currNode!=null){
currNode = currNode.next;
prevNode = prevNode.next;
}
prevNode.next = prevNode.next.next;
return sentry.next;
}
4. 回文链表
判断一个链表是否是回文链表。
4.1 思路
还是需要两个指针,第一个指针每次向后走一个结点,第二个指针每次向后走两个结点,同时还需要一个指针指向第一个指针的前一个结点,当第二个指针是最后一个结点的时候,或者说找到中间结点时,需要根据第一个指针的前置结点将链表分成两段,然后翻转后半部分的链表,在将两个链表逐个对比。
4.2 代码
public boolean isPalindrome(ListNode head) {
if (head == null || head.next == null) {
return true;
}
//慢指针前置结点
ListNode slowPrev = head;
//慢指针
ListNode slow = slowPrev.next;
//快指针
ListNode fast = slow.next;
//找中间结点
while (fast != null && fast.next != null) {
slowPrev = slow;
slow = slow.next;
fast = fast.next.next;
}
//分成两段
if (slowPrev != null) {
slowPrev.next = null;
}
//翻转后半部分的链表
slowPrev = null;
while (slow != null) {
ListNode temp = slow.next;
slow.next = slowPrev;
slowPrev = slow;
slow = temp;
}
//两个链表逐个对比
while (head != null && slowPrev != null) {
if (head.val != slowPrev.val) {
return false;
}
head = head.next;
slowPrev = slowPrev.next;
}
return true;
}
总结
链表的操作还有很多,LeetCode上有很多题,没事看一看,学习的不是代码,是思路。