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

            

JAVA链表next指向的是地址吗_java