2023-2024下半学期刷题记录

本文介绍了几个编程问题,涉及动态规划解决最大子数组和、合并区间、寻找无重复字符子串、旋转数组操作、异位词查找、矩阵置零、螺旋矩阵遍历、图像旋转以及动态规划解决打家劫舍问题,还提到了如何使用滑动窗口和哈希优化解决方案。
摘要由CSDN通过智能技术生成

三天打鱼两天晒网的做题记录,目前在做热题100

3.10 

53.最大子数组和(动态规划)

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组

是数组中的一个连续部分。

动态规划,具体见注释

动态规划要素:1.最优子结构 2.边界(dp[0]) 3.状态转移公式(dp[i]=?)

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
    //动态规划:dp[i]表示什么,怎么由dp[i-1]求dp[i],dp[0]等于几,最后需要得到什么
    //dp[i]表示以nums[i]结尾的连续子数组的最大和
    //dp[i]=max{nums[i],dp[i−1]+nums[i]}
    //dp[0]=nums[0]
    //计算所有dp取最大值
    int len=nums.size();
    int dp[len];
    int maxsum=nums[0];
    dp[0]=nums[0];
    for(int i=1;i<len;i++)
    {
        dp[i]=max(dp[i-1]+nums[i],nums[i]);
        if(dp[i]>maxsum) maxsum=dp[i];
    }
    return maxsum;


    }
};

56.合并区间

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。

 解法不难,先排序,再合并即可

class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        //根据左边界大小排序
        sort(intervals.begin(),intervals.end());
        vector<vector<int>> ans;

        for(int i=0;i<intervals.size();i++)
        {
            //当前区间的左右边界
            int left=intervals[i][0];
            int right=intervals[i][1];
            //右边界大于下一区间的左边界
            while(i+1<intervals.size()&&right>=intervals[i+1][0])
            {
                right=max(right,intervals[i+1][1]);
                i++;
            }
            ans.push_back({left,right});
        }
        return ans;
    }
};

3.无重复字符的最长子串(滑动窗口)

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串的长度

 类似双指针,右指针的下一个有重复的就改左指针

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int len=s.length();
        int ans=1;
        int left=0,right=0;
        while(right<len-1){
            right++;
            for(int i=left;i<right;i++)
            {
                if(s[right]==s[i])
                {
                    left=i+1;
                }
            }
            if(right-left+1>ans)ans=right-left+1;
        }
        if(s=="")ans=0;
        return ans;
    }
};

3.11

189.轮转数组

 给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

想法一是双指针直接改原数组;二是创建一个新数组(比较简单,但是原题估计不是想让答题者这样做的) 

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int len=nums.size();
        vector<int> ans;
        k=len-k%len;//%len以防转了好几圈
        for(int i=0;i<len;i++){
            if(k==len)k=0;
            ans.push_back(nums[k]);//不能直接ans[i]=
            k++;
        }
        nums=ans;

    }
};

238.除自身以外数组的乘积(前缀和)

给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。

题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在  32 位 整数范围内。

请 不要使用除法,且在 O(n) 时间复杂度内完成此题。

 看了一眼题解:当前数的答案=其左边所有数的乘积*右边所有数的乘积

等于求两侧的前缀和

class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        int len=nums.size();
        //分别求从左边开始和从右边开始的前缀和
        //当前数的ans=他左边的乘积*右边的乘积
        int left[len],right[len];
        //left[n]中的n是第n个数的左边乘积
        left[0]=1;
        right[len-1]=1;
        for(int i=1;i<len;i++)
        {
            left[i]=left[i-1]*nums[i-1];
            right[len-i-1]=right[len-i]*nums[len-i];
        }
        vector<int> ans;
        for(int i=0;i<len;i++){
            ans.push_back(left[i]*right[i]);
        }
        return ans;
    }
};

3.13

483.找到字符串中所有字母异位词(滑动窗口)

给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。

 每次滑动都会因前一个和新加的一个数改变

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        vector<int> ans;
        int len1=s.length();
        int len2=p.length();
        if(len2>len1)return ans;
        //对比两个字符串是否是异位的方法:用一个26位数组记录各个字母的数量
        //第一个子串单独算,后面的只需要减去前一个,加上后一个即可
        vector<int> mpp(26,0);
        vector<int> mps(26,0);
        for(int i=0;i<len2;i++){
            mpp[p[i]-'a']++;
            mps[s[i]-'a']++;
        }
        if(mpp==mps) {ans.push_back(0);}
        for(int i=len2;i<len1;i++){
            mps[s[i-len2]-'a']--;
            mps[s[i]-'a']++;
            if(mpp==mps) {ans.push_back(i-len2+1);} 
        }
        return ans;

    }
};

3.25

73.矩阵置零

给定一个 m x n 的矩阵,如果一个元素为 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法

笨蛋算法:用俩哈希存下来有0的行和列,再遍历置零;

加强算法:用第一行和第一列记录该行、列有没有0(要用单独的flag记录第一行第一列的0们) 

class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        int m=matrix.size();
        int n=matrix[0].size();
        unordered_set<int> hanghash;
        unordered_set<int> liehash;
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(matrix[i][j]==0){
                    hanghash.insert(i);
                    liehash.insert(j);
                }
            }
        }
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(hanghash.count(i)||liehash.count(j)){
                    matrix[i][j]=0;
                }
            }
        }
    }
};

54.螺旋矩阵 

给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

题解算法:用四个数来记录上下左右边界,逐渐将边界往里缩,当边界冲突时结束;一轮一轮进行,每轮都是按照 往右,往下,往左,往上的顺序

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        vector<int> ans;
        int u=0;
        int d=matrix.size()-1;
        int l=0;
        int r=matrix[0].size()-1;
        //设置上下左右边界
        while(1){
            //按照:往右,往下,往左,往上的顺序
            for(int i=l;i<=r;i++) ans.push_back(matrix[u][i]);
            if(++u>d) break;
            for(int j=u;j<=d;j++) ans.push_back(matrix[j][r]);
            if(--r<l) break;
            for(int i=r;i>=l;i--) ans.push_back(matrix[d][i]);
            if(--d<u) break;
            for(int j=d;j>=u;j--) ans.push_back(matrix[j][l]);
            if(++l>r) break;
        }
        return ans;

    }
};

 48.旋转图像

给定一个 × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。

你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。

题解算法一号:找规律,四个变换一组,不影响其他位置

二号:更奇妙的规律,直接翻转两次就好了 

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int n = matrix.size();
        //先将矩阵上下翻转。再按对角线翻转
        for(int i=0;i<n/2;i++){
            for(int j=0;j<n;j++){
                swap(matrix[i][j],matrix[n-i-1][j]);
            }
        }
        for(int i=0;i<n;i++){
            for(int j=0;j<i;j++){
                swap(matrix[i][j],matrix[j][i]);
            }
        }

    }
};

4.3

树:递归递归递归

先序遍历:根 左 右

中序遍历:左 根 右

后序遍历:左 右 根

以后要用js刷题咧

198.打家劫舍(动态规划)

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

/**
 * @param {number[]} nums
 * @return {number}
 */
var rob = function(nums) {
//状态转移:dp[i]=max(dp[i-1],dp[i-2]+nums[i])
  var dp=nums;
  if(nums.length==0)return 0;
  if(nums.length==1)return nums[0];
  if(nums.length==2)return Math.max(nums[0],nums[1]);
  dp[1]=Math.max(nums[0],nums[1]);
  for(var i=2;i<nums.length;i++){
    dp[i]=Math.max(dp[i-1],dp[i-2]+nums[i]);
  }
  return dp[nums.length-1];

};

4.9

560.和为k的子数组

给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 

子数组是数组中元素的连续非空序列。

本来想用双指针,想岔了

瞟了一眼题解说用前缀和,但是双层循环超时,看了题解用的哈希 

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number}
 */
var subarraySum = function(nums, k) {
    //单纯前缀和+双循环会超时,只能用哈希
    var len=nums.length;
    var ans=0;
    var sum=0;
    let map=new Map();
    map[0]=1;
    for(let i=0;i<len;i++){
    sum+=nums[i];
    if(map[sum-k]) {//用sum-k才能找前缀和间的差
        ans+=map[sum-k];
    }
    if(map[sum]){
        map[sum]++;
    }else{
        map[sum]=1; 
    }
    }
    return ans;
};

102.二叉树的层序遍历

 js中用数组存(栈),先把头节点放进去,而后循环:把当前数组中第一个节点取出来,把它的左右子节点push到数组中,并删除该节点,直到数组中没有节点

var levelOrder = function(root) {  
    let que = [];  
    let result = [];
    if (root) que.push(root);  
      
    while(que.length > 0) {  
        let levelSize = que.length;  
        let currentLevel = []; // 存储当前层的节点值  
          
        for(let i = 0; i < levelSize; i++){  
            let node = que.shift(); // 使用shift从队列开头取出节点,shift同时删除该节点  
            currentLevel.push(node.val); 
              
            if(node.left) que.push(node.left);  
            if(node.right) que.push(node.right);  
        }  
          
        result.push(currentLevel); // 将当前层的节点值数组添加到结果数组中  
    }  
      
    return result; // 返回层序遍历的结果  
};

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值