704.二分查找
1、题目
二分查找(Binary Search)算法,也叫折半查找算法。二分查找的思想非常简单,有点类似分治的思想。二分查找针对的是一个有序的数据集合,每次都通过跟区间的中间元素对比,将待查找的区间缩小为之前的一半,直到找到要查找的元素,或者区间被缩小为 0。
2、题解
-
左闭右开 [left, right)
class Solution
{
public:
int search(vector<int>& nums, int target)
{
int left = 0;
int right = nums.size(); // 定义target在左闭右开的区间里,即:[left, right)
int result = -1 // 未找到目标值默认-1
while (left < right)
{
// 因为left == right的时候,在[left, right)是无效的空间,所以使用 <
int middle = left + ((right - left) / 2);
if (nums[middle] > target)
{
right = middle; // target 在左区间,在[left, middle)中
}
else if (nums[middle] < target)
{
left = middle + 1; // target 在右区间,在[middle + 1, right)中
}
else
{
result = middle; // 数组中找到目标值,直接返回下标
}
}
// 未找到目标值
return -1;
}
};
-
左闭右闭 [left, right]
class Solution
{
public:
int search(vector<int>& nums, int target)
{
int left = 0;
int right = nums.size() - 1; // 定义target在左闭右闭的区间里,[left, right]
int result = -1; // 未找到目标值默认-1
while (left <= right)
{
// 当left==right,区间[left, right]依然有效,所以用 <=
int middle = left + ((right - left) / 2); // 防止溢出 等同于(left + right)/2
if (nums[middle] > target)
{
right = middle - 1; // target 在左区间,所以[left, middle - 1]
}
else if (nums[middle] < target)
{
left = middle + 1; // target 在右区间,所以[middle + 1, right]
}
else
{
result = middle; // 数组中找到目标值,直接返回下标
}
}
return result;
}
};
3、总结
注意两种不同写法中,区间的合法性。
27.移除元素
1、题目
2、题解
思路一:暴力解法
两层for循环,一个for循环遍历数组元素 ,第二个for循环更新数组。
// 时间复杂度:O(n^2) // 空间复杂度:O(1) class Solution { public: int removeElement(vector<int>& nums, int val) { int size = nums.size(); for (int i = 0; i < size; i++) { if (nums[i] == val) { // 发现需要移除的元素,就将数组集体向前移动一位 for (int j = i + 1; j < size; j++) { nums[j - 1] = nums[j]; } i--; // 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位 size--; // 此时数组的大小-1 } } return size; } };
思路二:双指针法
双指针法(快慢指针法):通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
慢指针:指向更新 新数组下标的位置
// 时间复杂度:O(n) // 空间复杂度:O(1) class Solution { public: int removeElement(vector<int>& nums, int val) { if(nums.size()==0) return 0; int slow=0; int fast=0; int k=0; //记录值不为val的数量 while(fast<nums.size()) { if(nums[fast]==val) { fast++; } else{ nums[slow++]=nums[fast++]; k++; } } cout<<k; return k; };
补充解法
//相向双指针方法,基于元素顺序可以改变的题目描述改变了元素相对位置,确保了移动最少元素 //时间复杂度:O(n) //空间复杂度:O(1) class Solution { public: int removeElement(vector<int>& nums, int val) { int leftIndex = 0; int rightIndex = nums.size() - 1; while (leftIndex <= rightIndex) { // 找左边等于val的元素 while (leftIndex <= rightIndex && nums[leftIndex] != val) { ++leftIndex; } // 找右边不等于val的元素 while (leftIndex <= rightIndex && nums[rightIndex] == val) { --rightIndex; } // 将右边不等于val的元素覆盖左边等于val的元素 if (leftIndex < rightIndex) { nums[leftIndex++] = nums[rightIndex--]; } } return leftIndex; // leftIndex一定指向了最终数组末尾的下一个元素 } };
3、总结
数组是存放在连续内存空间上的相同类型数据的集合,移除元素后需要对剩余的元素进行重新移动。同时,移除元素时不能直接删除,只能对其进行覆盖。vector.earse()的时间复杂度是O(n)。