LeedCode算法思想-雙指針

雙指針主要用於遍歷數組,兩個指針指向不同的元素,從而協同完成任務。

1. 有序數組的 Two Sum

167. Two Sum II - Input array is sorted (Easy)

Leetcode / 力扣

Input: numbers={2, 7, 11, 15}, target=9
Output: index1=1, index2=2

題目描述:在有序數組中找出兩個數,使它們的和爲 target。

使用雙指針,一個指針指向值較小的元素,一個指針指向值較大的元素。指向較小元素的指針從頭向尾遍歷,指向較大元素的指針從尾向頭遍歷。

  • 如果兩個指針指向元素的和 sum == target,那麼得到要求的結果;
  • 如果 sum > target,移動較大的元素,使 sum 變小一些;
  • 如果 sum < target,移動較小的元素,使 sum 變大一些。

數組中的元素最多遍歷一次,時間複雜度爲 O(N)。只使用了兩個額外變量,空間複雜度爲 O(1)。

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        if (numbers == null) {
            return null;   
        }

        int startIndex = 0;
        int endIndex = numbers.length - 1;

        while(startIndex < endIndex) {
            int sum = numbers[startIndex] + numbers[endIndex];
            if (sum == target) {
                return new int[]{startIndex + 1, endIndex + 1};
            } else if (sum < target) {
                startIndex++;
            } else {
                endIndex--;
            }
        }

        return null;
    }
}

2. 兩數平方和

633. Sum of Square Numbers (Easy)

Leetcode / 力扣

Input: 5
Output: True
Explanation: 1 * 1 + 2 * 2 = 5

題目描述:判斷一個非負整數是否爲兩個整數的平方和。

可以看成是在元素爲 0~target 的有序數組中查找兩個數,使得這兩個數的平方和爲 target,如果能找到,則返回 true,表示 target 是兩個整數的平方和。

本題和 167. Two Sum II - Input array is sorted 類似,只有一個明顯區別:一個是和爲 target,一個是平方和爲 target。本題同樣可以使用雙指針得到兩個數,使其平方和爲 target。

本題的關鍵是右指針的初始化,實現剪枝,從而降低時間複雜度。設右指針爲 x,左指針固定爲 0,爲了使 02 + x2 的值儘可能接近 target,我們可以將 x 取爲 sqrt(target)。

因爲最多只需要遍歷一次 0~sqrt(target),所以時間複雜度爲 O(sqrt(target))。又因爲只使用了兩個額外的變量,因此空間複雜度爲 O(1)。

class Solution {
    public boolean judgeSquareSum(int target) {
        if (target < 0) {
            return false;
        }

        int startNum = 0;
        int endNum = (int) Math.sqrt(target);

        while (startNum <= endNum) {
            int powSum = startNum * startNum + endNum * endNum;

            if (powSum == target) {
                return true;
            } else if ( powSum > target) {
                endNum--;
            } else {
                startNum++;
            }
        }

        return false;
    }
}

3. 反轉字符串中的元音字符

345. Reverse Vowels of a String (Easy)

Leetcode / 力扣

Given s = "leetcode", return "leotcede".

使用雙指針,一個指針從頭向尾遍歷,一個指針從尾到頭遍歷,當兩個指針都遍歷到元音字符時,交換這兩個元音字符。

爲了快速判斷一個字符是不是元音字符,我們將全部元音字符添加到集合 HashSet 中,從而以 O(1) 的時間複雜度進行該操作。

  • 時間複雜度爲 O(N):只需要遍歷所有元素一次
  • 空間複雜度 O(1):只需要使用兩個額外變量
private final static HashSet<Character> vowels = new HashSet<>(
        Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'));

public String reverseVowels(String s) {
    if (s == null) return null;
    int i = 0, j = s.length() - 1;
    char[] result = new char[s.length()];
    while (i <= j) {
        char ci = s.charAt(i);
        char cj = s.charAt(j);
        if (!vowels.contains(ci)) {
            result[i++] = ci;
        } else if (!vowels.contains(cj)) {
            result[j--] = cj;
        } else {
            result[i++] = cj;
            result[j--] = ci;
        }
    }
    return new String(result);
}

4. 迴文字符串

680. Valid Palindrome II (Easy)

Leetcode / 力扣

Input: "abca"
Output: True
Explanation: You could delete the character 'c'.

題目描述:可以刪除一個字符,判斷是否能構成迴文字符串。

所謂的迴文字符串,是指具有左右對稱特點的字符串,例如 “abcba” 就是一個迴文字符串。

使用雙指針可以很容易判斷一個字符串是否是迴文字符串:令一個指針從左到右遍歷,一個指針從右到左遍歷,這兩個指針同時移動一個位置,每次都判斷兩個指針指向的字符是否相同,如果都相同,字符串纔是具有左右對稱性質的迴文字符串。

本題的關鍵是處理刪除一個字符。在使用雙指針遍歷字符串時,如果出現兩個指針指向的字符不相等的情況,我們就試着刪除一個字符,再判斷刪除完之後的字符串是否是迴文字符串。

在判斷是否爲迴文字符串時,我們不需要判斷整個字符串,因爲左指針左邊和右指針右邊的字符之前已經判斷過具有對稱性質,所以只需要判斷中間的子字符串即可。

在試着刪除字符時,我們既可以刪除左指針指向的字符,也可以刪除右指針指向的字符。

public boolean validPalindrome(String s) {
    for (int i = 0, j = s.length() - 1; i < j; i++, j--) {
        if (s.charAt(i) != s.charAt(j)) {
            return isPalindrome(s, i, j - 1) || isPalindrome(s, i + 1, j);
        }
    }
    return true;
}

private boolean isPalindrome(String s, int i, int j) {
    while (i < j) {
        if (s.charAt(i++) != s.charAt(j--)) {
            return false;
        }
    }
    return true;
}

5. 歸併兩個有序數組

88. Merge Sorted Array (Easy)

Leetcode / 力扣

Input:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6],       n = 3

Output: [1,2,2,3,5,6]

題目描述:把歸併結果存到第一個數組上。

需要從尾開始遍歷,否則在 nums1 上歸併得到的值會覆蓋還未進行歸併比較的值。

public void merge(int[] nums1, int m, int[] nums2, int n) {
    int index1 = m - 1, index2 = n - 1;
    int indexMerge = m + n - 1;
    while (index1 >= 0 || index2 >= 0) {
        if (index1 < 0) {
            nums1[indexMerge--] = nums2[index2--];
        } else if (index2 < 0) {
            nums1[indexMerge--] = nums1[index1--];
        } else if (nums1[index1] > nums2[index2]) {
            nums1[indexMerge--] = nums1[index1--];
        } else {
            nums1[indexMerge--] = nums2[index2--];
        }
    }
}

6. 判斷鏈表是否存在環

141. Linked List Cycle (Easy)

Leetcode / 力扣

使用雙指針,一個指針每次移動一個節點,一個指針每次移動兩個節點,如果存在環,那麼這兩個指針一定會相遇。

public boolean hasCycle(ListNode head) {
    if (head == null) {
        return false;
    }
    ListNode l1 = head, l2 = head.next;
    while (l1 != null && l2 != null && l2.next != null) {
        if (l1 == l2) {
            return true;
        }
        l1 = l1.next;
        l2 = l2.next.next;
    }
    return false;
}

7. 最長子序列

524. Longest Word in Dictionary through Deleting (Medium)

Leetcode / 力扣

Input:
s = "abpcplea", d = ["ale","apple","monkey","plea"]

Output:
"apple"

題目描述:刪除 s 中的一些字符,使得它構成字符串列表 d 中的一個字符串,找出能構成的最長字符串。如果有多個相同長度的結果,返回字典序的最小字符串。

通過刪除字符串 s 中的一個字符能得到字符串 t,可以認爲 t 是 s 的子序列,我們可以使用雙指針來判斷一個字符串是否爲另一個字符串的子序列。

public String findLongestWord(String s, List<String> d) {
    String longestWord = "";
    for (String target : d) {
        int l1 = longestWord.length(), l2 = target.length();
        if (l1 > l2 || (l1 == l2 && longestWord.compareTo(target) < 0)) {
            continue;
        }
        if (isSubstr(s, target)) {
            longestWord = target;
        }
    }
    return longestWord;
}

private boolean isSubstr(String s, String target) {
    int i = 0, j = 0;
    while (i < s.length() && j < target.length()) {
        if (s.charAt(i) == target.charAt(j)) {
            j++;
        }
        i++;
    }
    return j == target.length();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章