雙指針是一種思想或一種技巧並不是特別具體的算法。
具體就是用兩個變量動態存儲兩個結點,來方便我們進行一些操作。通常用在線性的數據結構中。
三數之和
給你一個整數數組 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;
}
}