704. 二分查找
给定一个
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]
之间。
解题思路:遇到有序且无重复的序列,查找值的下标可用二分法。
注意要点:
1. 下标截止条件:while,对于左闭右开区间,left<right时均有意义
2. 下标更新:left不加1会陷入循环。eg: {0, 1}, target = 2, 此时left将不停被复制为0.
class Solution {
public:
int search(vector<int>& nums, int target) {
int n = nums.size();
if ( target < nums[0] || target > nums[n-1] ) {
return -1;
}
int left = 0;
int right = n;
// 选择区间为左闭右开
while (left < right) {
int middle = (left + right)/2;
if (target < nums[middle]) {
right = middle;
}
else if (target > nums[middle]) {
left = middle + 1; //加1很关键,不加1会导致程序陷入循环
}
else
return middle;
}
return -1;
}
};
附加左闭右闭版本,也较为简单。
class Solution {
public:
int search(vector<int>& nums, int target) {
int n = nums.size();
int left = 0;
int right = n-1;
while (left <= right){
int middle = (left + right) / 2;
if (target == nums[middle]) {
return middle;
}
else if (target < nums[middle]) {
right = middle - 1;
}
else if (target > nums[middle]) {
left = middle + 1;
}
}
return -1;
}
};
相关题目:
35. 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为
O(log n)
的算法。示例 1:
输入: nums = [1,3,5,6], target = 5 输出: 2示例 2:
输入: nums = [1,3,5,6], target = 2 输出: 1示例 3:
输入: nums = [1,3,5,6], target = 7 输出: 4提示:
1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums
为 无重复元素 的 升序 排列数组-104 <= target <= 104
左闭右开版本,返回下标时可直接用right优化。
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int n = nums.size();
int left = 0;
int right = n;
// 左闭右开
while (left < right) {
int middle = (left + right) / 2;
if (target == nums[middle]) {
return middle;
}
else if (target < nums[middle]) {
right = middle;
}
else if (target > nums[middle]) {
left = middle + 1;
}
}
/*
if (left >= n)
return n;
else if (nums[left] < target)
return left + 1;
else
return left;
*/
// 令人惊奇的是上述代码可以直接优化为right, 仔细考虑一下四种情况即可
return right;
}
};
左闭右闭版本
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int n = nums.size();
int left = 0;
int right = n-1;
while (left <= right) {
int middle = (left + right) / 2;
if (target == nums[middle])
return middle;
else if (target < nums[middle])
right = middle - 1;
else
left = middle + 1;
}
return right + 1;
}
};
34. 找元素的起终位置(难)
给你一个按照非递减顺序排列的整数数组
nums
,和一个目标值target
。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值
target
,返回[-1, -1]
。你必须设计并实现时间复杂度为
O(log n)
的算法解决此问题。示例 1:
输入:nums = [5,7,7,8,8,10]
, target = 8 输出:[3,4]示例 2:
输入:nums = [5,7,7,8,8,10]
, target = 6 输出:[-1,-1]示例 3:
输入:nums = [], target = 0 输出:[-1,-1]提示:
0 <= nums.length <= 105
-109 <= nums[i] <= 109
nums
是一个非递减数组-109 <= target <= 109
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int leftBorder = getLeftBorder(nums, target);
int rightBorder = getRightBorder(nums, target);
// 情况一:target在数组范围之外,即最左或最右
if (leftBorder == -2 || rightBorder == -2) return {-1, -1};
// 情况二:在范围之内,且存在于数组中
if (rightBorder - leftBorder >= 2) return {leftBorder+1, rightBorder-1};
// 情况三:在范围之内,且不存在于数组中,此时左右边界无意义
return {-1, -1};
}
//二分法找右边界, 左闭右闭版本
int getRightBorder(vector<int>& nums, int target) {
int n = nums.size();
int left = 0;
int right = n-1;
int rightBorder = -2;
while (left <= right) {
int mid = (left + right) / 2;
if (nums[mid] > target)
right = mid - 1;
else {
left = mid + 1;
rightBorder = left;
}
}
return rightBorder;
}
//二分法寻找左边界,左闭右闭版本
int getLeftBorder(vector<int>& nums, int target) {
int n = nums.size();
int left = 0;
int right = n-1;
int leftBorder = -2;
while (left <= right) {
int mid = (left + right) / 2;
if (nums[mid] < target) {
left = mid + 1;
}
else {
right = mid - 1;
leftBorder = right;
}
}
return leftBorder;
}
};
69. x的平方根
本质:求出满足k*k <= x的最大值k
注意:mid*mid可能超出int 范围,需改成long long
class Solution {
public:
int mySqrt(int x) {
int left = 0;
int right = x;
int ans = -1;
while (left <= right) {
int mid = (left + right) / 2;
if ((long long)mid * mid <= x) {
ans = mid;
left = mid + 1;
}
else
right = mid - 1;
}
return ans;
}
};
367. 有效完全平方数
给你一个正整数
num
。如果num
是一个完全平方数,则返回true
,否则返回false
。完全平方数 是一个可以写成某个整数的平方的整数。换句话说,它可以写成某个整数和自身的乘积。
不能使用任何内置的库函数,如
sqrt
。示例 1:
输入:num = 16 输出:true 解释:返回 true ,因为 4 * 4 = 16 且 4 是一个整数。示例 2:
输入:num = 14 输出:false 解释:返回 false ,因为 3.742 * 3.742 = 14 但 3.742 不是一个整数。提示:
1 <= num <= 231 - 1
class Solution {
public:
bool isPerfectSquare(int num) {
long long int left = 1;
long long int right = num;
while (left <= right) {
long long int mid = (left + right) / 2;
if ( mid*mid == num)
return true;
else if ( mid*mid < num) {
left = mid + 1;
}
else
right = mid - 1;
}
return false;
}
};
27. 移除元素
给你一个数组
nums
和一个值val
,你需要 原地 移除所有数值等于val
的元素。元素的顺序可能发生改变。然后返回nums
中与val
不同的元素的数量。假设
nums
中不等于val
的元素数量为k
,要通过此题,您需要执行以下操作:
- 更改
nums
数组,使nums
的前k
个元素包含不等于val
的元素。nums
的其余元素和nums
的大小并不重要。- 返回
k。
示例 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 fastIndex = 0;
int slowIndex = 0;
while (fastIndex < nums.size()) {
if (nums[fastIndex] == val) {
fastIndex++;
continue;
}
nums[slowIndex] = nums[fastIndex];
fastIndex++;
slowIndex++;
}
return slowIndex;
}
};
相关题目:
26. 删除重复项
给你一个 非严格递增排列 的数组
nums
,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回nums
中唯一元素的个数。考虑
nums
的唯一元素的数量为k
,你需要做以下事情确保你的题解可以被通过:
- 更改数组
nums
,使nums
的前k
个元素包含唯一元素,并按照它们最初在nums
中出现的顺序排列。nums
的其余元素与nums
的大小不重要。- 返回
k
.
注意:判定下标时,一定先将出界条件放在前面
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int slowIndex = 0;
int fastIndex = 0;
int n = nums.size();
while (fastIndex < n) {
nums[slowIndex] = nums[fastIndex];
while (fastIndex < n && nums[fastIndex] == nums[slowIndex])
fastIndex++;
slowIndex++;
}
return slowIndex;
}
};
283. 移动0
给定一个数组
nums
,编写一个函数将所有0
移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:
输入: nums =[0,1,0,3,12]
输出:[1,3,12,0,0]
示例 2:
输入: nums =[0]
输出:[0]
提示:
1 <= nums.length <= 104
-231 <= nums[i] <= 231 - 1
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int n = nums.size();
int slowIndex = 0;
int fastIndex = 0;
while (fastIndex < n) {
if (nums[fastIndex] == 0) {
fastIndex++;
continue;
}
nums[slowIndex] = nums[fastIndex];
slowIndex++;
fastIndex++;
}
while (slowIndex < n) {
nums[slowIndex] = 0;
slowIndex++;
}
}
};
844. 比较含退格的字符串(易错)
给定
s
和t
两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回true
。#
代表退格字符。注意:如果对空文本输入退格字符,文本继续为空。
示例 1:
输入:s = "ab#c", t = "ad#c" 输出:true 解释:s 和 t 都会变成 "ac"。示例 2:
输入:s = "ab##", t = "c#d#" 输出:true 解释:s 和 t 都会变成 ""。示例 3:
输入:s = "a#c", t = "b" 输出:false 解释:s 会变成 "c",但 t 仍然是 "b"。提示:
1 <= s.length, t.length <= 200
s
和t
只含有小写字母以及字符'#'
注意:退格可能会退很多格!
class Solution {
public:
bool backspaceCompare(string s, string t) {
int n = s.size();
int slowIndex = 0;
int fastIndex = 0;
while (fastIndex < n){
if (s[fastIndex] == '#') {
if (slowIndex > 0)
slowIndex--;
}
else {
s[slowIndex] = s[fastIndex];
slowIndex++;
}
fastIndex++;
}
int len1 = slowIndex;
n = t.size();
slowIndex = 0;
fastIndex = 0;
while (fastIndex < n){
if (t[fastIndex] == '#') {
if (slowIndex > 0)
slowIndex--;
}
else {
t[slowIndex] = t[fastIndex];
slowIndex++;
}
fastIndex++;
}
int len2 = slowIndex;
if (len1 != len2) return false;
for (int i = 0; i < len1; i++) {
if (s[i] != t[i])
return false;
}
return true;
}
};
977. 有序数组的平方
给你一个按 非递减顺序 排序的整数数组
nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。示例 1:
输入:nums = [-4,-1,0,3,10] 输出:[0,1,9,16,100] 解释:平方后,数组变为 [16,1,0,9,100] 排序后,数组变为 [0,1,9,16,100]示例 2:
输入:nums = [-7,-3,2,3,11] 输出:[4,9,9,49,121]提示:
1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums
已按 非递减顺序 排序进阶:
- 请你设计时间复杂度为
O(n)
的算法解决本问题
注意:排序时注意变通,有时可以倒序排序,转换正逆向思维,如本题的双指针可以从外向内延申。
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int n = nums.size();
// for (int i=0; i<n; i++) {
// if (nums[i] >= 0)
// break;
// }
// int leftIndex = i;
// int rightIndex = i;
vector<int> ans(n, 0);
int leftIndex = 0;
int rightIndex = n-1;
for (int i = n-1; i>-1; i--) {
if (nums[leftIndex]*nums[leftIndex] > nums[rightIndex]*nums[rightIndex]) {
ans[i] = nums[leftIndex]*nums[leftIndex];
leftIndex++;
}
else {
ans[i] = nums[rightIndex]*nums[rightIndex];
rightIndex--;
}
}
return ans;
}
};