1. 程式人生 > 實用技巧 >Leetcode(easy Double pointer)

Leetcode(easy Double pointer)

Leetcode(easy Double pointer)

Leetcode 雙指標簡單題目

26 刪除排序陣列中的重複項

class Solution{
	public int removeDuplicates(int[] nums){
		// step代表慢指標
		int step = 0;
		// 這裡面的i就相當於快指標
		for(int i = 1;i<nums.length;i++){
			if(nums[i] != nums[step]){
				// 滿指標後移 存放i指向的不同的元素
				step++;
				nums[step] = nums[i];
			}
		}
		return step+1;
	}
}

27 移除元素

class Solution{
	public int removeElement(int[] nums,int val){
		int step = 0;
		for(int i = 0;i<nums.length;i++){
			if(nums[i]!=val){
				nums[step] = nums[i];
				step++;
			}
		}
		return step;
	}
}

28 實現strStr()

實現 strStr() 函式。給定一個 haystack 字串和一個 needle 字串,在 haystack 字串中找出 needle 字串出現的第一個位置 (從0開始)。如果不存在,則返回 -1。

class Solution {
  public int strStr(String haystack, String needle) {
    int L = needle.length(), n = haystack.length();
    if (L == 0) return 0;

    int pn = 0;
    while (pn < n - L + 1) {
      while (pn < n - L + 1 && haystack.charAt(pn) != needle.charAt(0)) ++pn;
      int currLen = 0, pL = 0;
      while (pL < L && pn < n && haystack.charAt(pn) == needle.charAt(pL)) {
        ++pn;
        ++pL;
        ++currLen;
      }

      if (currLen == L) return pn - L;
      pn = pn - currLen + 1;
    }
    return -1;
  }
}

88 合併兩個有序陣列

給你兩個有序整數陣列 nums1 和 nums2,請你將 nums2 合併到 nums1 中,使 nums1 成為一個有序陣列。

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

    }
}

125 驗證迴文串

給定一個字串,驗證它是否是迴文串,只考慮字母和數字字元,可以忽略字母的大小寫。

class Solution {
    public boolean isPalindrome(String s) {
        int n = s.length();
        int left = 0, right = n - 1;
        while (left < right) {
            while (left < right && !Character.isLetterOrDigit(s.charAt(left))) {
                ++left;
            }
            while (left < right && !Character.isLetterOrDigit(s.charAt(right))) {
                --right;
            }
            if (left < right) {
                if (Character.toLowerCase(s.charAt(left)) != Character.toLowerCase(s.charAt(right))) {
                    return false;
                }
                ++left;
                --right;
            }
        }
        return true;
    }
}

141 環形連結串列

給定一個連結串列,判斷連結串列中是否有環。

/**
 * Definition for singly-linked list.
 * 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 fast = head;
        ListNode slow = head;
        while(fast!=null && fast.next !=null && slow!=null){
            // 一定要把if(fast == slow) return 放在移動過指標的下面,不然的話初始狀態肯定是相等的
            slow = slow.next;
            fast = fast.next.next;
            if(fast == slow) return true;
        }
        return false;

    }
}

167. 兩數之和 II - 輸入有序陣列

給定一個已按照升序排列 的有序陣列,找到兩個數使得它們相加之和等於目標數。函式應該返回這兩個下標值 index1 和 index2,其中 index1 必須小於 index2。

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        // 設定兩個指標
        int i = 0;
        int j = numbers.length - 1;
        boolean flag = true;
        while(flag){
            if(numbers[i] + numbers[j] > target) j--;
            else if(numbers[i] + numbers[j] < target) i++;
            else return new int[]{i+1,j+1};
        }
        return new int[2];
    }
}

234 迴文連結串列

請判斷一個連結串列是否為迴文連結串列。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isPalindrome(ListNode head) {
        if(head == null || head.next == null) return true;
        ListNode pre = head;
        ListNode cur = head;
        int len = 0;
        while(pre != null){
            pre=pre.next;
            len++;
        }
        pre = head;
        int mid = (len%2)==1?(len/2+2):(len/2+1);
        while(mid-- >1) cur = cur.next;
        cur = reverse(cur);
        while(cur != null && pre != null){
            if(pre.val != cur.val) return false;
            pre = pre.next;
            cur = cur.next;
        }
        return true;

    }
    // 將連結串列翻轉,並返回翻轉之後的頭結點
    public ListNode reverse(ListNode root){
        if(root == null || root.next == null) return root;
        ListNode dummy = new ListNode(0);
        dummy.next = null;
        ListNode pre = root;
        ListNode cur = root.next;
        while(pre != null){
            pre.next = dummy.next;
            dummy.next = pre;
            pre = cur;
            if(cur!=null) cur = cur.next;
        }

        return dummy.next;
    }
}

283. 移動零

給定一個數組 nums,編寫一個函式將所有 0 移動到陣列的末尾,同時保持非零元素的相對順序。

class Solution {
    public void moveZeroes(int[] nums) {
        /**
        // 這種解法會導致非零元素的相對位置的改變
        int i = 0;
        int j = nums.length-1;
        while(i<j){
            // 正向找到0所在的下標
            while(i<j && nums[i]!=0) i++;
            // 反向找到不為0的數字所在的下標
            while(i<j && nums[j]==0) j--;
            int tmp = nums[i];
            nums[i] = nums[j];
            nums[j] = tmp;
            i++;
            j--;
        }
        */
		if(nums==null) {
			return;
		}
		//第一次遍歷的時候,j指標記錄非0的個數,只要是非0的統統都賦給nums[j]
		int j = 0;
		for(int i=0;i<nums.length;++i) {
			if(nums[i]!=0) {
				nums[j++] = nums[i];
			}
		}
		//非0元素統計完了,剩下的都是0了
		//所以第二次遍歷把末尾的元素都賦為0即可
		for(int i=j;i<nums.length;++i) {
			nums[i] = 0;
		}
	}
}

344. 反轉字串

編寫一個函式,其作用是將輸入的字串反轉過來。輸入字串以字元陣列 char[] 的形式給出。不要給另外的陣列分配額外的空間,你必須原地修改輸入陣列、使用 O(1) 的額外空間解決這一問題。
你可以假設陣列中的所有字元都是 ASCII 碼錶中的可列印字元。

class Solution {
    public void reverseString(char[] s) {
        // 設定兩個指標
        int i = 0;
        int j = s.length-1;
        while(i<j){
            char tmp = s[i];
            s[i] = s[j];
            s[j] = tmp;
            i++;
            j--;
        }
    }
}

345. 反轉字串中的母音字母

編寫一個函式,以字串作為輸入,反轉該字串中的母音字母

class Solution {
    public String reverseVowels(String s) {
        Set<Character> yuan = new HashSet<>();
        String str = "aeiouAEIOU";
        for(Character c:str.toCharArray()) yuan.add(c);
        // 設定兩個指標,left和right,其中left指標從左向右查詢母音字元,right從右向左查詢母音字元,
        int left = 0;
        int right = s.length()-1;
        // 因為java無法對字串進行直接的操作,這裡我們將字串s變為字元陣列
        char[] s2c = s.toCharArray();
        // 遍歷s2c
        while(left < right){
            while(left < right && !yuan.contains(s2c[left])) left++;
            while(left < right && !yuan.contains(s2c[right])) right--;
            char temp = s2c[left];
            s2c[left] = s2c[right];
            s2c[right] = temp;
            // 交換完畢之後,讓指標移動
            left++;
            right--;
        }
        return String.valueOf(s2c);
    }
}

349 兩個陣列的交集

題目:給定兩個陣列,編寫一個函式來計算它們的交集。 不允許重複的元素出現在最終結果中。

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        Set<Integer> union = new HashSet<>();
        Arrays.sort(nums1);
        Arrays.sort(nums2);
        // 設定兩個指標 i和j 其中i指向nums1中的元素,j指向nums2中的元素
        int i = 0;
        int j = 0;
        //只要兩個nums還沒有被遍歷完,就繼續便利
        while(i < nums1.length && j < nums2.length){
            // 如果指向的元素相同,則將元素新增到union中
            if(nums1[i] == nums2[j]){
                union.add(nums1[i]);
                i++;
                j++;
            } 
            // 如果指向的元素不相同,則移動指標
            else{
                // 這時候移動指標也分為兩種情況,第一種是nums1[i] > nums2[j]
                if(nums1[i] > nums2[j]) j++;
                else i++;
            }
        }
        int[] res = new int[union.size()];
        int index = 0;
        for(int k:union) res[index++] = k; 
        return res;
    }
}

350 兩個陣列的交集2

題目:給定兩個陣列,編寫一個函式來計算它們的交集。 允許重複的元素出現在最終結果中。

class Solution {
    public int[] intersect(int[] nums1, int[] nums2) {
        List<Integer> union = new ArrayList<>();
        Arrays.sort(nums1);
        Arrays.sort(nums2);
        // 設定兩個指標 i和j 其中i指向nums1中的元素,j指向nums2中的元素
        int i = 0;
        int j = 0;
        //只要兩個nums還沒有被遍歷完,就繼續便利
        while(i < nums1.length && j < nums2.length){
            // 如果指向的元素相同,則將元素新增到union中
            if(nums1[i] == nums2[j]){
                union.add(nums1[i]);
                i++;
                j++;
            } 
            // 如果指向的元素不相同,則移動指標
            else{
                // 這時候移動指標也分為兩種情況,第一種是nums1[i] > nums2[j]
                if(nums1[i] > nums2[j]) j++;
                else i++;
            }
        }
        int[] res = new int[union.size()];
        for(int k = 0;k<union.size();k++) res[k] = union.get(k); 
        return res;

    }
}

844 比較含退格的字串

題目:給定 S 和 T 兩個字串,當它們分別被輸入到空白的文字編輯器後,判斷二者是否相等,並返回結果。 # 代表退格字元。

// 雖然是可以用雙指標來寫這道題,不過相對而言,使用額外的資料結構更容易理解以及更容易實現。使用stack
class Solution {
    public boolean backspaceCompare(String S, String T) {
        return toString(S).equals(toString(T));
    }
    public String toString(String str){
        Stack<Character> stack = new Stack<>();
        for(Character c:str.toCharArray()){
            if(c=='#' && !stack.isEmpty()) stack.pop();
            else if(c=='#') continue;
            else{
                stack.push(c);
            }
        }
        String res = "";
        while(!stack.isEmpty()) res+=stack.pop();
        return res;
    }
}

925 長鍵按入

題目:你的朋友正在使用鍵盤輸入他的名字 name。偶爾,在鍵入字元 c 時,按鍵可能會被長按,而字元可能被輸入 1 次或多次。你將會檢查鍵盤輸入的字元 typed。如果它對應的可能是你的朋友的名字(其中一些字元可能被長按),那麼就返回 True。

class Solution {
    public boolean isLongPressedName(String name, String typed) {
        // 設定兩個指標,i,j 其中i指標指向的是name中的字元,j指標指向的是typed中的字元
        int i = 0;
        int j = 0;
        // 只要typed還有字元,就一直向後遍歷
        while(j<typed.length()){
            // 如果i指標指向的name中的字元與j指標指向的typed中字元相同的話,兩個指標均向後移動
            if(i<name.length()&&name.charAt(i) == typed.charAt(j)){
                i++;
                j++;
            }else if(j>0 && typed.charAt(j) == typed.charAt(j-1)){
                //如果i指標指向的name中的字元與j指標指向的typed中的字元不相等並且typed中的j指向的字元與上一個j-1指向的字元相同,
                j++;
            }else{
                return false;
            }
        }
        return i == name.length();
    }
}

977. 有序陣列的平方

給定一個按非遞減順序排序的整數陣列 A,返回每個數字的平方組成的新陣列,要求也按非遞減順序排序。

class Solution {
    public int[] sortedSquares(int[] A) {
        // 迴圈便利,將A中的元素替換成為原是數子的平方
        for(int i =0;i<A.length;i++) A[i] = A[i]*A[i];
        quickSort(A,0,A.length-1);
        return A;
    }
    // 手動實現快速排序
    public void quickSort(int[] nums,int l,int r){
        if(l<r){
            int i = l;
            // 選取基準值
            int pivot = nums[l];
            int j = r;
            while(i<j){
                // 從右到左找到比pivot小的值
                while(i<j && nums[j]>=pivot) j--;
                // 找到了比pivot小的值,按照快速排序的思想,該值應該出現在基準值的左邊,所以交換位置
                if(i<j) nums[i] =  nums[j];
                // 從左到右找到比pivot大的值
                while(i<j && nums[i]<=pivot) i++;
                if(i<j) nums[j] = nums[i];
            }
            //交換完之後,將pivot放到他本來應該在的位置
            nums[i] = pivot;
            // 然後遞迴處理基準值左邊的以及基準值右邊的子數列
            quickSort(nums,l,i-1);
            quickSort(nums,i+1,r);
        }
    }
}

劍指 Offer 04. 二維陣列中的查詢

在一個 n * m 的二維陣列中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函式,輸入這樣的一個二維陣列和一個整數,判斷陣列中是否含有該整數。

class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        // 從陣列的右上角開始查詢,如果當前元素比target小就向下移動,如果當前元素比target大就向左移動
        if(matrix.length == 0 || matrix == null) return false;
        // 獲取matrix的行數和列數
        int m = matrix.length;
        int n = matrix[0].length;
        // 設計兩個指標,i和j 其中i指向當前所在的行數,j指向當前所在的列數,初始化i=0,j=n-1
        int i = 0;
        int j = n-1;
        while(j>-1 && i<m){
            if(matrix[i][j] > target) j--;
            else if(matrix[i][j] < target) i++;
            else return true;
        }
        return false;
    }
}

劍指 Offer 22. 連結串列中倒數第k個節點

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public int kthToLast(ListNode head, int k) {
        // 因為題目中說了k保證是有效的,所以不用先計算整個連結串列的長度
        // 定義兩個指標,一個快指標,一個慢指標,快指標在滿指標後k個,當快指標為空的時候,滿指標就到了目的元素的位置
        ListNode slow = head;
        ListNode fast = head;
        while(k-->0) fast = fast.next;
        while(fast!=null){
            slow = slow.next;
            fast = fast.next;
        }
        return slow.val;
    }
}

面試題 02.02. 返回倒數第 k 個節點

實現一種演算法,找出單向連結串列中倒數第 k 個節點。返回該節點的值。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public int kthToLast(ListNode head, int k) {
        // 因為題目中說了k保證是有效的,所以不用先計算整個連結串列的長度
        // 定義兩個指標,一個快指標,一個慢指標,快指標在滿指標後k個,當快指標為空的時候,滿指標就到了目的元素的位置
        ListNode slow = head;
        ListNode fast = head;
        while(k-->0) fast = fast.next;
        while(fast!=null){
            slow = slow.next;
            fast = fast.next;
        }
        return slow.val;
    }
}

面試題 10.01. 合併排序的陣列

給定兩個排序後的陣列 A 和 B,其中 A 的末端有足夠的緩衝空間容納 B。 編寫一個方法,將 B 合併入 A 並排序。初始化 A 和 B 的元素數量分別為 m 和 n。

class Solution {
    public void merge(int[] A, int m, int[] B, int n) {
        // 一看就是倒著來嘛
        int aLen = A.length-1;
        // 設定兩個指標分別指向A,B中的元素,i指向A,j指向B
        int i = m-1;
        int j = n-1;
        while(i>-1 && j>-1){
            if(A[i] > B[j]){
                A[aLen] = A[i];
                aLen--;
                i--;
            }else{
                A[aLen] = B[j];
                aLen--;
                j--;
            }
        }
        while(i>-1){
            A[aLen] = A[i];
            aLen--;
            i--;
        }
        while(j>-1){
            A[aLen] = B[j];
            aLen--;
            j--;
        }

    }
}