手撕力扣之数组题:旋转图像、螺旋矩阵I II、加一、多数元素、旋转数组、除自身以外数组的乘积、 找到所有数组中消失的数字、和为K的子数组、把数组排成最小的数、扑克牌中的顺子、递增的三元子序列

力扣048. 旋转图像
给定一个 n × n 的二维矩阵表示一个图像。
将图像顺时针旋转 90 度。
说明:
你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。
手撕力扣之数组题:旋转图像、螺旋矩阵I II、加一、多数元素、旋转数组、除自身以外数组的乘积、 找到所有数组中消失的数字、和为K的子数组、把数组排成最小的数、扑克牌中的顺子、递增的三元子序列_第1张图片

class Solution {
     
public:
    void rotate(vector<vector<int>>& matrix) {
     
        int len = matrix.size();
        int mid = (len-1)/2;
        //flip over x = len/2
        for(int i=0; i<len; i++){
     
            for(int j=0; j<=mid; j++){
     
                swap(matrix[i][j],matrix[i][len-1-j]);
            }
        }
        //flip over diag
        for(int i=0; i<len; i++){
     
            for(int j=0; j<len-1-i; j++){
     
                swap(matrix[i][j],matrix[len-1-j][len-1-i]);
            }
        }
    }
};

作者:edward_wang
链接:https://leetcode-cn.com/problems/rotate-image/solution/shu-zu-xia-biao-suo-yin-wen-ti-by-edward_wang/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

力扣054. 螺旋矩阵
1.首先设定上下左右边界
2.其次向右移动到最右,此时第一行因为已经使用过了,可以将其从图中删去,体现在代码中就是重新定义上边界
3.判断若重新定义后,上下边界交错,表明螺旋矩阵遍历结束,跳出循环,返回答案
4.若上下边界不交错,则遍历还未结束,接着向下向左向上移动,操作过程与第一,二步同理
5.不断循环以上步骤,直到某两条边界交错,跳出循环,返回答案

class Solution {
     
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
     
        vector <int> ans;
        if(matrix.empty()) return ans; //若数组为空,直接返回答案
        int u = 0; //赋值上下左右边界
        int d = matrix.size() - 1;
        int l = 0;
        int r = matrix[0].size() - 1;
        while(true)
        {
     
            for(int i = l; i <= r; ++i) ans.push_back(matrix[u][i]); //向右移动直到最右
            if(++ u > d) break; //重新设定上边界,若上边界大于下边界,则遍历遍历完成,下同
            for(int i = u; i <= d; ++i) ans.push_back(matrix[i][r]); //向下
            if(-- r < l) break; //重新设定有边界
            for(int i = r; i >= l; --i) ans.push_back(matrix[d][i]); //向左
            if(-- d < u) break; //重新设定下边界
            for(int i = d; i >= u; --i) ans.push_back(matrix[i][l]); //向上
            if(++ l > r) break; //重新设定左边界
        }
        return ans;
    }
};


作者:youlookdeliciousc
链接:https://leetcode-cn.com/problems/spiral-matrix/solution/cxiang-xi-ti-jie-by-youlookdeliciousc-3/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

力扣059. 螺旋矩阵 II
给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。

class Solution {
     
public:
    vector<vector<int>> generateMatrix(int n) {
     
        vector<vector<int>> res(n,vector<int>(n));
        int up=0;int down=n-1;
        int left=0;int right=n-1;
        int num=1;
        while(true)
        {
     
            for(int i=left;i<=right;i++)  res[up][i]=num++;
            if(++up>down)  break;
            for(int i=up;i<=down;i++)  res[i][right]=num++;
            if(--right<left)  break;
            for(int i=right;i>=left;i--)  res[down][i]=num++;
            if(--down<up)  break;
            for(int i=down;i>=up;i--)  res[i][left]=num++;
            if(++left>right)  break;
        }
        return res;
    }
};

力扣066. 加一
给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位,数组中每个元素只存储单个数字。
你可以假设除了整数0之外,这个整数不会以零开头。

class Solution {
     
public:
    vector<int> plusOne(vector<int>& digits) {
     
        int length=digits.size();
        digits[length-1]+=1;
        for(int i=length-1;i>0;i--)
        {
     
            if(digits[i]==10)
            {
     
                digits[i]=0;
                digits[i-1]+=1;
            }
        }
        if(digits[0]==10)
        {
     
            digits[0]=0;
            digits.insert(digits.begin(),1);
        }
        return digits;
    }
};

力扣169. 多数元素
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。

class Solution {
     
public:
    int majorityElement(vector<int>& nums) {
     
//摩尔投票法,先假设第一个数过半数并设cnt=1;遍历后面的数如果相同则cnt+1,不同则减一,当cnt为0时则更换新的数字为候选数(成立前提:有出现次数大于n/2的数存在)
        int res=0,cnt=0;
        for(int i=0;i<nums.size();i++){
     
            if(cnt==0) {
     
                res=nums[i];
                cnt++;
            }
            else{
     
                res==nums[i]?cnt++:cnt--;
            }
        }
        return res;
    }
};

力扣189. 旋转数组
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。

思路:此题可以采用头插法,一个一个的移动。但是有种更加简单的选择数组的方式。我们可以采用翻转的方式,比如12345经过翻转就变成了54321,这样已经做到了把前面的数字放到后面去,但是还没有完全达到我们的要求,比如,我们只需要把12放在后面去,目标数组就是34512,与54321对比发现我们就只需要在把分界线前后数组再进行翻转一次就可得到目标数组了。所以此题只需要采取三次翻转的方式就可以得到目标数组,首先翻转分界线前后数组,再整体翻转一次即可。此题面试常考,大家可以记一下此方法。

class Solution {
     
public:
    void reverse(vector<int>& nums,int begin,int end)
    {
     
        int temp;
        while(begin<end){
     
            temp = nums[begin];
            nums[begin] = nums[end];
            nums[end] = temp;
            begin++;
            end--;
        }
    }
    void rotate(vector<int>& nums, int k) {
     
        if(nums.size()<2) return;
        k %=nums.size();
        reverse(nums,0,nums.size()-1);
        reverse(nums,0,k-1);
        reverse(nums,k,nums.size()-1);
    }
};

力扣238. 除自身以外数组的乘积
给你一个长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。
思路:左右累乘,巧妙记录每个元素的左右乘积,时间复杂度O(n),空间复杂度0(1)。

class Solution {
     
public:
    vector<int> productExceptSelf(vector<int>& nums) {
     
        
        int n=nums.size();
        int left=1,right=1;     //left:从左边累乘,right:从右边累乘
        vector<int> res(n,1);
        
        for(int i=0;i<n;++i)    //最终每个元素其左右乘积进行相乘得出结果
        {
     
            res[i]*=left;       //乘以其左边的乘积
            left*=nums[i];
            
            res[n-1-i]*=right;  //乘以其右边的乘积
            right*=nums[n-1-i];
        }
        
        return res;
        
    }
};

力扣448. 找到所有数组中消失的数字
给定一个范围在 1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。
找到所有在 [1, n] 范围之间没有出现在数组中的数字。
您能在不使用额外空间且时间复杂度为O(n)的情况下完成这个任务吗? 你可以假定返回的数组不算在额外空间内。
【笔记】将所有正数作为数组下标,置对应数组值为负值。那么,仍为正数的位置即为(未出现过)消失的数字。
举个例子:
原始数组:[4,3,2,7,8,2,3,1]
重置后为:[-4,-3,-2,-7,8,2,-3,-1]
结论:[8,2] 分别对应的index为[5,6](消失的数字)

vector<int> findDisappearedNumbers(vector<int>& nums) {
     
    for (int i = 0; i < nums.size(); ++i)
        nums[abs(nums[i])-1] = -abs(nums[abs(nums[i])-1]);
    vector<int> res;
    for (int i = 0; i < nums.size(); ++i){
     
        if (nums[i] > 0)
            res.push_back(i+1);
    }
    return res;
}

力扣560. 和为K的子数组
给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。
解题思路
假如存在区间[left,right],使得在[left,right]这个区间的子数组的和为k。换句话说,就是前right项和减去前left-1项和等于k,即前left-1项和等于前right项和减去k。
可以这样做,在扫描数组的同时,假设当前扫到第i位,记录它的前i项和sum,用该和减去k,即sum-k,判断sum-k是否为某个位置的前n项和,若是,更新统计量。

class Solution {
     
public:
    int subarraySum(vector<int>& nums, int k) {
     
        int sum = 0, ans = 0;
        unordered_map<int,int> mp;
        mp[0] = 1;
        for(int i: nums){
     
            sum += i;
            if(mp.find(sum-k) != mp.end()) ans += mp[sum-k];
            mp[sum] ++;
        }
        return ans;
    }
};

作者:jarvis1890
链接:https://leetcode-cn.com/problems/subarray-sum-equals-k/solution/qian-zhui-he-shi-xian-jian-dan-by-jarvis1890/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

力扣剑指 Offer 45. 把数组排成最小的数
输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。
说明:
输出结果可能非常大,所以你需要返回一个字符串而不是整数
拼接起来的数字可能会有前导 0,最后结果不需要去掉前导 0

class Solution {
     
public:
    string minNumber(vector<int>& nums) {
     
        vector<string> strs;
        string res;
        for(auto num:nums)
            strs.push_back(to_string(num));
        sort(strs.begin(),strs.end(),compare);
        for(auto str:strs)
            res+=str;
        return res;
    }
private:
    static bool compare(const string &a,const string &b)
    {
     
        return a+b<b+a;
    }
};

力扣剑指 Offer 61. 扑克牌中的顺子
从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。
思路:
不排序:
同样,如果我们能够知道 5 张扑克牌中的最大值 maxValuemaxValue 和最小值 minValueminValue ,那我们就知道,要使它为顺子需要 maxValue - minValue + 1张牌。
在查找 maxValuemaxValue 和 minValueminValue 过程中,跳过大小王0 。
如果 maxValue - minValue + 1 > ,5maxValue−minValue+1>5说明题目给的 5 张牌不足以构成顺子,返回 false .
即使里面有大小王,也不够用来填补使它构成顺子。
如果 maxValue - minValue + 1 <= 5 ,maxValue−minValue+1<=5,说明 5 张牌足以构成顺子,返回 true。
里面的大小王填补在合适位置即可。
同时,我们再定义一个标志数组判断是否有重复数字,发现重复数字直接返回 falsefalse 即可。

class Solution {
     
public:
    bool isStraight(vector<int>& nums) {
     
        vector<bool> sig(14,false);
        int maxNum=0;int minNum=14;
        for(auto num:nums)
        {
     
            if(num==0)  continue;
            if(sig[num])  return false;
            sig[num]=true;
            maxNum=max(maxNum,num);
            minNum=min(minNum,num);
        }
        return maxNum-minNum+1<=5;
    }
};

排序:判断相邻两张牌之间需要多少个大王或小王来填补

class Solution {
     
public:
    bool isStraight(vector<int>& nums) {
     
        sort(nums.begin(), nums.end());
        int zero = 0;
        for (int i = 0; i < 4; i ++) {
     
            if (nums[i] == 0) {
     
                zero++;
                continue;
            } 
            if (nums[i] == nums[i+1]) {
     
                return false;
            }
            zero -= nums[i+1]-nums[i] - 1;
        }
        return zero >= 0;
    }
};

作者:huwt
链接:https://leetcode-cn.com/problems/bu-ke-pai-zhong-de-shun-zi-lcof/solution/bu-ke-pai-zhong-de-shun-zi-pai-xu-fang-shi-he-bu-p/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

力扣334. 递增的三元子序列
给定一个未排序的数组,判断这个数组中是否存在长度为 3 的递增子序列。
数学表达式如下:
如果存在这样的 i, j, k, 且满足 0 ≤ i < j < k ≤ n-1,
使得 arr[i] < arr[j] < arr[k] ,返回 true ; 否则返回 false 。
说明: 要求算法的时间复杂度为 O(n),空间复杂
思路:
首先,新建两个变量 small 和 mid ,分别用来保存题目要我们求的长度为 3 的递增子序列的最小值和中间值。
接着,我们遍历数组,每遇到一个数字,我们将它和 small 和 mid 相比,若小于等于 small ,则替换 small;否则,若小于等于 mid,则替换 mid;否则,若大于 mid,则说明我们找到了长度为 3 的递增数组!

class Solution {
     
public:
  bool increasingTriplet(vector<int>& nums) {
     
    int len = nums.size();
    if (len < 3) return false;
    int small = INT_MAX, mid = INT_MAX;
    for (auto num : nums) {
     
      if (num <= small) {
     
        small = num;
      } else if (num <= mid) {
     
        mid = num;
      } 
      else if (num > mid) {
     
        return true;
      }
    }
    return false;    
  }
};

作者:fxxuuu
链接:https://leetcode-cn.com/problems/increasing-triplet-subsequence/solution/c-xian-xing-shi-jian-fu-za-du-xiang-xi-jie-xi-da-b/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的:(数据结构与算法,leetcode)