【Day1】 二分 + 双指针
704. 二分法
-
左闭右闭(迭代 + 循环):
class Solution { private: int res = -1; int divid(vector<int>& nums, int l, int r, int target){ // 迭代终止条件 if(l > r) { return res; } int m = (l + r) / 2; if(nums[m] > target) { res = divid(nums, l, m - 1, target); } else if(nums[m] < target){ res = divid(nums, m + 1, r, target); } else{ res = m; } return res; } public: int search(vector<int>& nums, int target) { int len = nums.size(); return divid(nums, 0, len - 1, target); } }; // 2. 循环 class Solution { public: int search(vector<int>& nums, int target) { int res = -1; int l = 0; int r = nums.size() - 1; while(l <= r) { int m = (l + r) / 2; if(nums[m] > target) { r = m - 1; } else if(nums[m] < target){ l = m + 1; } else{ res = m; break; } } return res; } };
-
左闭右开(仅循环):
class Solution { public: int search(vector<int>& nums, int target) { int res = -1; int l = 0; int r = nums.size(); while(l < r) { int m = l + ((r - l) / 2); if(nums[m] > target) { r = m; } else if(nums[m] < target){ l = m + 1; } else{ res = m; break; } } return res; } };
-
注意事项:
int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
-
感想:
右开的时候:因为不含最右侧的元素,所以 ——
- 起始的时候要多往右探一位
- 右指针所指是虚的,不是真正会被比较的元素,所以l==r的情况下,其实被比较的子数据已经空了,不必再进行切分了,所以这里while中不再有此条件。
27. 移除元素
-
暴力循环:
注意:这里的循环终止条件要考虑到后面的val元素长度,否则会陷入死循环
// 1. while class Solution { public: int removeElement(vector<int>& nums, int val) { int len = nums.size(); int same = 0; // 相同元素计数 int i = 0; // 当前元素指针 while(i < len - same){ if(nums[i] == val){ // 逐个往后交换元素,给挪到最后(其它已就位val元素的前面) for(int j = i + 1; j < len - same; j++){ int trans = nums[j]; nums[j] = nums[j - 1]; nums[j - 1] = trans; } same ++; // 相同元素 +1 }else{ i++; } } return len - same; } }; // 2. for循环 for(int i = 0; i < len - same; i++){ if(nums[i] == val){ // 逐个往后交换元素,给挪到最后(其它已就位val元素的前面) for(int j = i + 1; j < len - same; j++){ int trans = nums[j]; nums[j] = nums[j - 1]; nums[j - 1] = trans; } same ++; // 相同元素 +1 i--; // 指针回退一格(因为后面的元素顶上来了) } }
-
直接交换法
- 思路: val元素直接与最后的元素进行一个交换(交换时进行二次判断、当最后的元素已经是val,则计数+1、转为同其前面的元素进行交换,以此类推)
- 注意: 这里题目中不要求保留原数组的元素顺序,所以可以用这种方法实现,若有此要求则不可用。
class Solution { private: // Func: 数组元素交换位置 int change(vector<int>& nums, int l, int r) { int trans = nums[l]; nums[l] = nums[r]; nums[r] = trans; return 0; } public: int removeElement(vector<int>& nums, int val) { int len = nums.size(); int same = 0; // 相同元素计数 for (int i = 0; i < len - same; i++) { if (nums[i] == val) { // 末尾是val元素,则直接跳过、节省时间 // 这里要同时考虑数组访问不可溢出 while (len > same && nums[len - same - 1] == val) { if(i == len - same - 1){ break;// 末尾元素不是i元素本身时才跳过 } same++; } change(nums, i, len - same - 1); same++; } // 由于末尾的元素已经判断过是否val,所以换过来的元素必然非val,故不再进行指针回退 } return len - same; } };
-
双指针法
思路: 这个大概就是一个人在前面去找所有不是val的元素,另一个元素留守后方规规矩矩一步一格去标记所有非val元素的最终位置(因为val元素相当于是不占前面的位置的),所以就是慢指针不断接收快指针送过来的元素就完事了,不用再进行交换、直接覆盖,所以可以写得很粗暴。
class Solution { public: int removeElement(vector<int>& nums, int val) { int l = 0, r = 0; while(r < nums.size()){ // r负责找非val元素(注意防止溢出) if(nums[r] == val){ r++; }else{ nums[l++] = nums[r++];// 快指针把非val元素丢给慢指针 } } return l; } };
双指针法练习:
-
class Solution { public: vector<int> sortedSquares(vector<int>& nums) { int len = nums.size(); int left = 0, right = len - 1, cur = len - 1; vector<int> res(len,0); while(left <= right){ int mul_l = nums[left] * nums[left]; int mul_r = nums[right] * nums[right]; if(mul_l > mul_r) { res[cur--] = mul_l; left++; } else{ res[cur--] = mul_r; right--; } } return res; } };
-
class Solution { public: void moveZeroes(vector<int>& nums) { int l = 0, r = 0; int len = nums.size(); while(r < len){ if(nums[r] != 0){ nums[l++] = nums[r++]; } else{ r++; } } while(l < len){ nums[l++] = 0; } } };