数组
二级目录
二分查找
34. 在排序数组中查找元素的第一个和最后一个位置
class Solution {
public:
int binarySearch(vector<int>& nums, int target, bool lower)
{
int l = 0, r = nums.size()-1, mid;
int result = nums.size();
while(l <= r)
{
mid = l + (r - l)/2;
if((lower && nums[mid] >= target) || (nums[mid] > target))
{
result = mid;
r = mid - 1;
}
else
{
l = mid + 1;
}
}
return result;
}
vector<int> searchRange(vector<int>& nums, int target) {
if (nums.empty()) return vector<int> {-1, -1};
int leftIdx = binarySearch(nums, target, true);
int rightIdx = binarySearch(nums, target, false) - 1;
if (leftIdx <= rightIdx && rightIdx < nums.size() && nums[leftIdx] == target && nums[rightIdx] == target) {
return vector<int>{leftIdx, rightIdx};
}
return vector<int> {-1, -1};
}
};
自主写法,参考数据结构与算法之美!
class Solution {
public:
int binarySearch(vector<int>& nums, int target, bool flag)
{
int l = 0, r = nums.size()-1, mid;
int result = nums.size();
while(l <= r)
{
mid = l + (r - l)/2;
if(nums[mid] > target)
{
r = mid - 1;
}
else if(nums[mid] < target)
{
l = mid + 1;
}
else
{
if(flag)
{
if(mid == 0 || nums[mid - 1] != target) { return mid; }
else { r = mid - 1; }
}
else
{
if(mid == nums.size() - 1 || nums[mid + 1] != target) { return mid; }
else { l = mid + 1; }
}
}
}
return -1;
}
vector<int> searchRange(vector<int>& nums, int target) {
if (nums.empty()) return vector<int> {-1, -1};
int leftIdx = binarySearch(nums, target, true);
int rightIdx = binarySearch(nums, target, false);
if (leftIdx <= rightIdx && rightIdx < nums.size() && nums[leftIdx] == target && nums[rightIdx] == target) {
return vector<int>{leftIdx, rightIdx};
}
return vector<int> {-1, -1};
}
};
查找第一个大于等于给定值的元素
public int bsearch(int[] a, int n, int value) {
int l = 0;
int r = n - 1;
while (l <= r) {
int mid = l + ((r - l) >> 1);
if (a[mid] >= value) {
if ((mid == 0) || (a[mid - 1] < value)) return mid;
else r = mid - 1;
} else {
l = mid + 1;
}
}
return -1;
}
查找最后一个小于等于给定值的元素
public int bsearch7(int[] a, int n, int value) {
int l = 0;
int r = n - 1;
while (l <= r) {
int mid = l + ((r - l) >> 1);
if (a[mid] > value) {
r = mid - 1;
} else {
if ((mid == n - 1) || (a[mid + 1] > value)) return mid;
else l = mid + 1;
}
}
return -1;
}
69. x 的平方根
class Solution {
public:
int mySqrt(int x) {
int l=0, r=x, ans = -1;
int mid;
while(l <= r)
{
mid = l + (r - l) / 2;
if((long long)mid * mid <= x) { ans = mid; l = mid + 1; }
else { r = mid - 1; }
}
return ans;
}
};
带有精度怎么考虑?
367. 有效的完全平方数
class Solution {
public:
bool isPerfectSquare(int num) {
int l=0, r=num;
int mid;
while(l <= r)
{
mid = l + (r - l) / 2;
if((long long )mid * mid > num) { r = mid - 1; }
else if((long long)mid * mid < num) { l = mid + 1; }
else { return true; }
}
return false;
}
};
33. 搜索旋转排序数组(×✓)
class Solution {
public:
int search(vector<int>& nums, int target) {
int l=0, r=nums.size()-1;
while(l <= r)
{
int mid = l + (r - l) / 2;
if(nums[mid] == target) { return mid; }
if(nums[l] <= nums[mid])
{
if(nums[l] <= target && target < nums[mid])
{ r = mid - 1; }
else
{ l = mid + 1; }
}
else
{
if(nums[mid] < target && target <= nums[r])
{ l = mid + 1; }
else
{ r = mid - 1; }
}
}
return -1;
}
};
81. 搜索旋转排序数组 II(√)
class Solution {
public:
bool search(vector<int>& nums, int target) {
int l=0, r=nums.size()-1;
while(l <= r)
{
int mid = l + (r - l) / 2;
if(nums[mid] == target) { return true; }
if(nums[l] < nums[mid])
{
if( nums[l] <= target && target < nums[mid])
r = mid - 1;
else
l = mid + 1;
}
else if(nums[l] > nums[mid])
{
if(nums[mid] < target && target <= nums[r])
l = mid + 1;
else
r = mid - 1;
}
else
{
//skip duplicate one
l++;
}
}
return false;
}
};
移除元素
双指针解法。
27. 移除元素(√)
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
/*
for(vector<int>::iterator it = nums.begin(); it!=nums.end(); ++it)
{
if(*it == val)
{
it = nums.erase(it);
--it;
}
}
return nums.size();
*/
int slowIdx = 0;
int n = nums.size();
for(int fastIdx=0; fastIdx<n; ++fastIdx)
{
if(nums[fastIdx] != val)
{
nums[slowIdx++] = nums[fastIdx];
}
}
return slowIdx;
}
};
26.删除排序数组中的重复项(√)
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
if(nums.empty()) { return 0; }
int slowIdx=0;
for(int fastIdx=0; fastIdx<nums.size(); ++fastIdx)
{
if(nums[fastIdx] != nums[slowIdx])
{
nums[++slowIdx] = nums[fastIdx];
}
}
return ++slowIdx;
}
};
80. 删除有序数组中的重复项 II(×)
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
if(nums.size() <= 2) { return nums.size(); }
int slowIdx = 2;
for(int fastIdx = 2; fastIdx<nums.size(); ++fastIdx)
{
if(nums[fastIdx] != nums[slowIdx-2])
{
nums[slowIdx++] = nums[fastIdx];
}
}
return slowIdx;
}
};
283. 移动零(×)没思路。
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int slow = 0;
for(int fast = 0; fast<nums.size(); ++fast)
{
if(nums[fast] != 0)
{
swap(nums[slow], nums[fast]);
slow++;
}
}
}
};
977. 有序数组的平方
方法一:直接排序。(√)
方法二:双指针。(×)
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
vector<int> result(nums.size(), 0); //写法学习下,陌生
int k = nums.size()-1;
for(int i=0, j=nums.size()-1; i<=j; )
{
if(nums[i]*nums[i] > nums[j]*nums[j])
{
result[k--] = nums[i]*nums[i];
i++;
}
else
{
result[k--] = nums[j]*nums[j];
j--;
}
}
return result;
}
};
209. 长度最小的子数组(滑动窗口 / 双指针)
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int result = INT_MAX;
int sum = 0; // 滑动窗口数值之和
int subLength = 0; // 滑动窗口的长度
int i=0; // 滑动窗口起始位置
for(int j=0; j<nums.size(); ++j)
{
sum += nums[j];
// 注意这里使用while,每次更新 i(起始位置),并不断比较子序列是否符合条件
while(sum >= target)
{
subLength = j - i + 1; // 取子序列的长度
result = result < subLength ? result : subLength;
// 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置)
sum -= nums[i++];
}
}
// 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
return result == INT_MAX ? 0 : result;
}
};
904. 水果成篮(未学习×)
76. 最小覆盖子串(未学习×)
844. 比较含退格的字符串
方法一:重构字符串
class Solution {
private:
string build(string str)
{
string ret;
for(auto ch : str)
{
if(ch != '#') { ret.push_back(ch); }
else if(!ret.empty()){ ret.pop_back(); }
}
return ret;
}
public:
bool backspaceCompare(string s, string t) {
return build(s) == build(t);
}
};
方法二:双指针(待学习)
螺旋矩阵
59. 螺旋矩阵 II
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> result(n, vector<int>(n, 0));
// 定义每循环一个圈的起始位置
int startx = 0, starty = 0;
// 循环几次,例如n为奇数3,那么loop = 1 只是循环一圈,矩阵中间的值需要单独处理
int loop = n / 2;
// 矩阵中间的位置,例如:n为3, 中间的位置就是(1,1),n为5,中间位置为(2, 2)
int mid = n / 2;
int count = 1; // 用来给矩阵中每一个空格赋值
int offset = 1; // 每一圈循环,需要控制每一条边遍历的长度
int i, j;
while(loop--)
{
i = startx;
j = starty;
// 下面开始的四个for就是模拟转了一圈
// 模拟填充上行从左到右(左闭右开)
for(j=starty; j<starty + n - offset; j++)
{
result[startx][j] = count++;
}
// 模拟填充右列从上到下(左闭右开)
for(i=startx; i<startx + n - offset; i++)
{
result[i][j] = count++;
}
// 模拟填充下行从右到左(左闭右开)
for(; j>starty; j--)
{
result[i][j] = count++;
}
// 模拟填充左列从下到上(左闭右开)
for(; i>startx; i--)
{
result[i][j] = count++;
}
// 第二圈开始的时候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
startx++;
starty++;
// offset 控制每一圈里每一条边遍历的长度
offset += 2;
}
// 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
if(n % 2) { result[mid][mid] = count; }
return result;
}
};
54. 螺旋矩阵(没懂×)
剑指 Offer 29. 顺时针打印矩阵(没懂×)
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
if(matrix.size() == 0 || matrix[0].size() == 0) { return {}; }
int rows = matrix.size(), columns = matrix[0].size();
vector<int> result;
int left = 0, right = columns - 1, top = 0, bottom = rows - 1;
while(left <= right && top <= bottom)
{
for(int column = left; column <= right; ++column)
{
result.push_back(matrix[top][column]);
}
for(int row=top+1; row<=bottom; ++row)
{
result.push_back(matrix[row][right]);
}
if(left < right && top < bottom)
{
for(int column = right-1; column > left; --column)
{
result.push_back(matrix[bottom][column]);
}
for(int row = bottom; row > top; --row)
{
result.push_back(matrix[row][left]);
}
}
left++;
right--;
top++;
bottom--;
}
return result;
}
};
15. 三数之和(双指针解法)
1.先排序
2.基本框架
3.注意去重逻辑
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
for(int i=0; i<nums.size(); ++i)
{
// 本题是三数之和为零,所以可有这句代码
if(nums[i] > 0) return result;
// 注意去重逻辑
if(i > 0 && nums[i] == nums[i-1]) continue;
for(int left=i+1, right=nums.size()-1; left<right; )
{
int sum = nums[i] + nums[left] + nums[right];
if(sum > 0) { right--; }
else if(sum < 0) { left++; }
else
{
result.push_back(vector<int>{nums[i], nums[left], nums[right]});
// 注意去重逻辑
while(left<right && nums[right] == nums[right-1]) right--;
while(left<right && nums[left] == nums[left]+1) left++;
right--;
left++;
}
}
}
return result;
}
};
18. 四数之和(双指针解法)
1.先排序
2.基本框架
3.注意去重逻辑
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
for(int i=0; i<nums.size(); ++i)
{
// 注意去重逻辑
if(i > 0 && nums[i] == nums[i-1]) continue;
for(int j=i+1; j<nums.size(); ++j)
{
// 注意去重逻辑
if(j > i+1 && nums[j] == nums[j-1]) continue;
for(int left=j+1, right=nums.size()-1; left<right; )
{
// 整形溢出问题
long long sum = (long long)nums[i] + nums[j] + nums[left] + nums[right];
if(sum > target) right--;
else if(sum < target) left++;
else
{
result.push_back(vector<int> {nums[i], nums[j], nums[left], nums[right]});
// 注意去重逻辑
while(left < right && nums[right] == nums[right-1]) right--;
while(left < right && nums[left] == nums[left+1]) left++;
right--;
left++;
}
}
}
}
return result;
}
};