双指针介绍

示例练习>>>

双指针(two points)通过设置两个指针不断移动来解决问题的,一般主要用来操作数组,链表以及字符串。双指针的种类比较多,有相向双指针,两个指针从两边往中间移。有背向双指针,两个指针从中间往两边移。还有同向双指针,两个指针同时往一个方向移。还有快慢指针,一个指针每次移动的步数多,另一个指针每次移动的步数少。

相向双指针

相向是面对面的意思,相向双指针就是两个指针从两边往中间移动,大家最常见的可能就是二分法查找以及回文串的判断,我们来看下回文串的判断,使用两个指针分别指向字符串的两端,如果他们指向的字符相等就同时往中间移。


private boolean sentencePalindrome(String s) {
    int left = 0;// 左指针。
    int right = s.length() - 1;// 右指针。
    while (left < right) {
        if (s.charAt(left++) != s.charAt(right--))
            return false;
    }
    return true;// 都比较完了,说明是回文串,返回true。
}

背向双指针

背向双指针是指两个指针往两边走,和相向双指针相反,这个可以参考下 马拉车算法 ,求最长回文子串。

同向双指针

同向双指针就是两个指针同时往一个方向移,这个在滑动窗口中一般比较常见。同向双指针中两个指针可以指向同一个对象,也可以指向不同的对象,比如合并两个有序数组,可以让两个指针分别指向两个数组。来看这样一道题,给定一个整数数组 arr[] ,将数组中所有的零移动到数组末尾,同时保持非零元素相对的顺序。

Input:arr[] = {3, 5, 0, 0, 4}
Output: 3 5 4 0 0

这题说的是把 0 移动到数组末尾,非 0 元素移到数组的前面,并且还要保证非 0 元素的相对顺序。可以使用两个指针 left 和 right ,其中 right 始终往右移动,当 right 指向的值不为 0 的时候就和 left 指向的值交换,然后两个指针同时往右移一步,如果 right 指向的值为 0 ,left 不动, right 继续往右移,如下图所示。


void pushZerosToEnd(int[] arr) {
    int left = 0;// 左指针。
    int right = 0;// 右指针。
    int length = arr.length;
    while (right < length) {
        // right 指向的值不为 0 就和 left 指向的值交换。
        if (arr[right] != 0) {
            int tmp = arr[left];
            arr[left] = arr[right];
            arr[right] = tmp;
            left++;// 挪完之后左指针也往右移。
        }
        right++;// 右指针始终往右移。
    }
}

快慢双指针

快慢双指针一般也是同向的,使用两个指针,一个走的快一个走的慢。比如找出链表的中间节点,如果链表长度是奇数,返回中间节点,如果链表长度是偶数,返回中间的第二个节点。

Input:LinkedList: 1->2->3->4->5
Output: 3 (链表长度是奇数,返回中间节点)

Input:LinkedList: 2->4->6->7->5->1
Output: 7 (链表长度是偶数,返回中间的第二个节点)

使用快慢指针,快指针每次走两步,慢指针每次走一步,直到快指针走到链表的尾节点,或者快指针为空。如果链表长度是奇数,慢指针会走到中间的那个节点,如果链表长度是偶数,慢指针会走到中间的第二个节点。


int getMiddle(Node head) {
    Node fast = head, slow = head;
    while (fast != null && fast.next != null) {
        fast = fast.next.next;// 每次走两步。
        slow = slow.next;// 每次走一步。
    }
    return slow.data;
}

信奥赛编程(刷题请进)>>>

经过两年的打磨,我的新作《算法秘籍》已经出版,有需要的可以点击购买。也可以点击 内容介绍 查看详情。