Leetcode刷题之路-数组类型1
目录
189.旋转数组
给定一个数组,将数组元素向右移动k个位置,其中k是非负数。
示例:
输入: [1,2,3,4,5,6,7] 和 k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
输入: [-1,-100,3,99] 和 k = 2
输出: [3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]
说明:
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
要求使用空间复杂度为 O(1) 的 原地 算法。
- [ 旋转数组在不开辟其他空间的前提下,用反转法 ]
- [ 后k项反转,再把前len-k项反转,最后整个数组整体反转,就可以得出答案 ]
- [ 反转的顺序无关紧要 ]
void rotate(vector<int>& nums, int k) {
int len = nums.size();
vector<int>::iterator Start = nums.begin();
k = k%len;
reverse(Start,Start+len-k);
reverse(Start+len-k,nums.end());
reverse(Start,nums.end());
}
56.合并区间
给定一个区间的集合,请合并所有重叠的区间
示例:
输入: intervals = [[1,3],[2,6],[8,10],[15,18]]
输出: [[1,6],[8,10],[15,18]]
解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6]
输入: intervals = [[1,4],[4,5]]
输出: [[1,5]]
解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。
提示:
intervals[i][0] <= intervals[i][1]
- [ 新手只会用暴力方法。。 ]
- [ 遍历吧,第一次是从头开始,判断正确,则合并,然后调用erase删除第一个元素,然后就悲剧了 ]
- [ 数组删除一个元素就需要后面的全部元素往前挪移一位,删除第一个导致耗时极大 ]
- [ 所以在之前的基础上,修改成从后面开始遍历,删除也只是删除后面的元素,往前挪的时间消费就少了 ]
- [ 当然,需要先排序。而且需要手动制定排序规则,这里是按数组的第二个元素的大小,从小到大排序 ]
- [ 然后再从后往前遍历,直接修改vector<vector
>就可以了,不需要额外申请空间 ]
vector<vector<int>> merge(vector<vector<int>>& intervals) {
int boxsize = intervals.size();
if (boxsize == 0) return intervals;
int i = boxsize-2;
auto cmp=[](const vector<int>& v1,const vector<int>& v2){return v1[1]<v2[1];};
sort(intervals.begin(), intervals.end(),cmp);
while (boxsize - 1 != 0) {
if (i < 0) return intervals;
if (intervals[i][1] >= intervals[i + 1][0] || intervals[i][1] >= intervals[i + 1][1]) {
intervals[i + 1][0] = intervals[i + 1][0] < intervals[i][0] ? intervals[i + 1][0] : intervals[i][0];
intervals[i + 1][1] = intervals[i + 1][1] > intervals[i][1] ? intervals[i + 1][1] : intervals[i][1];
intervals.erase(intervals.begin() + i);
boxsize = intervals.size();
i--;
}
else {
boxsize = intervals.size();
if (i >= 0) {
i--;
}
}
}
return intervals;
189.旋转矩阵
给你一幅由 N × N 矩阵表示的图像,其中每个像素的大小为 4 字节。请你设计一种算法,将图像旋转 90 度。
不占用额外内存空间能否做到?
示例:
给定 matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],
原地旋转输入矩阵,使其变为:
[
[7,4,1],
[8,5,2],
[9,6,3]
]
示例 2:
给定 matrix =
[
[ 5, 1, 9,11],
[ 2, 4, 8,10],
[13, 3, 6, 7],
[15,14,12,16]
],
原地旋转输入矩阵,使其变为:
[
[15,13, 2, 5],
[14, 3, 4, 1],
[12, 6, 8, 9],
[16, 7,10,11]
]
- [ 不占用额外空间的做法在做的时候没想到 ]
- [ 开辟一个新的box空间把目标复制下来 ]
- [ 把目标容器清空,然后再按照规律从box里取数据出来填上去 ]
void rotate(vector<vector<int>>& matrix) {
vector<vector<int>> box = matrix;
int boxsize = box.size();
matrix.clear();
for (int i = 0; i < boxsize; i++) {
matrix.emplace_back(0);
for (int j = 0; j < boxsize; j++) {
matrix[i].push_back(box[boxsize - j - 1][i]);
}
}
}
- [ 不占用额外空间的做法抄人家 ]
- [ 把矩阵以对角线为轴,交换 ]
- [ 然后再以Y轴中心为轴,交换,就得出结果了]
void rotate(vector<vector<int>>& matrix) {
int rows = matrix.size();
for(int i=0;i<rows;i++){
for(int j=i+1;j<rows;j++){//这里注意同元素不要直接^=操作,否则回不去,对角线都是0
matrix[i][j] ^= matrix[j][i];
matrix[j][i] ^= matrix[i][j];
matrix[i][j] ^= matrix[j][i];
}
}
for(int i=0;i<(rows/2);i++){
for(int j=0;j<rows;j++){
matrix[j][i] ^= matrix[j][rows - i - 1];
matrix[j][rows - i - 1] ^= matrix[j][i];
matrix[j][i] ^= matrix[j][rows - i - 1];
}
}
}
01.08. 零矩阵
编写一种算法,若M*N矩阵中某个元素为0,则将其所在行列都清零
示例:
输入:
[
[1,1,1],
[1,0,1],
[1,1,1]
]
输出:
[
[1,0,1],
[0,0,0],
[1,0,1]
]
输入:
[
[0,1,2,0],
[3,4,5,2],
[1,3,1,5]
]
输出:
[
[0,0,0,0],
[0,4,5,0],
[0,3,1,0]
]
- [ 遍历整个空间,找出元素0在数组矩阵里的x,y坐标值,分别放到两个set容器里 ]
- [ set容器基于红黑树实现,key值就是value值,且不能重复,可以达到去重的效果 ]
- [ 接着就是遍历set容器里的坐标数据,将目标矩阵的对应行列清空为0 ]
void setZeroes(vector<vector<int>>& matrix) {
set<int> x,y;
int boxsizey = matrix.size();
int boxsizex = matrix[0].size();
for(int i = 0;i < boxsizey;i++){
for(int j = 0;j < boxsizex;j++){
if(matrix[i][j] == 0){
x.emplace(i);
y.emplace(j);
}
}
}
if(!x.empty()){
for(int i:x){
for(int j = 0;j < boxsizex;j++)
matrix[i][j] = 0;
}
}
if(!y.empty()){
for(int i:y){
for(int j = 0;j < boxsizey;j++)
matrix[j][i] = 0;
}
}
}
498.矩阵对角线遍历
给定一个含有 M x N 个元素的矩阵(M行,N列),请以对角线遍历的顺序返回这个矩阵中的所有元素,对角线遍历如下图所示。
示例:
输入:
[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
]
输出: [1,2,4,7,5,3,6,8,9]
vector<int> findDiagonalOrder(vector<vector<int>>& matrix) {
vector<int> nums;//开辟空间存放结果
int m = matrix.size();
if (m == 0) return nums;
int n = matrix[0].size();
if (n == 0) return nums;//如果是空的直接返回空的数组
bool bXFlag = true;//设定一个到达上限需要逆转的标志,初始化为正向
for (int i = 0; i < m + n; i++)
{
int pm = bXFlag ? m : n;
int pn = bXFlag ? n : m;
//初始方向,pm是行数,pn是列数。逆转之后,则pm是列数,pn是行数
int x = (i < pm) ? i : pm - 1; //pm在逆转后行列就反过来了
int y = i - x;
//x+y = i 在每一趟对角线中,x,y的和相等的第一趟[0,0]=0,第二趟[0,1],[1,0]=1,就是等于i
//每一趟对角线,x,y的和都比前一趟大1,所以i++
//所以上面个两句是获得调转方向时的x,y坐标
while (x >= 0 && y < pn) //一次结束的条件是x或者y到达上限
{
nums.push_back(bXFlag ? matrix[x][y] : matrix[y][x]);//变换方向的具体实现bXFlag
x--;
y++; //因为y+x=i,所以只要保证x减1,y同时也要加1,直到有一方到达边界
}
bXFlag = !bXFlag;//那就要变换方向咯
}
return nums;
}
//转载自https://leetcode-cn.com/problems/diagonal-traverse/solution/dui-jiao-xian-bian-li-fen-xi-ti-mu-zhao-zhun-gui-l/
209.长度最小的数组
给定一个含有n个正整数的数组和一个正整数s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例:
输入:s = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
进阶:
- [ 如果你已经完成了 O(n) 时间复杂度的解法, 请尝试 O(n log n) 时间复杂度的解法。 ]
- [ 这个代码在leetcode通过不了,是因为我看到了“连续子数组”,认为是要相邻的元素组成的子数组 ]
- [ 而且以为元素和加起来要等于s,其实是大于等于s ]
- [ 虽然错了,但是也姑且记录下来吧(题目改成我理解的就可以用了) ]
int minSubArrayLen(int s, vector<int>& nums) {
int len = nums.size();
int res = 0;
for (int i = 0; i < len; ++i) {
auto start = nums.begin();
auto end = nums.begin() + i;
while (end != nums.end()) {
for (int j = 0; j < i+1; j++) {
res += *(start + j);
}
if (res == s) return (end - start + 1);
res = 0;
start++;
end++;
}
}
return 0;
}
- [ 双指针,快慢指针 ]
- [ 快指针优先走,当框住的元素的和大于目标值时,记录下此时的 ]
- [ 减去慢指针的值,并右移慢指针一位,直至框住的元素又小于目标值,再移动快指针 ]
- [ 当快指针溢出的时候结束循环 ]
int minSubArrayLen(int s, vector<int>& nums) {
int len = nums.size();
if (len == 0) {
return 0;
}
int res = INT_MAX;
int start = 0, end = 0;
int sum = 0;
while (end < len) {
sum += nums[end];
while (sum >= s) { //算是一个判断条件,当sum大于或等于s的时候
res = min(res, end - start + 1); //获得此时res的
sum -= nums[start]; //把sum的值减到小于s
start++; //减掉nums[start],那就把指针右移,前面的start已经不可能是成员了
}
end++;
}
return res == INT_MAX ? 0 : res;
}
118.杨辉三角
给定一个非负整数 numRows,生成杨辉三角的前 numRows 行
在杨辉三角中,每个数是它左上方和右上方的数的和。
示例:
输入: 5
输出:
[
[1],
[1,1],
[1,2,1],
[1,3,3,1],
[1,4,6,4,1]
]
- [ 在杨辉三角中,每个数是它左上方和右上方的数的和。 ]
- [ 所以只要排除数组的一头一尾两个数字为1的情况 ]
- [ 其余的空位的值都满足res[i-1][j-1]+res[i-1][j] ]
vector<vector<int>> generate(int numRows) {
vector<vector<int>> res;
if(numRows == 0) return res;
res.push_back({});
for(int i = 1;i < numRows+1;++i){
res.push_back({});
for(int j = 0;j < i;++j){
int nums = j == 0 ? 1 : j == i-1 ? 1 : res[i-1][j-1]+res[i-1][j];
res[i].push_back(nums);
}
}
res.erase(res.begin());
return res;
}
26.删除排序数组中的重复项
给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
示例:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。
给定 nums = [0,0,1,1,1,2,2,3,3,4],
函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
你不需要考虑数组中超出新长度后面的元素。
- [ 自己的做法不值一提,记录下人家牛逼的思路 ]
- [ 只需遍历一次原数组 ]
- [ 把后面搜索到的与当前位置元素不相等的元素填到下一位,看代码能懂,不知道怎么描述 ]
int removeDuplicates(vector<int>& nums) {
int len = nums.size();
if(len < 2) return len;
int j = 0;
for(int i = 1; i < len;++i)
if(nums[j]!=nums[i]) nums[++j]=nums[i];
return ++j;
}
347.前K个高频元素
定一个非空的整数数组,返回其中出现频率前 k 高的元素。
示例:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
输入: nums = [1], k = 1
输出: [1]
你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。
题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。
你可以按任意顺序返回答案
- [ 自己第一次想的思路 ]
- [ 把元素遍历一次放到unordered_set和unordered_multiset中 ]
- [ set不同元素只存放一个,mulitset相当于把数组的元素拷贝份过来 ]
- [ 接着遍历一次set,用find和count得到mulitset里每个元素重复的数据,放进unordered_map里 ]
- [ 创建一个vector
和make_pair函数得到pair对象,就是把unordered_map转化成vector,方便用sort ] - [ 写一个仿函数,作为sort的第三个参数,令sort遵循value由大到小排列的规则排序 ]
- [ 至此,题目需要k个元素,就只需要循环k次,把vector
里的数据放进vector 返回就行了 ]
class Solution {
public:
static bool big(const pair<int,int>& first, const pair<int,int>& last) {
return first.second > last.second;
}
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_multiset<int> box;
unordered_set<int> play;
unordered_map<int, int> numsbox;
vector<pair<int,int>> vec;
vector<int> res;
for (int i = 0, len = nums.size(); i < len; ++i) {
box.emplace(nums[i]);
play.emplace(nums[i]);
}
for (int i : play) {
auto it = box.find(i);
numsbox.emplace(i, box.count(*it));
}
for (auto itr = numsbox.begin(); itr != numsbox.end(); itr++) {
vec.push_back(make_pair(itr->first, itr->second));
}
sort(vec.begin(), vec.end(),big);
for (int i = 0; i < k; ++i) {
res.push_back(vec[i].first);
}
return res;
}
};
735.行星碰撞(外表是数组,其实是考察栈)
给定一个整数数组 asteroids,表示在同一行的行星。
对于数组中的每一个元素,其绝对值表示行星的大小,正负表示行星的移动方向(正表示向右移动,负表示向左移动)。每一颗行星以相同的速度移动。
找出碰撞后剩下的所有行星。碰撞规则:两个行星相互碰撞,较小的行星会爆炸。如果两颗行星大小相同,则两颗行星都会爆炸。两颗移动方向相同的行星,永远不会发生碰撞。
示例:
输入:
asteroids = [5, 10, -5]
输出: [5, 10]
解释:
10 和 -5 碰撞后只剩下 10。 5 和 10 永远不会发生碰撞。
输入:
asteroids = [8, -8]
输出: []
解释:
8 和 -8 碰撞后,两者都发生爆炸。
输入:
asteroids = [10, 2, -5]
输出: [10]
解释:
2 和 -5 发生碰撞后剩下 -5。10 和 -5 发生碰撞后剩下 10。
输入:
asteroids = [-2, -1, 1, 2]
输出: [-2, -1, 1, 2]
解释:
-2 和 -1 向左移动,而 1 和 2 向右移动。
由于移动方向相同的行星不会发生碰撞,所以最终没有行星发生碰撞。
说明:
数组 asteroids 的长度不超过 10000。
每一颗行星的大小都是非零整数,范围是 [-1000, 1000] 。
- [ 维护一个栈,先把数组的第一个元素push到栈顶 ]
- [ 然后开始遍历数组的每一个元素,让其和栈顶元素比较 ]
- [ 如果满足对撞条件,就判断哪个元素大,如果是栈顶元素大,那就不push数组元素到栈顶就好了 ]
- [ 如果是数组元素大于栈顶元素,就让栈顶元素出栈,让新的栈顶元素继续和数组元素比较 ]
- [ 循环至碰撞条件不满足或者栈空了 ]
- [ 遍历完之后栈里的元素就是碰撞完剩下的元素,让他们从栈顶出栈,push_back到存放结果的容器里 ]
- [ 这时候存放结果的容器是反的,因为栈先进后出。所以调用reverse反转容器的元素,return容器 ]
- [ 其实这题难是难在确定判断的边界问题。。 ]
vector<int> asteroidCollision(vector<int>& asteroids) {
if (asteroids.size() == 0) return asteroids;
stack<int> Stack;
vector<int> res;
Stack.push(asteroids[0]);
for (int i = 1, len = asteroids.size(); i < len; ++i) {
if (Stack.empty()) {
Stack.push(asteroids[i]);
continue;
}
int temp = Stack.top();
if (temp >= 0 && asteroids[i] < 0) {
while (!Stack.empty()&&temp*asteroids[i]<0) {
if (abs(temp) < abs(asteroids[i])) {
if (temp < 0) break;
Stack.pop();
if (!Stack.empty()) temp = Stack.top();
else temp = 0;
if(temp*asteroids[i]>=0) Stack.push(asteroids[i]);
}
else if (abs(temp) == abs(asteroids[i])) {
Stack.pop();
break;
}
else
break;
}
}
else
Stack.push(asteroids[i]);
}
while (!Stack.empty()) {
res.push_back(Stack.top());
Stack.pop();
}
reverse(res.begin(), res.end());
return res;
}