代码随想录算法训练营第一天|704.二分查找、27.移除元素
LeetCode 704.二分查找(C++)#
题目链接:704.二分查找 力扣leetCode
二分查找 算法思路:
二分查找需要保证数组为有序数组同时无重复元素,否组无法通过二分查找进行判断(结果无法唯一)
二分查找通过不断收缩数组,趋近中间值进行,由于有序,可以通过中间值与目标的比较实现
①左闭右闭区间
1 class Solution { 2 public: 3 int search(vector<int>& nums, int target) { 4 int left =0; 5 int right =nums.size()-1;//right为最右边,target定义在左闭右闭区间 6 while (left <=right){//left==right是依然有效 7 int middle=left+((right-left)/2);//防止溢出,left right middle均为下标值 8 if(nums[middle]>target){ 9 right=middle-1; //在左区间,[left,middle-1] 10 } 11 else if(nums[middle]<target){ 12 left=middle+1; //target在右区间,所以[middle+_1,right] 13 } 14 else{ 15 return middle; //target==mums[middle],输出下标值 16 } 17 } 18 return-1;//未找到目标值 19 } 20 };
左闭右闭区间中,left==right 是有意义的,因此在进行循环的边界判断中需要left<=right
▲middle=left+(right-left)/2等同于(left+right)/2,可以防止溢出。原因:当left和right足够大时,left+right后可能导致结果超出范围,如果使用left+(right-left)/2则可以保证middle始终在left和right之间。right-left必定小于数组范围。
②左闭右开区间
左闭右开区间中,left==right是没有意义的,因此判断中用left<right
1 // 版本二 2 class Solution { 3 public: 4 int search(vector<int>& nums, int target) { 5 int left = 0; 6 int right = nums.size(); // 定义target在左闭右开的区间里,即:[left, right) 7 while (left < right) { // 因为left == right的时候,在[left, right)是无效的空间,所以使用 < 8 int middle = left + ((right - left) >> 1); 9 if (nums[middle] > target) { 10 right = middle; // target 在左区间,在[left, middle)中 11 } else if (nums[middle] < target) { 12 left = middle + 1; // target 在右区间,在[middle + 1, right)中 13 } else { // nums[middle] == target 14 return middle; // 数组中找到目标值,直接返回下标 15 } 16 } 17 // 未找到目标值 18 return -1; 19 } 20 };
▲left一定是middle+1,因为middle已经确定≠target,所以要加一进行区间判断。
由于是左闭右开,middle<target即可保证判断区间时不会含上middle,所以right=middle即可,左闭右闭区间需要right=middle-1
LeetCode 27. 移除元素(C++)#
题目链接:27.移除元素LeetCode
移除元素算法思路:
移除元素的暴力算法是通过双循环进行,发现相等的元素后,就将改元素后面的所有元素向前移动一位,然后输出即可。第一个for循环遍历数组,第二个for循环更新数组。
此时时间复杂度为O(n²)。
时间复杂度最小时需要通过双指针进行。两个指针分为快慢指针,在一个for循环内完成两个for循环的工作。
单向双指针法#
快慢指针同向移动,不会改变元素的相对位置。
快指针:寻找新数组的元素,即将需删除的元素跳过后的新数组
慢指针:更新数组,指向更新新数组的下标
1 // 时间复杂度:O(n) 2 // 空间复杂度:O(1) 3 class Solution { 4 public: 5 int removeElement(vector<int>& nums, int val) { 6 int slowIndex = 0; 7 for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) { 8 if (val != nums[fastIndex]) { 9 nums[slowIndex++] = nums[fastIndex]; 10 } 11 } 12 return slowIndex; 13 } 14 };
相向双指针法#
1 /** 2 * 相向双指针方法,基于元素顺序可以改变的题目描述改变了元素相对位置,确保了移动最少元素 3 * 时间复杂度:O(n) 4 * 空间复杂度:O(1) 5 */ 6 class Solution { 7 public: 8 int removeElement(vector<int>& nums, int val) { 9 int leftIndex = 0; 10 int rightIndex = nums.size() - 1; 11 while (leftIndex <= rightIndex) { 12 // 找左边等于val的元素 13 while (leftIndex <= rightIndex && nums[leftIndex] != val){ 14 ++leftIndex; 15 } 16 // 找右边不等于val的元素 17 while (leftIndex <= rightIndex && nums[rightIndex] == val) { 18 -- rightIndex; 19 } 20 // 将右边不等于val的元素覆盖左边等于val的元素 21 if (leftIndex < rightIndex) { 22 nums[leftIndex++] = nums[rightIndex--]; 23 } 24 } 25 return leftIndex; // leftIndex一定指向了最终数组末尾的下一个元素 26 } 27 };
总结
leetCode中的时间不用在意,只要保证时间复杂度O(n)最小即可。时间复杂度排序:O(log)<O(n)<O(nlog)<O(n²)。
leetCode是在外部进行main函数调用,因此输出结果时一定是return n; 可以先写出来main中的输出结果,将其return即可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)