c++/go算法模板, 刷题记录

◉ 一、数组

912. 排序数组

给你一个整数数组 nums,请你将该数组升序排列。

示例 1:

输入:nums = [5,2,3,1]
输出:[1,2,3,5]
示例 2:

输入:nums = [5,1,1,2,0,0]
输出:[0,0,1,1,2,5]

提示:

1 <= nums.length <= 5 * 104
-5 * 104 <= nums[i] <= 5 * 104
题解

func Swap(x *int, y *int){
   
    temp := *x
    *x = *y
    *y = temp
}

func Partition(nums *[]int, i int,j int) int{
   
    idx := rand.Int()%(j-i+1)+i
    Swap(&(*nums)[i],&(*nums)[idx])
    x := (*nums)[i]
    for i<j{
   
        for i<j && (*nums)[j] >= x{
   
            j--
        }
        (*nums)[i] = (*nums)[j]
        for i<j && (*nums)[i]<=x {
   
            i++
        }
        (*nums)[j] = (*nums)[i]
    }
    (*nums)[i] = x
    return i 
}
func quickSort(nums *[]int, s int, e int){
   
    var m int
    if (s<e){
   
        m = Partition(nums,s,e)
        quickSort(nums,s,m-1)
        quickSort(nums,m+1,e)
    }
}
func sortArray(nums []int) []int {
   
    if len(nums) <=1 {
   
        return nums
    }
    rand.New(rand.NewSource(time.Now().UnixNano()))
    i,j := 0,len(nums)-1
    quickSort(&nums,i,j)
    return nums
    
}

1184. 公交站间的距离

难度:简单

环形公交路线上有 n 个站,按次序从 0n - 1 进行编号。我们已知每一对相邻公交站之间的距离,distance[i] 表示编号为 i 的车站和编号为 (i + 1) % n 的车站之间的距离。

环线上的公交车都可以按顺时针和逆时针的方向行驶。

返回乘客从出发点 start 到目的地 destination 之间的最短距离。

示例 1:

img

输入:distance = [1,2,3,4], start = 0, destination = 1
输出:1
解释:公交站 0 和 1 之间的距离是 1 或 9,最小值是 1。

示例 2:

img

输入:distance = [1,2,3,4], start = 0, destination = 2
输出:3
解释:公交站 0 和 2 之间的距离是 3 或 7,最小值是 3。

示例 3:

img

输入:distance = [1,2,3,4], start = 0, destination = 3
输出:4
解释:公交站 0 和 3 之间的距离是 6 或 4,最小值是 4。

提示:

  • 1 <= n <= 10^4
  • distance.length == n
  • 0 <= start, destination < n
  • 0 <= distance[i] <= 10^4

题解

计算start-end的加和,计算这个范围外的加和,输出较小者。

class Solution {
public:
    int distanceBetweenBusStops(vector<int>& nums, int start, int end) {
        int d1 = 0, d2 = 0;
        int l = min(start,end);
        int r = max(start,end);
        for(int i=0;i<nums.size();i++){
            if(i>=l && i<r){d1 += nums[i];}
            else{d2 += nums[i];}
        }
        return d1<d2?d1:d2;
    }
};

1539. 第 k 个缺失的正整数

难度:简单

给你一个 严格升序排列正整数数组 arr 和一个整数 k

请你找到这个数组里第 k 个缺失的正整数。

示例 1:

输入:arr = [2,3,4,7,11], k = 5
输出:9
解释:缺失的正整数包括 [1,5,6,8,9,10,12,13,...] 。第 5 个缺失的正整数为 9 。

示例 2:

输入:arr = [1,2,3,4], k = 2
输出:6
解释:缺失的正整数包括 [5,6,7,...] 。第 2 个缺失的正整数为 6 。

提示:

  • 1 <= arr.length <= 1000
  • 1 <= arr[i] <= 1000
  • 1 <= k <= 1000
  • 对于所有 1 <= i < j <= arr.lengthij 满足 arr[i] < arr[j]

题解

方法一:

相当于找空座位

  1. 正常排列时,缺失的正整数一定 >= k
  2. 数组中每出现一个 <= k 的数字, 意味着少了一个缺失的数字, 此时k+1,向后挪一个位置
 int findKthPositive(vector<int>& a, int k) {
     for(int i=0;i<a.size();++i){
             if(a[i]<=k)++k;
     }
     return k;
    }

方法二:二分法(需要找到各个元素与缺失元素个数的关系)

 int findKthPositive(vector<int>& arr, int k) {
        if (arr[0] > k) {
            return k;
        }

        int l = 0, r = arr.size();
        while (l < r) {
            int mid = l+((r-l) >> 1);
            int x = mid < arr.size() ? arr[mid] : INT_MAX;
            if (x - mid - 1 >= k) {
                r = mid;
            } else {
                l = mid + 1;
            }
        }

        return k - (arr[l - 1] - (l - 1) - 1) + arr[l - 1];
    }

41. 缺失的第一个正数

难度: 困难

给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。

请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。

示例 1:

输入:nums = [1,2,0]
输出:3

示例 2:

输入:nums = [3,4,-1,1]
输出:2

示例 3:

输入:nums = [7,8,9,11,12]
输出:1

提示:

  • 1 <= nums.length <= 5 * 105
  • -231 <= nums[i] <= 231 - 1

题解

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
      int n=nums.size();
      for(int i=0;i<n;++i){
          while(nums[i]>0&&nums[i]<n && nums[nums[i]-1]!=nums[i]){//将(0,n)的元素就位
             swap(nums[i],nums[nums[i]-1]);
        }
     }
     for(int i=0;i<n;++i){
       if(nums[i]!=i+1)return i+1;
     }
     return n+1;
    }
};

剑指 Offer 03. 数组中重复的数字

难度: 简单

找出数组中重复的数字。

在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

示例 1:

输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3 

限制:

2 <= n <= 100000

题解

  int findRepeatNumber(vector<int>& a) {
            if(a.size()<2)return -1;
            for(int i=0;i<a.size();i++){
                   if(a[i]!=i){//如果不相等
                        if(a[i]==a[a[i]]){ //如果相等,说明重复
                                return a[i];
                         }
                         swap(a[i],a[a[i]]);
                    }
            }
            return -1;
    }

136. 只出现一次的数字

难度: 简单

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,1]
输出: 1

示例 2:

输入: [4,1,2,1,2]
输出: 4

题解

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ans=0;
        for(const int&e:nums)ans ^=e;
        return ans ;
    }
};

283. 移动零

难度: 简单

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:

输入: [0,1,0,3,12]
输出: [1,3,12,0,0]

说明:

  1. 必须在原数组上操作,不能拷贝额外的数组。
  2. 尽量减少操作次数。

题解

一次切分

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
      if(nums.size()<2)return;
      int i=-1;      
      for(int j=0;j<nums.size();++j){
          if(nums[j]!=0) {//0在右边
            i++;
            swap(nums[i],nums[j]);  
          }  
      }
      return;
    }
};

//对比快排一次切分
int partition(vector<int>& nums,int start,int end){
    int x=nums[end];
    int i=start-1;
    for(int j=start;j<end;++j){
        if(nums[j]<= x){
            ++i;
            swap(nums[i],nums[j]);
        }
    }
    swap(nums[i+1],nums[end]);
    return i+1;
}


88. 合并两个有序数组

难度: 简单

给你两个有序整数数组 nums1nums2,请你将 nums2 合并到 nums1 中*,*使 nums1 成为一个有序数组。

初始化 nums1nums2 的元素数量分别为 mn 。你可以假设 nums1 的空间大小等于 m + n,这样它就有足够的空间保存来自 nums2 的元素。

示例 1:

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]

示例 2:

输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]

提示:

  • nums1.length == m + n
  • nums2.length == n
  • 0 <= m, n <= 200
  • 1 <= m + n <= 200
  • -109 <= nums1[i], nums2[i] <= 109

题解

逆向遍历,如果前一个数组为空,则直接放第二个数组的元素。

特殊示例:

(1)
[0,0,0]  [1,2,3]  //m:0  n:3

(2)
[1,2,3,0] [1] //m:4 n:1
class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
     int last = m+n-1;    //合并后元素的个数
     while(n){     //注意n
        if(m==0||nums1[m-1]<=nums2[n-1]){  //注意nums2
            nums1[last--] = nums2[--n];    
        }
        else{
            nums1[last--] = nums1[--m];    
        }     
     }  
    }
};

54. 螺旋矩阵

难度:中等

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

示例 1:

img

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]

示例 2:

img

输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]

提示:

  • m == matrix.length
  • n == matrix[i].length
  • 1 <= m, n <= 10
  • -100 <= matrix[i][j] <= 100

题解

 vector<int> spiralOrder(vector<vector<int>>& matrix) {
          if(matrix.empty())return {};
          vector<int>ans;
          int n=matrix[0].size();
          int m=matrix.size();
          int z=0,y=n-1,s=0,x=m-1; //左右上下
          int i=0;
          while(1){
                  //①从左到右
                  for(i=z;i<=y ;++i)ans.emplace_back(matrix[s][i]);
                  
                  //②从上到下
                   if(++s>x)break;           //判断是否越界
                  for(i=s; i<=x;++i)ans.emplace_back(matrix[i][y]);
                 
                  //③从右到左
                    if(--y<z)break;         //Judging whether it is out of bounds
                    for(i=y;i>=z;--i) ans.emplace_back(matrix[x][i]);
                  
                 //④从下往上
                   if(--x<s)       break; //judging whether it is out of bounds      
                   for(i=x;i>=s;--i)ans.emplace_back(matrix[i][z]);
                   
                   if(++z>y)break;  /*!!!*/
          }
          return ans;
    }

48. 旋转图像

难度:中等

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

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

示例 1:

img

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]

示例 2:

img

输入: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]]

示例 3:

输入:matrix = [[1]]
输出:[[1]]

示例 4:

输入:matrix = [[1,2],[3,4]]
输出:[[3,1],[4,2]]

提示:

  • matrix.length == n
  • matrix[i].length == n
  • 1 <= n <= 20
  • -1000 <= matrix[i][j] <= 1000

题解

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
      int n=matrix.size();
      for(int i=0;i<(n/2);++i)matrix[i].swap(matrix[n-i-1]) ;//先上下反转
      for(int i=0;i<n;++i){      //再转置
          for(int j=i;j<n;++j)
          swap(matrix[i][j],matrix[j][i]);    
      }
      return ;
    }
};

//旋转模拟
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 + 1) / 2; ++j) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[n - j - 1][i];
                matrix[n - j - 1][i] = matrix[n - i - 1][n - j - 1];
                matrix[n - i - 1][n - j - 1] = matrix[j][n - i - 1];
                matrix[j][n - i - 1] = temp;
            }
        }
    }
};

349. 两个数组的交集

难度:简单

给定两个数组,编写一个函数来计算它们的交集。

示例 1:

输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]

示例 2:

输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]

说明:

  • 输出结果中的每个元素一定是唯一的。
  • 我们可以不考虑输出结果的顺序。

题解

//调库函数:time: O(mlogm+nlogn)  space: O(logm+logn)
#define all(x) begin(x), end(x)  //!!!
class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        vector<int> ret;
        sort(all(nums1));
        sort(all(nums2));
        set_intersection(all(nums1), all(nums2), back_inserter(ret));
        ret.erase(unique(all(ret)), end(ret)); 
        return ret;
    }
};

//hash表法:time: O(n) space: O(n)
class Solution {
public:
        std::unordered_map<int,int> map;
        vector<int> ans;
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        
        for(int i = 0;i<nums1.size();i++){
            map[nums1[i]] = 1;
        }

        for(int i = 0;i<nums2.size();i++){
            if(map[nums2[i]] == 1){
                map[nums2[i]] = 0;
                ans.emplace_back(nums2[i]);
            }
        }
        return ans;
    }
};

189. 旋转数组

难度中等1002

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

进阶:

  • 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
  • 你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?

示例 1:

输入: nums = [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]

示例 2:

输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释: 
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]

提示:

  • 1 <= nums.length <= 2 * 104
  • -231 <= nums[i] <= 231 - 1
  • 0 <= k <= 105

题解

class Solution {
public:
    void rotate(vector<int>& a,  int k) {
        int n=a.size();
        k%=n;
        if (n<2||k==0)return;
        reverse(a.begin(),a.end());
        reverse(a.begin(),a.begin()+k);
        reverse(a.begin()+k,a.end());   
    } 
};

525. 连续数组

难度中等432

给定一个二进制数组 nums , 找到含有相同数量的 01 的最长连续子数组,并返回该子数组的长度。

示例 1:

输入: nums = [0,1]
输出: 2
说明: [0, 1] 是具有相同数量 0 和 1 的最长连续子数组。

示例 2:

输入: nums = [0,1,0]
输出: 2
说明: [0, 1] (或 [1, 0]) 是具有相同数量0和1的最长连续子数组。

提示:

  • 1 <= nums.length <= 105
  • nums[i] 不是 0 就是 1

题解

//前缀和问题,基础解法
class Solution {
public:
    int findMaxLength(vector<int>& nums) {
     int res=0,sum=0;
    
     unordered_map<int,int>umap; //key: prefix sum, v: index
     umap[0]=-1;
     for(int i=0;i<nums.size();++i){
      sum+=  (nums[i]==0?-1:1);
   
      if(umap.count(sum)){  //sum-0
              res=max(res,i-umap[sum]);
      }
      else{
              umap[sum]=i;
      }
     }
     return res;
    }
};

//优化hash表
class Solution {
public:
    int findMaxLength(vector<int>& nums) {
     int res=0,sum=0;
    const  int n=nums.size();
    vector<int>hash(2*n+1,-2);//存放下标,初始化为全-2
     hash[n]=-1;
     for(int i=0;i<n;++i){
      sum+=  (nums[i]==0?-1:1);
   
      if(hash[sum+n]!=-2){
              res=max(res,i-hash[n+sum]);
      }
      else{
              hash[n+sum]=i;
      }
     }
     return res;
    }
};

14. 最长公共前缀

难度简单1684收藏分享切换为英文接收动态反馈

编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 ""

示例 1:

输入:strs = ["flower","flow","flight"]
输出:"fl"

示例 2:

输入:strs = ["dog","racecar","car"]
输出:""
解释:输入不存在公共前缀。

提示:

  • 0 <= strs.length <= 200
  • 0 <= strs[i].length <= 200
  • strs[i] 仅由小写英文字母组成

题解

class Solution {
public:
    string longestCommonPrefix(vector<string>& strs) {
        if(strs.empty()) return string();
        sort(strs.begin(), strs.end());
        
        string start = strs.front(), end = strs.back();
        int i, num = min(start.size(), end.size());
        for(i = 0; i < num && start[i] == end[i]; i++);
        return start.substr(0,i);
    }
};

657. 机器人能否返回原点

难度简单204

在二维平面上,有一个机器人从原点 (0, 0) 开始。给出它的移动顺序,判断这个机器人在完成移动后是否在 (0, 0) 处结束

移动顺序由字符串表示。字符 move[i] 表示其第 i 次移动。机器人的有效动作有 R(右),L(左),U(上)和 D(下)。如果机器人在完成所有动作后返回原点,则返回 true。否则,返回 false。

**注意:**机器人“面朝”的方向无关紧要。 “R” 将始终使机器人向右移动一次,“L” 将始终向左移动等。此外,假设每次移动机器人的移动幅度相同。

示例 1:

输入: "UD"
输出: true
解释:机器人向上移动一次,然后向下移动一次。所有动作都具有相同的幅度,因此它最终回到它开始的原点。因此,我们返回 true。

示例 2:

输入: "LL"
输出: false
解释:机器人向左移动两次。它最终位于原点的左侧,距原点有两次 “移动” 的距离。我们返回 false,因为它在移动结束时没有返回原点。

题解

class Solution {
public:
    bool judgeCircle(string moves) {
        int x = 0, y = 0;
        for (int i = 0; i < moves.size(); i++) {
            if (moves[i] == 'U') y++;
            if (moves[i] == 'D') y--;
            if (moves[i] == 'L') x--;
            if (moves[i] == 'R') x++;
        }
        if (x == 0 && y == 0) return true;
        return false;
    }
};

15. 三数之和

难度中等3487

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 *a,b,c ,*使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

**注意:**答案中不可以包含重复的三元组。

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]

示例 2:

输入:nums = []
输出:[]

示例 3:

输入:nums = [0]
输出:[]

提示:

  • 0 <= nums.length <= 3000
  • -105 <= nums[i] <= 105

题解

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
    vector<vector<int>> ans;//首先创建一个存放一个符合题意的一个三数组
    
    int n=nums.size();      
    if(n<3){            //如果这个数组小于3三个元素 不可能满足
        return {};
    }
    sort(nums.begin(),nums.end());//对这个数组进行排序
    for(int i=0;i<n;i++){               //进入循环 开始 把i当作第一个数  
        if(nums[i]>0){return ans;}      //这是排序过的 如果第一个数大于0 那么后面的数没有负的 就不可能三个数相加等于0;
        if(i>0&&nums[i]==nums[i-1]){continue;}//如果这个数前面用过了 就跳过

        int l=i+1;//左指针
        int r=n-1;//右指针
        // 进入双指针法找到 i后面  两个数之和=-i的;
        while(l<r){
            //如果两数之和大 就要减小 右指针向左收缩
            if(nums[l]+nums[r]>-nums[i]){
                r--;
                
            }
                 //如果两数之和小 就要增加 左指针向右收缩
                else if(nums[l]+nums[r]<-nums[i]){
                l++;
               
            }
                //如果相等     
                else{
                ans.push_back(vector<int>{nums[i],nums[l],nums[r]});//将符合的 三个坐标插入 我们的答案二维数组
                //然后收缩指针 看看 之间还有没有 符合的
                l++;
                r--;
                    
                //在找到相等的情况下,有数字重复就跳过
                while(l<r&&nums[r]==nums[r+1]){ r--; }
                while(l<r&&nums[l]==nums[l-1]){ l++; }
            }
        }
    }
    return ans;
    }
};

16. 最接近的三数之和

难度中等820

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

示例:

输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。

提示:

  • 3 <= nums.length <= 10^3
  • -10^3 <= nums[i] <= 10^3
  • -10^4 <= target <= 10^4

题解

class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
     int n=nums.size();
     sort(nums.begin(),nums.end());
     int l,r,sum,ans=nums[0]+nums[1]+nums[2];
     for(int i=0;i<nums.size()-2;++i){   
        if(i>0 &&nums[i-1] == nums[i]) continue;
        l = i+1; 
        r = n-1;
        while(l<r){
           sum=nums[i]+nums[l]+nums[r] ;   
           if(sum == target) return target;
           if(abs(sum-target)<abs(ans-target)) ans=sum;  
           if(sum>target)r--;
           else l++;
        }  
     }
     return ans;
    }
};

304. 二维区域和检索 - 矩阵不可变

难度中等

给定一个二维矩阵,计算其子矩形范围内元素的总和,该子矩阵的左上角为 (row1, col1) ,右下角为 (row2, col2)

Range Sum Query 2D
上图子矩阵左上角 (row1, col1) = (2, 1) ,右下角(row2, col2) = **(4, 3),**该子矩形内元素的总和为 8。

示例:

给定 matrix = [
  [3, 0, 1, 4, 2],
  [5, 6, 3, 2, 1],
  [1, 2, 0, 1, 5],
  [4, 1, 0, 1, 7],
  [1, 0, 3, 0, 5]
]

sumRegion(2, 1, 4, 3) -> 8
sumRegion(1, 1, 2, 2) -> 11
sumRegion(1, 2, 2, 4) -> 12

提示:

  • 你可以假设矩阵不可变。
  • 会多次调用 sumRegion 方法*。*
  • 你可以假设 row1 ≤ row2col1 ≤ col2

题解

using namespace std;
#include <vector>

class NumMatrix {
private:
    vector<vector<int> > sums;
public:
    NumMatrix(vector<vector<int> >& matrix) {
        int n = matrix.size();
        int m = matrix[0].size();
        if (n == 0 || m == 0) {
            return;
        }

        sums.resize(n + 1, vector<int>(m + 1, 0));
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                sums[i][j] = matrix[i - 1][j- 1] + sums[i - 1][j] + sums[i][j - 1] - sums[i - 1][j - 1];
            }
        }

        return;
    }
    
    int sumRegion(int row1, int col1, int row2, int col2) {
        return sums[row2 + 1][col2 + 1] - sums[row2 + 1][col1] - sums[row1][col2 + 1] + sums[row1][col1];
    }
};

/**
 * Your NumMatrix object will be instantiated and called as such:
 * NumMatrix* obj = new NumMatrix(matrix);
 * int param_1 = obj->sumRegion(row1,col1,row2,col2);
 */

413. 等差数列划分

难度中等250

如果一个数列至少有三个元素,并且任意两个相邻元素之差相同,则称该数列为等差数列。

例如,以下数列为等差数列:

1, 3, 5, 7, 9
7, 7, 7, 7
3, -1, -5, -9

以下数列不是等差数列。

1, 1, 2, 5, 7

数组 A 包含 N 个数,且索引从0开始。数组 A 的一个子数组划分为数组 (P, Q),P 与 Q 是整数且满足 0<=P<Q<N 。

如果满足以下条件,则称子数组(P, Q)为等差数组:

元素 A[P], A[p + 1], …, A[Q - 1], A[Q] 是等差的。并且 P + 1 < Q 。

函数要返回数组 A 中所有为等差数组的子数组个数。

示例:

A = [1, 2, 3, 4]

返回: 3, A 中有三个子等差数组: [1, 2, 3], [2, 3, 4] 以及自身 [1, 2, 3, 4]。

题解

//dp
class Solution {
public:
    int numberOfArithmeticSlices(vector<int>& nums) {
       int n=nums.size();
       if(n<3) return 0; 
       int sum=0;
       vector<int> dp(n,0);  
       for(int i=2;i<n;++i){
         if(nums[i]-nums[i-1]==nums[i-1]-nums[i-2]) {
           dp[i]=dp[i-1]+1;
           sum+=dp[i];  //前缀和  
         }  
       }
       return sum;
    }
};

//优化dp
class Solution {
public:
    int numberOfArithmeticSlices(vector<int>& nums) {
       int n=nums.size();
       if(n<3) return 0; 
       int sum=0;
       int pre=0; 
       for(int i=2;i<n;++i){
         if(nums[i]-nums[i-1]==nums[i-1]-nums[i-2]) {
           pre=pre+1;
           sum+=pre;    
         }
         else pre=0;  
       }
       return sum;
    }
};

31. 下一个排列

难度: 中等

实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。

如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。

必须** 原地 **修改,只允许使用额外常数空间。

示例 1:

输入:nums = [1,2,3]
输出:[1,3,2]

示例 2:

输入:nums = [3,2,1]
输出:[1,2,3]

示例 3:

输入:nums = [1,1,5]
输出:[1,5,1]

示例 4:

输入:nums = [1]
输出:[1]

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 100

题解

逆向遍历找不符合 nums[i+1]<=nums[i] 的第一个不符合的index,然后逆序遍历找到不符合 nums[i]>=nums[j] 的index,交换 nums[i]和nums[j] ,反转 [begin+i+1,end) 的元素。

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
      int i = nums.size() - 2;
        while (i >= 0 && nums[i] >= nums[i + 1]) {  
            i--;
        }
        if (i >= 0) {
            int j = nums.size() - 1;       
            while (j >= 0 && nums[i] >= nums[j]) {
                j--;
            }
            swap(nums[i], nums[j]);
        }
        reverse(nums.begin() + i + 1, nums.end());
    }
};

498. 对角线遍历

难度中等219

给定一个含有 M x N 个元素的矩阵(M 行,N 列),请以对角线遍历的顺序返回这个矩阵中的所有元素,对角线遍历如下图所示。

示例:

输入:
[
 [ 1, 2, 3 ],
 [ 4, 5, 6 ],
 [ 7, 8, 9 ]
]

输出:  [1,2,4,7,5,3,6,8,9]

解释:

说明:

  1. 给定矩阵中的元素总数不会超过 100000 。

题解

fx=1,代表方向为斜上↗;

fx=-1,代表方向为斜下↙;

class Solution {
public:
    vector<int> findDiagonalOrder(vector<vector<int>>& mat) {
      int m = mat.size();
      if(!m)return {};
      int n = mat[0].size();
      vector<int> ans(m*n,0);
      int cur = 0,i=0,j=0;
      int fx=1;
      for(;cur<m*n;++cur){
        ans[cur]=mat[i][j];
        if(j==n-1&&fx==1){  //遇到右边,则换向为斜下和移动起始点(向下)
            fx=-1;
            i=i+1;
            continue;    
        }
        if(i==m-1&&fx==-1){ //遇到下边,则换向为斜上和移动起始点(向右)
            fx=1;
            j=j+1;  
           continue;    
        }
        if(i==0&&fx==1){ //遇到上边,则换向为斜下和移动起始点(向右)   
            fx=-1;
            j = j+1; 
            continue;     
        }
        if(j==0&&fx ==-1){ //遇到左边,则换向为斜上和移动起始点(向下)
            fx=1;
            i=i+1;  
            continue;    
        }
        i=i-fx;    //往斜上走,则分解为x方向向上,y方向向右
        j=j+fx;
      }
      return ans;
    }
};



443. 压缩字符串

难度中等258

给你一个字符数组 chars ,请使用下述算法压缩:

从一个空字符串 s 开始。对于 chars 中的每组 连续重复字符

  • 如果这一组长度为 1 ,则将字符追加到 s 中。
  • 否则,需要向 s 追加字符,后跟这一组的长度。

压缩后得到的字符串 s 不应该直接返回 ,需要转储到字符数组 chars中。需要注意的是,如果组长度为 1010 以上,则在 chars 数组中会被拆分为多个字符。

请在 修改完输入数组后 ,返回该数组的新长度。

你必须设计并实现一个只使用常量额外空间的算法来解决此问题。

示例 1:

输入:chars = ["a","a","b","b","c","c","c"]
输出:返回 6 ,输入数组的前 6 个字符应该是:["a","2","b","2","c","3"]
解释:
"aa" 被 "a2" 替代。"bb" 被 "b2" 替代。"ccc" 被 "c3" 替代。

示例 2:

输入:chars = ["a"]
输出:返回 1 ,输入数组的前 1 个字符应该是:["a"]
解释:
没有任何字符串被替代。

示例 3:

输入:chars = ["a","b","b","b","b","b","b","b","b","b","b","b","b"]
输出:返回 4 ,输入数组的前 4 个字符应该是:["a","b","1","2"]。
解释:
由于字符 "a" 不重复,所以不会被压缩。"bbbbbbbbbbbb" 被 “b12” 替代。
注意每个数字在数组中都有它自己的位置。

提示:

  • 1 <= chars.length <= 2000
  • chars[i] 可以是小写英文字母、大写英文字母、数字或符号

题解

class Solution {
public:
    int compress(vector<char>& chars) {
        int len = 0;
        for (int i = 0, cnt = 1; i < chars.size(); i++, cnt++) {
            if (i + 1 == chars.size() || chars[i] != chars[i + 1]) {
                chars[len++] = chars[i];
                if (cnt > 1) {
                    for (char ch : to_string(cnt)) {
                        chars[len++] = ch;
                    }
                }
                cnt = 0;
            }
        }
        return len;
    }
};

◉ 二、摩尔投票法

1. 多数元素

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

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例 1:

输入:[3,2,3]
输出:3

示例 2:

输入:[2,2,1,1,1,2,2]
输出:2

题解

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int count = 0, res = 0;
        for (int &item : nums) {
            if (count == 0) res = item, count++;//统计为0,换主元素
            else if (res != item) count--;//不相同则斗争,相抵消
            else if (res == item) count++;//相同,为一伙的
        }
        return res;
    }
};

2. 求众数 II

难度中等362收藏分享切换为英文接收动态反馈

给定一个大小为 n 的整数数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。

**进阶:**尝试设计时间复杂度为 O(n)、空间复杂度为 O(1)的算法解决此问题。

示例 1:

输入:[3,2,3]
输出:[3]

示例 2:

输入:nums = [1]
输出:[1]

示例 3:

输入:[1,1,1,3,3,2,2,2]
输出:[1,2]

题解

//摩尔投票法

class Solution {
public:
    vector<int> majorityElement(vector<int>& nums) {

        int candicate1 = nums[0];
        int candicate2 = nums[0];
        int count1 = 0;
        int count2 = 0;


        for(int& num :nums){
            if(candicate1==num){
                count1++;
                continue;
            }

            if(candicate2==num){
                count2++;
                continue;
            }


            if(count1==0){
                candicate1=num;
                count1++;
                continue;
            }

            if(count2==0){
                candicate2=num;
                count2++;
                continue;
            }

            count1--;
            count2--;
        }


        count1=0;
        count2=0;
        //可能有一个,可能有两个,可能一个,可能没有
        for(int& num: nums){
            if(num==candicate1){
                count1++;
            }else if(num==candicate2){
                count2++;
            }
        }

        vector<int> res;
        if(count1>nums.size()/3){
            res.push_back(candicate1);
        }
        if(count2>nums.size()/3){
            res.push_back(candicate2);
        }

        return res;
    }
};

◉ 三、哈希表

220. 存在重复元素 III

给你一个整数数组 nums 和两个整数 kt 。请你判断是否存在 两个不同下标 ij,使得 abs(nums[i] - nums[j]) <= t ,同时又满足 abs(i - j) <= k

如果存在则返回 true,不存在返回 false

示例 1:

输入:nums = [1,2,3,1], k = 3, t = 0
输出:true

示例 2:

输入:nums = [1,0,1,1], k = 1, t = 2
输出:true

示例 3:

输入:nums = [1,5,9,1,5,9], k = 2, t = 3
输出:false

题解

/*在默认的情况下cin绑定的是cout,每次执行 << 操作符的时候都要调用flush,这样会增加IO负担。可以通过tie(0)(0表示NULL)来解除cin与cout的绑定,进一步加快执行效率。*/

static int x=[](){
 ios::sync_with_stdio(false);
 cin.tie(NULL);
 return 0;
}();

class Solution {
public:
    long getID(long x, long t){
        //如果x元素大于等于零,直接分桶
        if(x>=0){
            return x / ( t + 1 );
        }else{
        //如果x元素小于零,偏移后再分桶
            return ( x + 1 )/( t + 1 ) - 1 ;
        }
        return 0;
    }
    bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
        int n = nums.size();
        //我们用unordered_map来实现桶,其底层实现是一个哈希表.
        unordered_map<int,int> m;
        for(int i = 0 ; i < n; i++ ){
            //当前元素
            long  x = nums[i];
            //给当前元素生成id,这里我们同一个id的桶内元素满足abs(nums[i] - nums[j]) <= t
            int id = getID(x,t);
            //前面的i-(k+1)是超出了范围的桶,我们把它提前删除,以免影响判断
            if( i-(k+1) >= 0 ){
                //把下标不满足我们判断范围的桶删除了
                m.erase(getID(nums[i-(k+1)],t));
            }
            //看看当前元素属于的桶中有没有元素
            if(m.find(id)!=m.end()){
                return true;
            }
            //看看前面相邻桶有没有符合条件的
            if(m.find(id - 1) != m.end() && abs(m[id-1]-x) <= t){
                return true;
            }
            //看看后面相邻桶有没有符合条件的
            if(m.find(id + 1) != m.end() && abs(m[id+1]-x) <= t){
                return true;
            }
            //分桶,把这个元素放入其属于的桶
            m[id] = x;
        }
        return false;
    }
};

*128. 最长连续序列

难度中等839收藏分享切换为英文接收动态反馈

给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

请你设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例 1:

输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。

示例 2:

输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9

提示:

  • 0 <= nums.length <= 105
  • -109 <= nums[i] <= 109

题解

class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
       if(nums.size()<2)return nums.size();     
       int ans=0;
       int temp=0;
       unordered_set<int>m;
       for(const int&e:nums) m.insert(e);   
       for(int e:nums){
         if(!m.count(e-1)){
           temp=1;
           while(m.count(e+1)){
              e++;
              temp++;   
           } 
         }
         ans = max(ans,temp);      
       } 
       return ans;
    }
};

3. 无重复字符的最长子串

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

示例 1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

示例 4:

输入: s = ""
输出: 0

题解

 int lengthOfLongestSubstring(const string& s) {
    if(s.size()==0)return 0;
    if(s.size()==1) return 1;
    bitset<128>map;
     int ans=1;
     map[s[0]]=1;
    int i=0,j=0;
   while(j<s.size()){  //map只记录滑动窗口的元素,移出滑动窗口后,从删除对应的元素
       ans=max(ans,j-i+1);
       ++j;
       while(0!=map[s[j]]){//重复了
               map[s[i]]=0;//向前移动i
               ++i;
       }
        map[s[j]]=1;   
        }
     return ans;
    }

930. 和相同的二元子数组

难度: 中等

给你一个二元数组 nums ,和一个整数 goal ,请你统计并返回有多少个和为 goal非空 子数组。

子数组 是数组的一段连续部分。

示例 1:

输入:nums = [1,0,1,0,1], goal = 2
输出:4
解释:
如下面黑体所示,有 4 个满足题目要求的子数组:
[1,0,1,0,1]
[1,0,1,0,1]
[1,0,1,0,1]
[1,0,1,0,1]

示例 2:

输入:nums = [0,0,0,0,0], goal = 0
输出:15

提示:

  • 1 <= nums.length <= 3 * 104
  • nums[i] 不是 0 就是 1
  • 0 <= goal <= nums.length

题解

//前缀和
class Solution {
public:
    int numSubarraysWithSum(vector<int>& nums, int goal) {
      unordered_map<int,int> m;  //保存前缀和
      m[0]=1;
      int sum=0;
      long long ans=0;
      for(int i=0;i<nums.size();++i){
              sum+=nums[i];
              ans+=m[sum-goal];
              m[sum]++;
      }
      return ans;
    }
};

13. 罗马数字转整数

难度:简单

罗马数字包含以下七种字符: IVXLCDM

字符          数值
I             1
V             5
X             10
L             50
C             100
D             500
M             1000

例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

  • I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
  • X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
  • C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。

给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。

示例 1:

输入: "III"
输出: 3

示例 2:

输入: "IV"
输出: 4

示例 3:

输入: "IX"
输出: 9

示例 4:

输入: "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.

示例 5:

输入: "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.

提示:

  • 1 <= s.length <= 15
  • s 仅含字符 ('I', 'V', 'X', 'L', 'C', 'D', 'M')
  • 题目数据保证 s 是一个有效的罗马数字,且表示整数在范围 [1, 3999]
  • 题目所给测试用例皆符合罗马数字书写规则,不会出现跨位等情况。
  • IL 和 IM 这样的例子并不符合题目要求,49 应该写作 XLIX,999 应该写作 CMXCIX 。
  • 关于罗马数字的详尽书写规则,可以参考 罗马数字 - Mathematics

题解

class Solution {
public:
    int romanToInt(string s) {
     int i = 0, n = 0;
     int len = s.size();
     int hash[26] = {0};
     hash['I'-'A'] = 1;
     hash['V'-'A'] = 5;
     hash['X'-'A'] = 10;
     hash['L'-'A'] = 50;
     hash['C'-'A'] = 100;
     hash['D'-'A'] = 500;
     hash['M'-'A'] = 1000;
     while (i < len)
     {
        if (i + 1 < len && hash[s[i]-'A'] < hash[s[i + 1]-'A'])
        {
            n = n + hash[s[i + 1]-'A'] - hash[s[i]-'A'];
            i = i + 2;
        }
        else
        {
            n = n + hash[s[i]-'A'];
            i++;   
        }
     }
     return n;
    }
};

1248. 统计「优美子数组」

难度中等175收藏分享切换为英文接收动态反馈

给你一个整数数组 nums 和一个整数 k

如果某个 连续 子数组中恰好有 k 个奇数数字,我们就认为这个子数组是「优美子数组」。

请返回这个数组中「优美子数组」的数目。

示例 1:

输入:nums = [1,1,2,1,1], k = 3
输出:2
解释:包含 3 个奇数的子数组是 [1,1,2,1] 和 [1,2,1,1] 。

示例 2:

输入:nums = [2,4,6], k = 1
输出:0
解释:数列中不包含任何奇数,所以不存在优美子数组。

示例 3:

输入:nums = [2,2,2,1,2,2,1,2,2,2], k = 2
输出:16

提示:

  • 1 <= nums.length <= 50000
  • 1 <= nums[i] <= 10^5
  • 1 <= k <= nums.length

题解

前缀和

static int x=[](){
        ios::sync_with_stdio(0);
        cin.tie(0);
        return 0;
}();

class Solution {
public:
    int numberOfSubarrays(vector<int>& nums, int k) {
     unordered_map<int,int>m; 
     m[0]=1;        //key!!!
     int ans=0;
     int  sum=0;
     for(int i=0;i<nums.size();++i){
             sum+=(nums[i]&1)?1:0;
             if(m.count(sum-k)) ans+=m[sum-k];
             m[sum]++;
     }
     return ans;
    }
};

724. 寻找数组的中心下标

难度:简单

给你一个整数数组 nums ,请计算数组的 中心下标

数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。

如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。

如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1

示例 1:

输入:nums = [1, 7, 3, 6, 5, 6]
输出:3
解释:
中心下标是 3 。
左侧数之和 sum = nums[0] + nums[1] + nums[2] = 1 + 7 + 3 = 11 ,
右侧数之和 sum = nums[4] + nums[5] = 5 + 6 = 11 ,二者相等。

示例 2:

输入:nums = [1, 2, 3]
输出:-1
解释:
数组中不存在满足此条件的中心下标。

示例 3:

输入:nums = [2, 1, -1]
输出:0
解释:
中心下标是 0 。
左侧数之和 sum = 0 ,(下标 0 左侧不存在元素),
右侧数之和 sum = nums[1] + nums[2] = 1 + -1 = 0 。

提示:

  • 1 <= nums.length <= 104
  • -1000 <= nums[i] <= 1000

题解

//前缀和
class Solution {
public:
    int pivotIndex(vector<int>& nums) {
    int n = nums.size();
    vector<int>s1(n+2,0) ;  //前缀和s1
    vector<int>s2(n+2,0) ;  //前缀和s2
    for (int i = 1; i <= n; i++) s1[i] = s1[i - 1] + nums[i - 1];
    for (int i = n; i >= 1; i--) s2[i] = s2[i + 1] + nums[i - 1];
    for (int i = 1; i <= n; i++) {
            if (s1[i] == s2[i]) return i - 1;
        }
   return -1;
    }
};

//优化前缀和
class Solution {
public:
    int pivotIndex(vector<int>& nums) {
        int n = nums.size();
        int total = 0, sum = 0;
    
        for (int i = 0; i < n; i++) total += nums[i];
        for (int i = 0; i < n; i++) {
            if (sum == total - sum - nums[i]) return i;
            sum += nums[i];
        }
        return -1;
    }
};

1109. 航班预订统计

难度中等153收藏分享切换为英文接收动态反馈

这里有 n 个航班,它们分别从 1n 进行编号。

有一份航班预订表 bookings ,表中第 i 条预订记录 bookings[i] = [firsti, lasti, seatsi] 意味着在从 firstilasti包含 firstilasti )的 每个航班 上预订了 seatsi 个座位。

请你返回一个长度为 n 的数组 answer,其中 answer[i] 是航班 i 上预订的座位总数。

示例 1:

输入:bookings = [[1,2,10],[2,3,20],[2,5,25]], n = 5
输出:[10,55,45,25,25]
解释:
航班编号        1   2   3   4   5
预订记录 1 :   10  10
预订记录 2 :       20  20
预订记录 3 :       25  25  25  25
总座位数:      10  55  45  25  25
因此,answer = [10,55,45,25,25]

示例 2:

输入:bookings = [[1,2,10],[2,2,15]], n = 2
输出:[10,25]
解释:
航班编号        1   2
预订记录 1 :   10  10
预订记录 2 :       15
总座位数:      10  25
因此,answer = [10,25]

提示:

  • 1 <= n <= 2 * 104
  • 1 <= bookings.length <= 2 * 104
  • bookings[i].length == 3
  • 1 <= firsti <= lasti <= n
  • 1 <= seatsi <= 104

题解

class Solution {
public:
    vector<int> corpFlightBookings(vector<vector<int>>& bookings, int n) {
        vector<int> presum(n,0);
        for(auto &x:bookings){      //差分法
            presum[x[0]-1]+=x[2];  //上飞机,注意编号是从1到n,所以要-1;
            if(x[1]-1<n-1){
            presum[x[1]+1-1]-=x[2];  //下飞机,但是到终点站下的不用减
            }
        }

        for(int i =1;i<n;i++){          
            presum[i]+=presum[i-1];   //前缀和得到各站的人数
        }
        return presum;
    }
};

525. 连续数组

难度中等438

给定一个二进制数组 nums , 找到含有相同数量的 01 的最长连续子数组,并返回该子数组的长度。

示例 1:

输入: nums = [0,1]
输出: 2
说明: [0, 1] 是具有相同数量 0 和 1 的最长连续子数组。

示例 2:

输入: nums = [0,1,0]
输出: 2
说明: [0, 1] (或 [1, 0]) 是具有相同数量0和1的最长连续子数组。

提示:

  • 1 <= nums.length <= 105
  • nums[i] 不是 0 就是 1

题解

前缀和三部曲:

  1. hash保存的下标还是数值。(下标对应子数组长度,数值对应和为k的个数)
  2. hash[0]的含义。统计个数时,hash[0]代表sumk时,个数记为1。计算数组长度时,hash[0]代表sumk时,索引的位置。
class Solution {
public:
    int findMaxLength(vector<int>& nums) {
     int res=0,sum=0;
    
     unordered_map<int,int>umap;
     umap[0]=-1;   //考虑sum==k的情况
     for(int i=0;i<nums.size();++i){
      sum+=  (nums[i]==0?-1:1);
   
      if(umap.count(sum-0)){   //target=0
              res=max(res,i-umap[sum]);
      }
      else{
              umap[sum]=i;
      }
     }
     return res;
    }
};

325.和等于 k 的最长子数组长度

题目描述:
给定一个数组 nums 和一个目标值 k,找到和等于 k 的最长子数组长度。如果不存在任意一个符合要求的子数组,则返回 0。

注意:
nums 数组的总和是一定在 32 位有符号整数范围之内的。

示例 1:

输入: nums = [1, -1, 5, -2, 3], k = 3
输出: 4 
解释: 子数组 [1, -1, 5, -2] 和等于 3,且长度最长。
示例 2:

输入: nums = [-2, -1, 2, 1], k = 1
输出: 2 
解释: 子数组 [-1, 2] 和等于 1,且长度最长。

题解

//
class Solution {
public:
    int maxSubArrayLen(vector<int>& nums, int k) {
        
        //利用前缀和数组 快速计算子数组的和
        //而又因为求的是最长子数组等于k,所以我们在哈希表中保存的是前缀和为x第一次出现的下标
        //然后我们求以每个元素为结尾的,最长 和等于k的子数组长度
        
        //值为x的前缀和最早出现的下标y  hash【x】=y
        unordered_map<int,int> hash;
        // 这个初始化很重要,为了考虑到 0~i等于k的情况
        hash[0]=-1;
        int sum=0;
        int res=0;
        for(int i=0;i<nums.size();++i){
            sum += nums[i];
            if(hash.count(sum-k))  res= max(res,i-hash[sum-k]);
            if(!hash.count(sum)) hash[sum]=i; // 因为要的是第一次出现的值
        }
        
        return res;
        
    }
};

1. 两数之和

难度:简单

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

提示:

  • 2 <= nums.length <= 104
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109
  • 只会存在一个有效答案

题解

class Solution {
public:
    vector<int> twoSum(vector<int>& nums,const int& target) {
      unordered_map<int,int> m;
      unordered_map<int,int> ::iterator it;
      for(int i=0;i<nums.size();++i){
           it = m.find(target-nums[i]);
          if(it!=m.end()){return vector<int>({i,it->second}); }  
          m[nums[i]]=i;  
      }
      return vector<int>();
    }
};

12. 整数转罗马数字

难度中等674

罗马数字包含以下七种字符: IVXLCDM

字符          数值
I             1
V             5
X             10
L             50
C             100
D             500
M             1000

例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

  • I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
  • X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
  • C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。

给你一个整数,将其转为罗马数字。

示例 1:

输入: num = 3
输出: "III"

示例 2:

输入: num = 4
输出: "IV"

示例 3:

输入: num = 9
输出: "IX"

示例 4:

输入: num = 58
输出: "LVIII"
解释: L = 50, V = 5, III = 3.

示例 5:

输入: num = 1994
输出: "MCMXCIV"
解释: M = 1000, CM = 900, XC = 90, IV = 4.

提示:

  • 1 <= num <= 3999

题解

class Solution {
public:
    string intToRoman(int num) {
        int values[]={1000,900,500,400,100,90,50,40,10,9,5,4,1};
        string reps[]={"M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"};
        
        string res;
        for(int i=0; i<13; i++){
            while(num>=values[i]){
                num -= values[i];
                res += reps[i];
            }
        }
        return res;
    }
};

◉ 四、链表

链表定义

struct ListNode {
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};

剑指 Offer 24. 反转链表

定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

限制:

0 <= 节点个数 <= 5000

题解

//迭代法
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
     if(!head||!head->next)return head;
     ListNode * newh=nullptr;
     ListNode * p=head;
     while(head){
       p= head->next;
       head->next = newh;
       newh = head;
       head =p;
     }
     return newh;
    }
};

//递归法
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (head == NULL || head->next == NULL) {
            return head;
        }
        ListNode* ret = reverseList(head->next);
        head->next->next = head;
        head-&g
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值