目录
1.简单
1.1删除排序数组中的重复项(计数法)
1.2移除元素(计数法)
2.中等
2.1盛最多水的容器(双指针)
2.2最接近的三数之和(双指针)
2.3 下一个排序
2.4搜索旋转排序数组(二分法)
2.5在排序数组中查找元素的第一个和最后一个位置(二分法)
2.6旋转图像
2.7螺旋矩阵
2.8合并区间
2.9螺旋矩阵Ⅱ
3.困难
3.1缺失的第一个正数
3.2插入区间
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array
解法:设置count指针,统计当前重复的数量,且元素向前移count个单位
int removeDuplicates(vector& nums){
int count = 0;
for(int i = 1; i < nums.size(); i++){
if(nums[i] == nums[i - 1]){
count++;
}
nums[i - count] = nums[i];
}
return nums.size() - count;
}
执行用时 :16 ms, 在所有 C++ 提交中击败了87.07%的用户
内存消耗 :10 MB, 在所有 C++ 提交中击败了27.10%的用户
给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-element
计数法
int removeElement(vector& nums, int val) {
int count = 0;
for (int i = 0; i < nums.size(); ++i) {
nums[i - count] = nums[i];
if(nums[i] == val)
count++;
}
return nums.size() - count;
}
给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/container-with-most-water
int maxArea(vector& height) {
int left = 0, right = height.size() - 1;
int maxArea = 0;
int temp = 0;
while (left < right){
if(height[left] > height[right]){ //right小了,需要找更大的
temp = height[right] * (right - left);
right--;
} else if(height[left] < height[right]){//left小了,需要找更大的
temp = height[left] * (right - left);
left++;
} else{
temp = height[left] * (right - left);
left++;
right--;
}
maxArea = max(maxArea, temp);
}
return maxArea;
}
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.
与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum-closest
int threeSumClosest(vector& nums, int target) {
sort(nums.begin(), nums.end());
int closest = nums[0] + nums[1] + nums[2];
for(int i = 0; i < nums.size(); i++){
int left = i + 1, right = nums.size() - 1;
while(left < right){
int sum = nums[i] + nums[left] + nums[right];
if(sum == target)
return target;
if(sum < target){
left++;
} else{
right--;
}
//存储更接近target的sum
if(abs(target - sum) < abs(target - closest)){
closest = sum;
}
}
}
return closest;
}
实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须原地修改,只允许使用额外常数空间。
以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/next-permutation
抄了一张图,仔细想想确实是那么回事。
void nextPermutation(vector& nums) {
int i = nums.size() - 2;
while(i >= 0){
if(nums[i] >= nums[i + 1]){
i--;
} else{
int j = i + 1;
while(j < nums.size()){
if(nums[j] > nums[i])
j++;
else
break;
}
swap(nums[i], nums[j - 1]);
sort(nums.begin() + i + 1, nums.end());
return;
}
}
sort(nums.begin(), nums.end());
}
执行用时 :4 ms, 在所有 C++ 提交中击败了94.73%的用户
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别。
示例 1:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/search-in-rotated-sorted-array
根据题意O(log n),肯定是采用二分法,但是不是完全排序的,所以要找规律,主要分为以下三种情况:
int search(vector& nums, int target) {
int left = 0, right = nums.size() - 1;
while(left <= right){
int mid = (left + right) / 2;
if(nums[mid] == target)
return mid;
if(nums[left] == target)
return left;
if(nums[right] == target)
return right;
if(nums[mid] > nums[left] && nums[mid] > nums[right]){//第一种情况
if(target > nums[left] && target < nums[mid]){
right = mid - 1;
} else{
left = mid + 1;
}
} else if(nums[mid] < nums[left] && nums[mid] < nums[right]){//第二种情况
if(target > nums[mid] && target < nums[right]){
left = mid + 1;
} else{
right = mid - 1;
}
} else{ //第三种情况
if(nums[mid] > target){
right = mid - 1;
} else{
left = mid + 1;
}
}
}
return -1;
}
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(log n) 级别。
如果数组中不存在目标值,返回 [-1, -1]。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array
最左和最右位置分两次查找,均采用二分法,比如查最左元素时,nums[mid] == target && nums[mid - 1] < target才是正确位置(特殊考虑mid == 0的情况),最右元素同理。
vector searchRange(vector& nums, int target) {
vector res = {-1, -1};
//开始位置
int left = 0, right = nums.size() - 1, mid;
while(left <= right){
mid = (left + right) / 2;
if(mid > 0){
if(nums[mid] == target){
if(nums[mid - 1] < target){//成功
res[0] = mid;
break;
} else if(nums[mid - 1] == target){
right = mid - 1;
}
} else if(nums[mid] < target){
left = mid + 1;
} else if(nums[mid] > target){
right = mid - 1;
}
} else if(mid == 0){ //防指针越界,特殊考虑
if(nums[mid] == target){
res[0] = mid;
break;
} else if(nums[mid] < target){
left = mid + 1;
} else
break;
}
}
//结束位置
left = 0, right = nums.size() - 1;
while(left <= right){
mid = (left + right) / 2;
if(mid < nums.size() - 1){
if(nums[mid] == target){
if(nums[mid + 1] > target){ //成功
res[1] = mid;
break;
} else if(nums[mid + 1] == target){
left = mid + 1;
}
} else if(nums[mid] < target){
left = mid + 1;
} else if(nums[mid] > target){
right = mid - 1;
}
} else if(mid == nums.size() - 1){ //特殊考虑
if(nums[mid] == target){
res[1] = mid;
break;
} else if(nums[mid] > target){
right = mid - 1;
} else
break;
}
}
return res;
}
给定一个 n × n 的二维矩阵表示一个图像。
将图像顺时针旋转 90 度。
你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。
给定 matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],
原地旋转输入矩阵,使其变为:
[
[7,4,1],
[8,5,2],
[9,6,3]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/rotate-image
先暴力解就完事了,结合学过的矩阵的知识,发现先转置,然后逐行反转,能解决问题而且能快速写出来。
void rotate(vector>& matrix) {
//先进行转置
int len = matrix.size();
if(len <= 1)
return;
for (int i = 0; i < len; ++i) {
for (int j = i + 1; j < len; ++j) {
swap(matrix[i][j], matrix[j][i]);
}
}
//逐行反转
for (int i = 0; i < len; ++i) {
int left = 0, right = len - 1;
while(left < right){
swap(matrix[i][left], matrix[i][right]);
left++;
right--;
}
}
}
给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。
示例 1:
输入:
[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
]
输出: [1,2,3,6,9,8,7,4,5]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/spiral-matrix
不需要记录是不是已经访问过了,就不断的更新这4个变量:行上界、行下界、列左界、列右界,然后依次按往右、往下、往左、往上的顺序遍历就行了,一旦有边界值交错,就break。
vector spiralOrder(vector>& matrix) {
vector res;
int rowBegin = 0, colBegin = 0;
if(matrix.empty())
return res;
int rowLen = matrix.size(), colLen = matrix[0].size();
while(rowBegin < rowLen && colBegin < colLen){
for(int i = colBegin; i < colLen; i++){ //往右
res.push_back(matrix[rowBegin][i]);
}
rowBegin++;//缩小行上界
if(rowBegin >= rowLen)
break;
for(int i = rowBegin; i < rowLen; i++){//往下
res.push_back(matrix[i][colLen - 1]);
}
colLen--;//缩小列右界
if(colLen <= colBegin)
break;
for(int i = colLen - 1; i >= colBegin; i--){ //往左
res.push_back(matrix[rowLen - 1][i]);
}
rowLen--;//缩小行下界
if(rowLen <= rowBegin)
break;
for(int i = rowLen - 1; i >= rowBegin; i--){ //往上
res.push_back(matrix[i][colBegin]);
}
colBegin++;//缩小列左界
}
return res;
}
给出一个区间的集合,请合并所有重叠的区间。
示例 1:
输入: [[1,3],[2,6],[8,10],[15,18]]
输出: [[1,6],[8,10],[15,18]]
解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/merge-intervals
根据区间左边界排序,首先把第一个区间加到res中去,这样就能保证res一直有值,如果下一个区间的左边界小于当前区间的右边界,合并;循环。合并中需要注意,当前区间右边界是否同时大于下一个区间的左右边界。
vector> merge(vector>& intervals) {
vector> res;
if (intervals.empty())
return res;
sort(intervals.begin(), intervals.end()); //按第一位排序
res.push_back(intervals[0]);
int i = 1;
while(i < intervals.size()){
int len = res.size(); //获取当前区间
if(res[len - 1][1] >= intervals[i][0]){
if(res[len - 1][1] < intervals[i][1]){
vector temp = {res[len - 1][0], intervals[i][1]};
res.pop_back();
res.push_back(temp);
}
i++;
} else{
res.push_back(intervals[i]);
i++;
}
}
return res;
}
给定一个正整数 n,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
输入: 3
输出:
[
[ 1, 2, 3 ],
[ 8, 9, 4 ],
[ 7, 6, 5 ]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/spiral-matrix-ii
同螺旋矩阵一,边遍历边改变上下左右边界值。
vector> generateMatrix(int n) {
vector > res(n, vector(n));
if (n == 0)
return res;
int num = 1;
int rowBegin = 0, rowEnd = n - 1; //行边界值
int colBegin = 0, colEnd = n - 1; //列边界值
while(rowBegin <= rowEnd && colBegin <= colEnd){
for (int i = colBegin; i <= colEnd; ++i) {
res[rowBegin][i] = num++;
}
rowBegin++;
for (int i = rowBegin; i <= rowEnd; ++i) {
res[i][colEnd] = num++;
}
colEnd--;
for (int i = colEnd; i >= colBegin; i--) {
res[rowEnd][i] = num++;
}
rowEnd--;
for (int i = rowEnd; i >= rowBegin; i--) {
res[i][colBegin] = num++;
}
colBegin++;
}
return res;
}
给定一个未排序的整数数组,找出其中没有出现的最小的正整数。
示例 1:
输入: [1,2,0]
输出: 3
示例 2:
输入: [3,4,-1,1]
输出: 2
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/first-missing-positive
参考大佬的解法:
全文链接:https://leetcode-cn.com/problems/first-missing-positive/solution/tong-pai-xu-python-dai-ma-by-liweiwei1419/
int firstMissingPositive(vector& nums) {
for (int i = 0; i < nums.size(); i++) {
while (nums[i] != i + 1) {
if (nums[i] <= 0 || nums[i] > nums.size() || nums[i] == nums[nums[i] - 1])
break;
// 将nums[i] 放置到对应位置上[1,2,3...]
int idx = nums[i] - 1;
nums[i] = nums[idx];
nums[idx] = idx + 1;
}
}
for (int i = 0; i < nums.size(); i++) {
if (nums[i] != (i + 1)) {
return (i + 1);
}
}
return (nums.size() + 1);
}
给出一个无重叠的 ,按照区间起始端点排序的区间列表。
在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。
示例 1:
输入: intervals = [[1,3],[6,9]], newInterval = [2,5]
输出: [[1,5],[6,9]]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/insert-interval
这道题需要考虑的细节会多一些,步骤大概如下:
vector> insert(vector>& intervals, vector& newInterval) {
vector> res;
if(intervals.empty()){
res.push_back(newInterval);
return res;
}
int len = intervals.size();
int flag = 0;
//首先插入一个区间到res
if(newInterval[0] < intervals[0][0]){
res.push_back(newInterval);
flag = 1;
} else{
res.push_back(intervals[0]);
}
int i; //获取插入的位置
for (i = 0; i < len; ++i) {
if(flag == 1){
break;
}
int resLen = res.size();
//满足该条件就说明区间该插在这个位置,并插入该区间
if(newInterval[0] >= res[resLen - 1][0] && newInterval[0] <= intervals[i][0]){
if(res[resLen - 1][1] >= newInterval[0]){
if(res[resLen - 1][1] >= newInterval[1]){
flag = 1;
} else{
vector temp = {res[resLen - 1][0], newInterval[1]};
res.pop_back();
res.push_back(temp);
flag = 1;
}
} else{
res.push_back(newInterval);
flag = 1;
}
break; //插入完成,退出循环
} else{
//插入intervals里的区间
if(res[resLen - 1][1] < intervals[i][0])
res.push_back(intervals[i]);
}
}
//如果区间应该在末尾合并
if(flag == 0){
int resLen = res.size();
if(res[resLen - 1][1] >= newInterval[0]){
if(res[resLen - 1][1] < newInterval[1]){
vector temp = {res[resLen - 1][0], newInterval[1]};
res.pop_back();
res.push_back(temp);
}
} else{
res.push_back(newInterval);
}
}
//逐步合并intervals中剩余区间
for (int j = i; j < len; ++j) {
int resLen = res.size();
if(res[resLen - 1][1] >= intervals[j][0]){
if(res[resLen - 1][1] >= intervals[j][1]){
continue;
} else{
vector temp = {res[resLen - 1][0], intervals[j][1]};
res.pop_back();
res.push_back(temp);
}
} else{
res.push_back(intervals[j]);
}
}
return res;
}
执行用时:12 ms