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上有很多题,没事看一看,学习的不是代码,是思路。