雙指針法


雙指針法:

雙指針法,指的是在遍歷對象的過程中,不是普通的使用單個指針進行訪問,而是使用兩個相同方向或者相反方向的指針進行掃描,從而達到相應的目的。

這里的指針,並非專指c中指針的概念,而是指索引,游標或指針,可迭代對象。

LeetCode雙指針題解:

1.有序數組的Two Sum

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

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

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

思路描述:

該題是經典的雙指針法,指針i和指針j,i和j分別指向頭和尾,
如果兩個指針指向的元素和sum==target,那么得到要求的結果;
如果sum>target,移動較大的元素,使sum變小一些,j--;
如果sum<target,移動較小的元素,是sum變大一些,i++;

代碼實現:

public int[] twoSum(int[] numbers, int target) {

        int i = 0, j = numbers.length - 1;
        while (i < j) {
            int num = numbers[i] + numbers[j];
            if (num == target) {
                return new int[]{i + 1, j + 1};
            } else if (num < target) {
                i++;
            } else {
                j--;
            }
        }
        return null;
    }

2.兩數平方和

633. Sum of Square Numbers (Easy)

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

題目描述:判斷一個數是否為兩個數的平方和

思路描述:

首先,一個數的平方和,那么另外兩個數肯定 <= Math.sqrt(num),則使用雙指針法來測試
指針i指向0,指針j指向Math.sqrt(num)
如果兩個指針指向的元素和sum==target,那么得到要求的結果;
如果sum>target,移動較大的元素,使sum變小一些,j--;
如果sum<target,移動較小的元素,是sum變大一些,i++;

代碼實現:

public static boolean judgeSquareSum(int c) {

        int i = 0;
        int j = (int) Math.sqrt(c);
        while (i <= j) {
            int num = i * i + j * j;
            if (num == c) {
                System.out.println(i + " " + j);
                return true;
            } else if (num < c) {
                i++;
            } else {
                j--;
            }
        }
        return false;
    }

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

345. Reverse Vowels of a String (Easy)

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

題目描述:

將給定的字符串中的元音字符進行互換位置

思路描述:

使用雙指針指向待反轉的兩個元音字符,
指針i從頭向尾遍歷,指針j從尾到頭遍歷。
如果兩個同時指向元音,互換位置,i++,j--
否則只有i指針指向元音,則j--,i不變
否則只有j指針指向元音,則i++,j不變
否則都沒有指向元音,則i++,j--

代碼實現:

public static String reverseVowels(String s) {
        List<Character> characters = Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U');
        int i = 0, j = s.length() - 1;
        char[] chars = s.toCharArray();
        while (i <= j) {

            if (characters.contains(chars[i]) && characters.contains(chars[j])) {
                char c = chars[i];
                chars[i] = chars[j];
                chars[j] = c;
                i++;
                j--;
            } else if (characters.contains(chars[i])) {
                j--;
            } else if (characters.contains(chars[j])) {
                i++;
            } else {
                i++;
                j--;
            }
        }
        return new String(chars);
    }

4. 回文字符串

680. Valid Palindrome II (Easy)

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

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

思路詳解:
	如果字符串的起始字符和結束字符相同(即 s[0]==s[s.length-1]),則內部字符是否為回文(s[1], s[2], ..., s[s.length - 2])將唯一地確定整個字符串是否為回文。
	如果字符串的起始字符和結束字符不同,則判斷(s[1], s[2], ..., s[s.length - 1])或者(s[0], s[2], ..., s[s.length - 2])是否是回文,如果兩個都不是,那么必定不是回文。

public static 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;
}
//判斷是否string是否是回文串
private static boolean ispalindrome(String string, int i, int j) {
    while (i < j) {
        if (string.charAt(i++) != string.charAt(j--)) {
            return false;
        }
    }
    return true;
}

5.歸並兩個有序數組

88. Merge Sorted Array (Easy)

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

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

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

思路描述:

使用Arrays.cpoy(A,m)來復制A產生一個臨時數組tem,使用雙指針,指針i指向數組tem,指針j指向數組B;
while( i<tem.length()&& j<tem.length()) ,則比較指針i,j指向的元素大小,小的放在A數組中
如果上面循環終止時候,i或者j還沒有指向終點位置,則繼續循環。
while(i< tem.length()) ,i指向的元素賦值給數組A
while(j<B.length()),j指向的元素賦值給數組A。

代碼實現:

public static void merge(int[] nums1, int m, int[] nums2, int n) {

        int[] tem = Arrays.copyOf(nums1, m);
        int i = 0, j = 0, k = 0;
        while (i < m && j < n) {
            if (tem[i] < nums2[j]) {
                nums1[k++] = tem[i++];
            } else {
                nums1[k++] = nums2[j++];
            }
        }
        while (i < m) {
            nums1[k++] = tem[i++];
        }
        while (j < n) {
            nums1[k++] = nums2[j++];
        }
    }

第二種邏輯:

public static void merge2(int[] nums1, int m, int[] nums2, int n) {
        int[] tem = Arrays.copyOf(nums1, m);
        int i = 0, j = 0, k = 0;
        while (i < m || j < n) {
            if (i < m && j < n) {
                if (tem[i] < nums2[j]) {
                    nums1[k++] = tem[i++];
                } else {
                    nums1[k++] = nums2[j++];
                }
            } else if (i < m) {
                nums1[k++] = tem[i++];
            } else if (j < n) {
                nums1[k++] = nums2[j++];
            }
        }
    }

6.判斷鏈表是否存在環

141. Linked List Cycle (Easy)

思路描述:

使用雙指針法,一個指針慢,一個指針快,如果存在環,那么這兩個指針肯定會相遇。

代碼實現:

class ListNode {
    int val;
    ListNode next;

    ListNode(int x) {
        val = x;
        next = null;
    }
}
public class Solution {
    public boolean hasCycle(ListNode head) {
        if (head == null || head.next == null) {
            return false;
        }
        ListNode slow = head;
        ListNode fast = head.next;

        while (slow != fast) {
            //這里的判斷一定是 fast.next == null 
            if (slow == null || fast.next == null) {
                return false;
            }
            slow = slow.next;
            fast = fast.next.next;

        }
        return true;
    }
}

7.最長子序列

524. Longest Word in Dictionary through Deleting (Medium)

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

Output:
"apple"

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

思路描述1:

暴力法:
對字符串s 進行拆解,把所有情況均表示出,再遍歷進行查找,則問題得到解決,時間復雜度很高。

思路描述:

首先根據要求,對字符串進行排序,排序規則:先根據字符串的長度進行排序,長度相等時,根據字符串的首字符的字典序進行排序。
對排序后的字符進行遍歷,第一個符合子序列的字符串即是所求字符串。

判斷字符串sub是不是s的子串,是使用雙指針法:
開始循環:i<s.length() && j<target.length()
一個指針指向s,一個指針指向sub,同時出發
如果指針指向的位置字符相同,則兩個指針同時+1,
如果不同,那么只有s指針+1,
循環終止時候,如果sub指針指向了sub的末尾,那么sub肯定是s的子串
     

代碼實現:

public String findLongestWord(String s, List<String> d) {
    //對字符串進行排序,規則是先按長度排序,長度相同時,再按照字符串字典序排序
    Collections.sort(d, (o1, o2) -> o2.length() == o1.length() ? o1.compareTo(o2) : o2.length() - o1.length());
    for (String s1 : d) {
        if (findLongestWord2(s, s1)) {
            return s1;
        }
    }
    return "";
}
public boolean findLongestWord2(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();
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM