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;
}
posted @ 2020-09-02 08:36  JoyooO  阅读(280)  评论(0编辑  收藏  举报