LeetCode之雙指針法

雙指針是一種思想或一種技巧並不是特別具體的算法。
具體就是用兩個變量動態存儲兩個結點,來方便我們進行一些操作。通常用在線性的數據結構中。

三數之和

給你一個整數數組 nums ,判斷是否存在三元組 [nums[i], nums[j], nums[k]] 滿足 i != j、i != k 且 j != k ,同時還滿足 nums[i] + nums[j] + nums[k] == 0 。請

你返回所有和爲 0 且不重複的三元組。

注意:答案中不可以包含重複的三元組。

題解:
class Solution {
   /**
     * 思路:
     * 設定:需要找到3個數,a+b+c=0, 這裏 a b c三個數的下標從左到右
     * 定義a的下標爲i,b的下標爲left,c的下標爲right
     * 首先,對數組進行排序
     * 則需要找nums[i]+nums[left]+nums[right] = 0,找到則放入集合中
     * 如果nums[i]+nums[left]+nums[right] > 0,則需right--
     * 如果nums[i]+nums[left]+nums[right] < 0,則需left++
     * 其次,a b c三個數都要去重
     * @param nums
     * @return
     */
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> list = new ArrayList<>();
        Arrays.sort(nums);  //排序
        int left = 0, right = 0;
        for (int i=0;i<nums.length;i++) {
            if (nums[i] > 0) {
                //當前最小數大於0,則找不到符合條件的組
                return list;
            }
            if (i>0 && nums[i] == nums[i-1]) {
                //對i去重:  如果當前組的a和上一組的a相等,則視爲重複組
                continue;
            }
            left = i+1;
            right = nums.length-1;
            while(left<right) {
                int temp = nums[i] + nums[left] + nums[right];
                if (temp > 0) {
                    right--;
                } else if (temp < 0) {
                    left++;
                } else {
                    //找到符合條件組
                    list.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    //將b c相同元素跳過,即去重
                    while(left < right && nums[left+1] == nums[left]) {
                        //將b去重
                        left++;
                    }
                    while(left < right && nums[right-1] == nums[right]) {
                        //將c去重
                        right--;
                    }
                    left++;
                    right--;
                }
            }
        }
        return list;
    }

}

環形鏈表 II

給定一個鏈表的頭節點 head ,返回鏈表開始入環的第一個節點。 如果鏈表無環,則返回 null。

如果鏈表中有某個節點,可以通過連續跟蹤 next 指針再次到達,則鏈表中存在環。 爲了表示給定鏈表中的環,評測系統內部使用整數 pos 來表示鏈表尾連接到鏈表中的位置(索引從 0 開始)。如果 pos 是 -1,則在該鏈表中沒有環。注意:pos 不作爲參數進行傳遞,僅僅是爲了標識鏈表的實際情況。

不允許修改 鏈表。

題解:
public class Solution {

    public ListNode detectCycle(ListNode head) {
        //題解:這個題的目標,是找到環的入環點,
        // 根據上圖,首先根據公式推導出  a = (n-1)(b+c) + c,通過這個公式,
        // 再用兩個指針相同速度走,相遇點就是要返回的入環節點
        //那麼第一步:定義一個快指針和慢指針,快指針每步走2個,慢指針每步走一個,在有環的情況下快指針一定會追上慢指針相遇
        //得到相交點,構成這幅圖的形式

        ListNode slow = head, fast = head;
        while(fast != null) {
            //讓慢指針每次走1步,快指針每次走2步
            slow = slow.next;
            if (fast.next != null) {
                fast = fast.next.next;
            } else {
                return null;
            }
            if (slow == fast) {
                //再構造兩個慢指針走到入環節點
                ListNode cur = head;
                while(cur != slow) {
                    cur = cur.next;
                    slow = slow.next;
                }
                return cur;
            }
        }
        return null;
    }
}

顏色分類

給定一個包含紅色、白色和藍色、共 n 個元素的數組 nums ,原地對它們進行排序,使得相同顏色的元素相鄰,並按照紅色、白色、藍色順序排列。

我們使用整數 0、 1 和 2 分別表示紅色、白色和藍色。

必須在不使用庫內置的 sort 函數的情況下解決這個問題。

題解:
    4. 雙單指針法
    public void sortColors(int[] nums) {
        int n = nums.length;
        int p0 = 0, p1 = 0;
        for (int i = 0; i < n; ++i) {
            if (nums[i] == 1) {
                swap(nums,i,p1);
                p1++;
            } else if (nums[i] == 0) {
               swap(nums,i,p0);
                if (p0 < p1) {
                    swap(nums,i,p1);
                }
                p0++;
                p1++;
            }
        }
    }

合併兩個有序數組

給你兩個按 非遞減順序 排列的整數數組 nums1 和 nums2,另有兩個整數 m 和 n ,分別表示 nums1 和 nums2 中的元素數目。

請你 合併 nums2 到 nums1 中,使合併後的數組同樣按 非遞減順序 排列。

注意:最終,合併後數組不應由函數返回,而是存儲在數組 nums1 中。爲了應對這種情況,nums1 的初始長度爲 m + n,其中前 m 個元素表示應合併的元素,後 n 個元素爲 0 ,應忽略。nums2 的長度爲 n 。

題解:

    public void merge(int[] nums1, int m, int[] nums2, int n) {
        //思路:雙指針法
        // 創建數組nums3,遍歷nums3,num1和nums2分別定義下標pos1,pos2,兩個指針數元素比較大小,小的放入nums3中,相等的將num1的放入nums3中
        int p1 = 0, p2 = 0;
        int[] nums3 = new int[m+n]; //創建總長度m+n的數組存所有元素
        int cur; //當前應該插入的值
        while(p1<m || p2<n) {  //循環條件是p1<m,p2<n,或者的關係,一個結束另一個繼續提交,知道兩者都結束,則全部結束
            if (p1 == m) {
                //nums1結束,但是nums2沒有結束
                cur = nums2[p2++];
            } else if (p2 == n) {
                //nums2結束,但是nums1沒有結束
                cur = nums1[p1++];
            } else if (nums1[p1] <= nums2[p2]) {
                //都沒有結束,選擇小的
                cur = nums1[p1++];
            } else {
                cur = nums2[p2++];
            }

            nums3[p1+p2-1] = cur; //當前該插入的位置爲p1+p2-1
        }

        for(int i=0;i<m+n;i++) { //將nums3中所有元素導到nums1中
            nums1[i] = nums3[i];
        }
    }

移動零

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

請注意 ,必須在不復制數組的情況下原地對數組進行操作。

題解:
    public void moveZeroes(int[] nums) {
        //思路:雙指針:指針i每遇到一個非0元素,就將慢指針j位置數設置爲nums[i],直到nums遍歷完成
        //在j位置之後的元素都置爲0,完成移動
        int j=0;
        for (int i=0;i<nums.length;i++) {
            if (nums[i] != 0) {
                nums[j++] = nums[i];
            }
        }

        for (int i = j;i<nums.length;i++) {
            nums[i] = 0;
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章