链表常见操作(一)快慢指针

快慢指针

最近在复习算法与数据结构, 一边写写博客啦。

链表辅助类

public class ListNode {
    public int val;
    public ListNode next;
    public ListNode(int val) {
        this.val = val;
    }

    public static ListNode createLinkedList(int[] vals) {
        if (vals == null || vals.length == 0) return null;
        ListNode head = new ListNode(vals[0]);
        ListNode tmp = head;
        for (int i = 1; i < vals.length; i++) {
            tmp.next = new ListNode(vals[i]);
            tmp = tmp.next;
        }
        return head;
    }
}

快指针和慢指针

public class Main {
    public static void main(String[] args) {
        printFastAndSlow(ListNode.createLinkedList(new int[]{1, 2, 3, 4, 5, 6, 7}));
    }

    private static void printFastAndSlow(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        System.out.println("fast: " + (fast == null? "null":fast.val));
        System.out.println("slow: " + (slow == null? "null":slow.val));
    }
}

特性一:

快指针比慢指针来得快, 如果出现了环路, 可以迅速判断出来. 见leetcode题目: 环形链表

假设存在环,经过k个时间单位,两者相遇,fast经过路程1+2k,slow经过路程1+k。不妨设环长度为len,由相遇得(1+2k) - (1+k) = len,因为刚好超过一圈,那么k=len。如果设置计数器,可以得到环的长度。

那么环的入口在哪里呢?假设环外长度为a,在fast和slow相遇之后,在head上再放一个normal指针开始走,速度同slow指针,起始路程为1,因为在head处。此时,让原来相遇的slow指针继续以相同速度走。那么normal指针,走了a个长度之后可以到达入口处,路程为1+a;而slow指针经过的路程为1+len+a,超过normal指针一圈,也会停在入口处,此时两个指针相遇。

我有时间再补个图。

特性二:

当快指针走到终点的时候, 慢指针刚好走到中间的位置。可以自己尝试一下。设链表的长度为n:

1. head的值为null:slow值为null, fast的值为null。

2. n为奇数: slow值为中间节点(第n/2+ 1个元素), fast的值为链表的最后一个元素。

3. n为偶数: slow的值为偏右节点(第 n/2+1元素),fast的值为null。
这个特性可以用来解决回文链表的问题。 见leetcode题目: 回文链表查找中间节点。处理回文链表时千万注意:fast千万放在slow前面,不然会受到slow变更影响。

class Solution {
    public boolean isPalindrome(ListNode head) {
        if (head == null) return true;
        ListNode slow = head;
        ListNode fast = head;
        ListNode prev = null;
        while(fast != null && fast.next != null && slow != null) {
            // 注意 fast = fast.next.next一定要写在前面。
            fast = fast.next.next;
            ListNode next = slow.next;
            slow.next = prev;
            prev = slow;
            slow = next;
        }
        // fast == null 偶数, 否则奇数。
        if (fast == null) {
            ListNode backward = prev;
            ListNode forward = slow;
            while (backward != null && forward != null) {
                if (backward.val != forward.val) return false;
                backward = backward.next;
                forward = forward.next;
            }
            return true;
        }
        // 奇数
        ListNode backward = prev;
        ListNode forward = slow.next;
        while (backward != null && forward != null) {
            if (backward.val != forward.val) return false;
            backward = backward.next;
            forward = forward.next;
        }
        return true;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值