1. 程式人生 > 資訊 >蘋果 M1 系列晶片同款,英特爾考慮第 14 代酷睿“Meteor Lake”CPU 部分使用臺積電 5nm 製造

蘋果 M1 系列晶片同款,英特爾考慮第 14 代酷睿“Meteor Lake”CPU 部分使用臺積電 5nm 製造

  • Leetcode 題解 - 雙指標
    • 1. 有序陣列的 Two Sum
    • 2. 兩數平方和
    • 3. 反轉字串中的母音字元
    • 4. 迴文字串
    • 5. 歸併兩個有序陣列
    • 6. 判斷連結串列是否存在環
    • 7. 最長子序列

 

1. 有序陣列的 Two Sum

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

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) {
        int l = 0;
        int r = numbers.length-1;
        while(l < r){
            int sum = numbers[l]+numbers[r];
            if(sum == target)
               return new int[]{l+1, r+1};
            else if(sum < target)
                l ++;
            else
                r --;
        }
        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)。

 public boolean judgeSquareSum(int target) {
     if (target < 0) return false;
     int i = 0, j = (int) Math.sqrt(target);
     while (i <= j) {
         int powSum = i * i + j * j;
         if (powSum == target) {
             return true;
         } else if (powSum > target) {
             j--;
         } else {
             i++;
         }
     }
     return false;
 }

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

345. Reverse Vowels of a String (Easy)

Leetcode / 力扣

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

思路:利用「雙指標」進行前後掃描,當左右指標都是母音字母時,進行互換並移到下一位。

class Solution {
    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" 就是一個迴文字串。

         判斷迴文串用雙指標的,i從前往後遍歷,j從後往前遍歷。
難點:怎麼去判斷刪除一個元素後的字串是不是迴文串
解決:如果遇到不等的判斷去掉左邊或者去掉右邊是否為迴文。

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]

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

思路:雙指標 ,從後往前:
         用的是倒序的方式。指標i,j,k分別指向num1陣列m-1位置,num2陣列n-1位置和num1陣列m+n-1位置,將i,j指向數值大的放在k位置。
需要從尾開始遍歷,否則在 nums1 上歸併得到的值會覆蓋還未進行歸併比較的值。

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int k = m+n;
        while (n >0){
            if (m >0 && nums1[m-1] > nums2[n-1]) {
                nums1[--k] = nums1[--m];
            }else{
                nums1[--k] = nums2[--n];
            }
        }
    }
}

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 中的一個字串,找出能構成的最長字串。如果有多個相同長度的結果,返回字典序的最小字串。

思路:排序 + 雙指標 + 貪心
可以先對 dictionary 根據題意進行自定義排序;
長度不同的字串,按照字串長度排倒序。長度相同的,則按照字典序排升序;
然後我們只需要對dictionary 進行順序查詢,找到的第一個符合條件的字串即是答案。

class Solution {
    public String findLongestWord(String s, List<String> dictionary) {
       Collections.sort(dictionary, (a,b)->{
           if (a.length() != b.length()) return  b.length()-a.length();
           return a.compareTo(b);
       });

       int n = s.length();
       for(String ss: dictionary){
           int m = ss.length();
           int i = 0, j = 0;
           while(i < n && j < m){
               if(s.charAt(i)== ss.charAt(j)) j++;
               i++;
           }
           if (j == m) return ss;
       }
       return "";

    }
}

滑動視窗原理

本質上來說,滑窗是雙指標,一根指標指向左端點,一根指標指向右端點。
右指標移動可以表示擴張視窗,左指標移動表示縮小視窗。
如果當前元素滿足題目要求時,可以挪動右指標嘗試更優解,並且更新需要記錄的變數(元素,元素個數++等)
如果當前視窗內的元素不滿足條件,可以挪動左指標嘗試調整,並且更新需要記錄的變數(元素,元素個數--等)
通過以上步驟視窗就開始“滑動”起來,在滑動過程中,要記得及時更新答案。一般為求最大或最小。
個人觀點認為,關鍵點在於找到一個 不滿足條件的情況 使得左指標移動,這樣可以簡化問題。找到不合理的情況並且及時調整是滑動視窗的關鍵。