704.二分查找
题目链接:https://leetcode.cn/problems/binary-search
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例 1:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
示例 2:
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
提示:
你可以假设 nums 中的所有元素是不重复的。
n 将在 [1, 10000]之间。
nums 的每个元素都将在 [-9999, 9999]之间。
左闭右闭
class Solution {
public:
int search(vector<int>& nums, int target) {
//左闭右闭区间
int left = 0, right = nums.size() - 1;
while (left <= right){
int mid = left + (right - left) / 2; //避免溢出
if(nums[mid] == target) { //直接判断目标值是否存在
return mid;
}
else if(nums[mid] < target){
left = mid + 1;
}
else right = mid -1;
}
return -1;
}
};
循环不变量:left左边的元素值一定小于target;right右边的元素值一定大于等于target
循环结束时:right < left; 所以left一定大于等于target,判断left与target的关系来确定target是否存在;但是要注意如果升序数组中没有目标值,最后left可能会越界;
class Solution {
public:
int search(vector<int>& nums, int target) {
//左闭右闭区间
int left = 0, right = nums.size() - 1;
while (left <= right){
int mid = left + (right - left) / 2; //避免溢出
if(nums[mid] < target){
left = mid + 1;
}
else right = mid -1;
}
return left < nums.size() && nums[left] == target ? left : -1;
}
};
左闭右开
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0, right = nums.size(), mid;
while(left < right){
mid = left + (right - left)/2;
if(target == nums[mid]){
return mid;
}
else if(target < nums[mid]){
right = mid;
}
else if(target > nums[mid]){
left = mid + 1;
}
}
return -1;
}
};
循环不变量:left左边的元素值一定小于target;right以及right右边的元素值一定大于等于target
循环结束时:right = left; 所以left一定大于等于target,判断left与target的关系来确定target是否存在;但是要注意如果升序数组中没有目标值,最后left可能会越界;
class Solution {
public:
int search(vector<int>& nums, int target) {
//左闭右开区间
int left = 0, right = nums.size();
while (left < right){
int mid = left + (right - left) / 2; //避免溢出
if(nums[mid] < target){
left = mid + 1; //区间更新为[mid+1, right)
}
else right = mid;//区间更新为[left, mid)
}
return left < nums.size() && (nums[left] == target) ? left : -1;
}
};
27. 移除元素
题目链接:https://leetcode.cn/problems/remove-element/
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。
假设 nums 中不等于 val 的元素数量为 k,要通过此题,您需要执行以下操作:
更改 nums 数组,使 nums 的前 k 个元素包含不等于 val 的元素。nums 的其余元素和 nums 的大小并不重要。
返回 k。
用户评测:
评测机将使用以下代码测试您的解决方案:
int[] nums = […]; // 输入数组
int val = …; // 要移除的值
int[] expectedNums = […]; // 长度正确的预期答案。
// 它以不等于 val 的值排序。
int k = removeElement(nums, val); // 调用你的实现
assert k == expectedNums.length;
sort(nums, 0, k); // 排序 nums 的前 k 个元素
for (int i = 0; i < actualLength; i++) {
assert nums[i] == expectedNums[i];
}
如果所有的断言都通过,你的解决方案将会 通过。
示例 1:
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2,,]
解释:你的函数函数应该返回 k = 2, 并且 nums 中的前两个元素均为 2。
你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。
示例 2:
输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3,,,_]
解释:你的函数应该返回 k = 5,并且 nums 中的前五个元素为 0,0,1,3,4。
注意这五个元素可以任意顺序返回。
你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。
提示:
0 <= nums.length <= 100
0 <= nums[i] <= 50
0 <= val <= 100
暴力
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
//暴力
int len = nums.size();
for(int i = 0; i < len; ++i){
if(nums[i] == val){
//所有元素前移 直接覆盖 因为输出不判断后面的值 无所谓
for(int j = i + 1; j < len; ++j){
nums[j - 1] = nums[j];
}
--i;//元素前移更新了当前 i 的元素,先 -- 再在循环中 ++ 可以重新判断 nums[i]
--len;
}
}
return len;
}
};
异向双指针
超慢 击败8.92%
有一点二分的影子在里面
//时间复杂度O(n)
//空间复杂度O(1)
class Solution {
public:
void swap(int &a, int &b){
int temp = a;
a = b;
b = temp;
}
int removeElement(vector<int>& nums, int val) {
if (nums.size() == 0){ //数组为空,要单独判断,否则会产生下标越界错误
return 0;
}
int i = 0;
int j = nums.size() - 1;
//下标 j 右边的元素等于 val, i 左边的元素不等于 val, 中间的元素待判断,左右指针不断缩小区间
//循环结束时 i = j , 根据 i 左边的元素不等于 val, 最后在判断 nums[i] 和 val 的关系即可;
//元素总个数为 下标加1;
while (i < j){
if (nums[i] == val){
swap(nums[i], nums[j]);
--j;
}
else {
++i;
}
}
if(nums[i] == val){
return i;
}
return i+1;
}
};
优化后
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int i = 0;
int j = nums.size() - 1;
//下标 j 右边的元素等于 val, i 左边的元素不等于 val, 中间的元素待判断,左右指针不断缩小区间
//循环结束时 i > j , 根据 i 左边的元素不等于 val,
//元素总个数为 j + 1 = i;
while (i <= j){
if (nums[i] == val){
nums[i] = nums[j]; //直接覆盖重复的元素,更新后的 nums[i] 也要重新判断
--j;
}
else {
++i;
}
}
return i;
}
};
双指针
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slow = 0;
int fast = 0;
while (fast < nums.size()){
if(nums[fast] != val){
nums[slow] = nums[fast];
++slow;
}
++fast;
}
return slow;
}
};
这里就是slow表示 不等于val 的数组下标,fast 去遍历原来的数组,如果nums[fast]不等于val,满足条件,就更新到nums[slow],并更新slow;