Leetcode Hot100

    技术2023-05-04  69

    Leetcode Hot100

    31. 下一个排列24. 两两交换链表中的节点Z字形变换1. 两数之和12. 整数转罗马数字15. 三数之和18. 四数之和39. 组合总和34. 在排序数组中查找元素的第一个和最后一个位置55. 跳跃游戏33. 搜索旋转排序数组56. 合并区间48. 旋转图像78. 子集93. 复原IP地址43. 字符串相乘64. 最小路径和114. 二叉树展开为链表49. 字母异位词分组22. 括号生成200. 岛屿数量75. 颜色分类221. 最大正方形76. 最小覆盖子串402. 移掉K位数字

    31. 下一个排列

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

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

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

    以下是一些例子,输入位于左侧列,其相应输出位于右侧列。 1,2,3 → 1,3,2 3,2,1 → 1,2,3 1,1,5 → 1,5,1 思路:参考

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

    24. 两两交换链表中的节点

    给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。 思路:参考

    /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* swapPairs(ListNode* head) { ListNode *dummy=new ListNode(0); //针对只有[1]的情况做的下面的处理 dummy->next=head; ListNode *first; ListNode *second; ListNode *prevNode=dummy; while(head!=NULL && head->next!=NULL){ first=head; second=head->next; prevNode->next=second; first->next=second->next; second->next=first; prevNode=first; head=first->next; } return dummy->next; } };

    Z字形变换

    将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。 比如输入字符串为 “LEETCODEISHIRING” 行数为 3 时,排列如下: L C I R E T O E S I I G E D H N 之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:“LCIRETOESIIGEDHN” 思路:参考

    class Solution { public: string convert(string s, int numRows) { if(numRows<2)return s; string res; vector<string>temp(numRows,""); int i=0,flag=-1; for(auto x:s){ temp[i]+=x; if(i==numRows-1||i==0)flag=-flag; i+=flag; } for(int i=0;i<temp.size();i++){ res+=temp[i]; } return res; } };

    1. 两数之和

    给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。 思路:参考

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

    12. 整数转罗马数字

    罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。 字符 数值 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 的范围内。 思路:参考

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

    15. 三数之和

    给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。 注意:答案中不可以包含重复的三元组。 思路:参考

    class Solution { public: vector<vector<int>> threeSum(vector<int>& nums) { vector<vector<int>>res; sort(nums.begin(),nums.end()); int n=nums.size(); for(int first=0;first<n;first++){ if(first>0&& nums[first]==nums[first-1])continue; int third=n-1; for(int second=first+1;second<n;second++){ if(second>first+1 && nums[second]==nums[second-1])continue; while(second<third && nums[second]+nums[third]+nums[first]>0){ third--; } if(second==third)break; if(nums[first]+nums[third]+nums[second]==0){ res.push_back({nums[first],nums[second],nums[third]}); } } } return res; } };

    18. 四数之和

    给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。 思路:参考3数之和

    class Solution { public: vector<vector<int>> fourSum(vector<int>& nums, int target) { int n=nums.size(); vector<vector<int>>res; sort(nums.begin(),nums.end()); for(int first=0;first<n;first++){ if(first>0&&nums[first]==nums[first-1])continue; for(int second=first+1;second<n;second++){ if(second>first+1 && nums[second]==nums[second-1])continue; int forth=n-1; for(int third=second+1;third<n;third++){ if(third>second+1 &&nums[third]==nums[third-1])continue; while(third<forth && nums[first]+nums[second]+nums[third]+nums[forth]>target){ forth--; } if(third==forth)break; if(nums[first]+nums[second]+nums[third]+nums[forth]==target){ res.push_back({nums[first],nums[second],nums[third],nums[forth]}); } } } } return res; } };

    39. 组合总和

    给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的数字可以无限制重复被选取。 说明: 所有数字(包括 target)都是正整数。 解集不能包含重复的组合。 思路:参考

    class Solution { public: void DFS(int start ,int target){ if(target==0){ res.push_back(path); return; } for(int i=start;i<candidates.size();i++){ if(target-candidates[i]>=0){ path.push_back(candidates[i]); DFS(i,target-candidates[i]); path.pop_back(); } } } vector<vector<int>> combinationSum(vector<int>& candidates, int target) { sort(candidates.begin(),candidates.end()); this->candidates=candidates; DFS(0,target); return res; } private: vector<vector<int>>res; vector<int>candidates; vector<int>path; };

    34. 在排序数组中查找元素的第一个和最后一个位置

    给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。 你的算法时间复杂度必须是 O(log n) 级别。 如果数组中不存在目标值,返回 [-1, -1]。 思路:二分法

    class Solution { public: vector<int> searchRange(vector<int>& nums, int target) { int left=0; int right=nums.size()-1; while(left<=right){ int mid=(left+right)/2; if(nums[mid]==target){ left=mid; right=mid; while(left>=0 &&nums[left]==target)left--; while(right<=nums.size()-1 && nums[right]==target)right++; return {left+1,right-1}; } else if(nums[mid]<target){ left=mid+1; } else{ right=mid-1; } } return {-1,-1}; } };

    55. 跳跃游戏

    给定一个非负整数数组,你最初位于数组的第一个位置。 数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个位置。 思路:参考

    class Solution { public: bool canJump(vector<int>& nums) { int n=nums.size(); int rightmost=0; for(int i=0;i<n;i++){ if(i<=rightmost){ rightmost=max(rightmost,i+nums[i]); if(rightmost>=n-1){ return true; } } } return false; } };

    33. 搜索旋转排序数组

    假设按照升序排序的数组在预先未知的某个点上进行了旋转。 ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。 你可以假设数组中不存在重复的元素。 你的算法时间复杂度必须是 O(log n) 级别。 思路:二分法

    class Solution { public: int search(vector<int>& nums, int target) { int n=nums.size(); if(!n)return -1; if(n==1)return nums[0]==target ? 0:-1; int l=0; int r=n-1; while(l<=r){ int mid=(l+r)/2; if (nums[mid]==target)return mid; if(nums[l]<=nums[mid]){ if(nums[l]<=target && target<nums[mid] ){ r=mid-1; } else{ l=mid+1; } } else{ if(nums[mid]<target && target<=nums[n-1] ){ l=mid+1; } else{ r=mid-1; } } } return -1; } };

    56. 合并区间

    给出一个区间的集合,请合并所有重叠的区间。 示例 1: 输入: [[1,3],[2,6],[8,10],[15,18]] 输出: [[1,6],[8,10],[15,18]] 解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6]. 思路:参考

    class Solution { public: vector<vector<int>> merge(vector<vector<int>>& intervals) { if(intervals.size()==0)return {}; sort(intervals.begin(),intervals.end()); for(int i=0;i<intervals.size();i++){ int L=intervals[i][0],R=intervals[i][1]; if(res.size()==0||res.back()[1]<L){ res.push_back({L,R}); } else{ res.back()[1]=max(res.back()[1],R); } } return res; } private: vector<vector<int>>res; };

    48. 旋转图像

    给定一个 n × n 的二维矩阵表示一个图像。 将图像顺时针旋转 90 度。 说明: 你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。 思路:参考

    class Solution { public: void rotate(vector<vector<int>> & matrix) { int n = matrix.size(); for(int i=0;i<n;i++){ for(int j=i;j<n;j++){ int tmp=matrix[i][j]; matrix[i][j]=matrix[j][i]; matrix[j][i]=tmp; } } for(int i=0;i<n;i++){ for(int j=0;j<n/2;j++){ int tmp=matrix[i][j]; matrix[i][j]=matrix[i][n-1-j]; matrix[i][n-1-j]=tmp; } } } };

    78. 子集

    给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。 说明:解集不能包含重复的子集。 思路:参考

    class Solution { public: void backtrack(vector<int> & nums,int pos){ res.push_back(cur); for(int i=pos;i<nums.size();i++){ cur.push_back(nums[i]); backtrack(nums,i+1); cur.pop_back(); } } vector<vector<int>> subsets(vector<int>& nums) { backtrack(nums,0); return res; } private: vector<vector<int>>res; vector<int>cur; };

    93. 复原IP地址

    给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。 有效的 IP 地址正好由四个整数(每个整数位于 0 到 255 之间组成),整数之间用 ‘.’ 分隔。 思路:参考

    class Solution { public: bool isValid(const string ip){ int val=stoi(ip); if(val>255)return false; if(ip.size()>=2 &&ip[0]=='0')return false; return true; } void backtrack(const string &s,int pos,vector<string> &path){ int maxLen=(4-path.size())*3; if(s.size()-pos>maxLen)return; if(path.size()==4 && pos==s.size()){ string ans=""; for(int i=0;i<4;i++){ ans+=path[i]; if(i!=3) ans+="."; } res.push_back(ans); return; } for (int i=pos;i<s.size()&& i<=pos+2;i++){ string ip=s.substr(pos,i-pos+1); if(!isValid(ip))continue; path.push_back(ip); backtrack(s,i+1,path); path.pop_back(); } } vector<string> restoreIpAddresses(string s) { vector<string>path; backtrack(s,0,path); return res; } private: vector<string>res; };

    43. 字符串相乘

    给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。 思路:参考

    class Solution { public: string multiply(string num1, string num2) { int n1=num1.size(); int n2=num2.size(); string res(n1+n2,'0'); for(int i=n2-1;i>=0;i--){ for(int j=n1-1;j>=0;j--){ int temp=(res[i+j+1]-'0')+(num2[i]-'0')*(num1[j]-'0'); res[i+j+1]=temp%10+'0'; res[i+j]+=temp/10; } } for(int i=0;i<n1+n2;i++){ if(res[i]!='0') return res.substr(i); } return "0"; } };

    64. 最小路径和

    给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 说明:每次只能向下或者向右移动一步。 思路:动态规划

    class Solution { public: int minPathSum(vector<vector<int>>& grid) { int row =grid.size(); int colum=grid[0].size(); vector<vector<int>> dp(row,vector<int>(colum,0)); dp[0][0]=grid[0][0]; for(int i=1;i<row;i++){ dp[i][0]=dp[i-1][0]+grid[i][0]; } for(int i=1;i<colum;i++){ dp[0][i]=dp[0][i-1]+grid[0][i]; } for(int i=1;i<row;i++){ for (int j=1;j<colum;j++){ dp[i][j]=min(dp[i-1][j],dp[i][j-1])+grid[i][j]; } } return dp[row-1][colum-1]; } };

    114. 二叉树展开为链表

    给定一个二叉树,原地将它展开为一个单链表。 思路:参考解法一

    /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */ class Solution { public: void flatten(TreeNode* root) { while(root!=NULL){ if(root->left==NULL){ root=root->right; } else{ TreeNode *pre=root->left; while(pre->right!=NULL){ pre=pre->right; } pre->right=root->right; root->right=root->left; root->left=NULL; root=root->right; } } } };

    49. 字母异位词分组

    给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。 思路:参考

    class Solution { public: vector<vector<string>> groupAnagrams(vector<string>& strs) { vector<vector<string>>res; unordered_map<string ,vector<string>> mymap; for (auto s:strs){ string temp=s; sort(temp.begin(),temp.end()); mymap[temp].push_back(s); } for(auto p=mymap.begin();p!=mymap.end();p++){ res.push_back(p->second); } return res; } };

    22. 括号生成

    数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。 思路:参考

    class Solution { public: void backtrack(string path,int n,int left,int right){ if(left==n &&right==n){ res.push_back(path); return; } if(left>n ||right>n|| right>left )return; backtrack(path+'(',n,left+1,right); backtrack(path+')',n,left,right+1); } vector<string> generateParenthesis(int n) { string path=""; backtrack(path,n,0,0); return res; } private: vector<string>res; };

    200. 岛屿数量

    给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。 岛屿总是被水包围,并且每座岛屿只能由水平方向或竖直方向上相邻的陆地连接形成。 此外,你可以假设该网格的四条边均被水包围。 思路:参考

    class Solution { public: void dfs(vector<vector<char>> & grid,int init_x,int init_y ){ grid[init_x][init_y]='0'; if(init_x>=1 && grid[init_x-1][init_y]=='1')dfs(grid,init_x-1,init_y); if( init_x+1<grid.size() && grid[init_x+1][init_y]=='1')dfs(grid,init_x+1,init_y); if(init_y>=1 && grid[init_x][init_y-1]=='1')dfs(grid,init_x,init_y-1); if(init_y+1<grid[0].size() && grid[init_x][init_y+1]=='1')dfs(grid,init_x,init_y+1); } int numIslands(vector<vector<char>>& grid) { if(grid.size()==0) return 0; int row=grid.size(); int col=grid[0].size(); int res=0; for(int i=0;i<row;i++){ for(int j=0;j<col;j++){ if(grid[i][j]=='1'){ res++; dfs(grid,i,j); } } } return res; } };

    75. 颜色分类

    给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。 此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 注意: 不能使用代码库中的排序函数来解决这道题。 思路:参考

    class Solution { public: void sortColors(vector<int>& nums) { int p0=0; int p2=nums.size()-1; int curr=p0; while(curr<=p2){ if(nums[curr]==0){ swap(nums[curr++],nums[p0++]); } else if(nums[curr]==2){ swap(nums[curr],nums[p2--]); } else curr++; } } };

    221. 最大正方形

    在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积。 示例: 输入: 1 0 1 0 0 1 0 1 1 1 1 1 1 1 1 1 0 0 1 0 输出: 4

    class Solution { public: int maximalSquare(vector<vector<char>>& matrix) { if(matrix.size()==0|| matrix[0].size()==0)return 0; int col=matrix[0].size(); int row=matrix.size(); vector<vector<int>>dp(row,vector<int>(col,0)); int res=0; for(int i=0;i<row;i++){ for(int j=0;j<col;j++){ if(matrix[i][j]=='1'){ if(i==0||j==0){ dp[i][j]=1; } else{ dp[i][j]=min(min(dp[i-1][j],dp[i-1][j-1]),dp[i][j-1])+1; } res=max(dp[i][j],res); } } } return res*res; } };

    76. 最小覆盖子串

    给你一个仅由大写英文字母组成的字符串,你可以将任意位置上的字符替换成另外的字符,总共可最多替换 k 次。在执行上述操作后,找到包含重复字母的最长子串的长度。 注意: 字符串长度 和 k 不会超过 10^4。 思路:滑动窗口

    class Solution { public: string minWindow_1(string s, string t) { //最小字串的开始位置和最小长度 int start=0,minLength=INT_MAX; unordered_map<char,int> needs; unordered_map<char,int> window; //建立字符串t的字符表 for(char c:t)needs[c]++; //初始化窗口区间和记录窗口中满足的字符个数 int left=0,right=0,match=0; //开始滑动窗口 while(right<s.size()) { char c1=s[right]; if(needs.count(c1)){//字符c1存在于t的字符表中 window[c1]++;//加入window if(window[c1]==needs[c1])//字符c1的个数已经满足字符表中的个数了 match++; } right++; while(match==needs.size()) { if(right-left<minLength){//窗口的大小小于最小长度,更新字符串 start=left; minLength=right-left; } char c2=s[left]; if(needs.count(c2)){//c2在needs中,主要用来去除window中多余的字符数的 window[c2]--;//字符c2的个数减1 if(window[c2]<needs[c2])//字符c2出现次数不再符合要求 match--; } left++;//不管c2在不在needs中,都需要将left右移,因为此时的window的字符个数必然多于needs的字符个数 } } return minLength==INT_MAX?"":s.substr(start,minLength); } };

    402. 移掉K位数字

    给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。 注意: num 的长度小于 10002 且 ≥ k。 num 不会包含任何前导零。 参考:地址

    #include<iostream> #include<bits/stdc++.h> using namespace std; class Solution { public: string removeKdigits(string num, int k) { string res; for(int i=0;i<num.size();i++){ while(res.size() && k && res.back()>num[i]){ res.pop_back(); k--; } if(res.size()==0 && num[i]=='0'){ continue;//跳过前置0 } res+=num[i]; } while(k>0 && !res.empty()){//当还要再移除数字的时候:从此时单调递增栈的top部删去数字 res.pop_back(); k--; } return res==""? "0":res; } }; int main(){ Solution sol; string str="1432219"; string str1="123456"; string str2="10001001"; string res=sol.removeKdigits(str2,3); int c=1; }
    Processed: 0.011, SQL: 9