代码随想录算法训练营第一天|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即可。

posted @ 2023-02-01 13:39  百里长川  阅读(302)  评论(0编辑  收藏  举报