LeetCode经典题目总结

it2024-10-19  24

 

目录

5. Longest Palindromic Substring

53. Maximum Subarray

115. Distinct Subsequences

139. Word Break

198. House Robber

28. Implement strStr()

668. Kth Smallest Number in Multiplication Table

35. Search Insert Position

230. Kth Smallest Element in a BST

378. Kth Smallest Element in a Sorted Matrix

1143. Longest Common Subsequence

最长公共子串问题

239. Sliding Window Maximum

464. Can I Win

413. Arithmetic Slices

108. Convert Sorted Array to Binary Search Tree

390. Elimination Game

1104. Path In Zigzag Labelled Binary Tree

394. Decode String

92. Reverse Linked List II

109. Convert Sorted List to Binary Search Tree

153. Find Minimum in Rotated Sorted Array

154. Find Minimum in Rotated Sorted Array II

10. Regular Expression Matching

322. Coin Change

494. Target Sum



 


5. Longest Palindromic Substring

题目描述:

Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

Example 1:

Input: "babad" Output: "bab" Note: "aba" is also a valid answer.

Example 2:

Input: "cbbd" Output: "bb"

思路:

用动态规划的思路,找一个字符串最长的回文序列,考虑当一个子序列为回文序列时将其两边各加一个字母后是不是回文序列。

而这个子字符串有两种情况:aba和bb。可以建立一个二维bool数组dp[n][n],当以i开头j结尾的子序列为回文序列时dp[i][j]==true。

则有状态转移方程:dp[i][j]=(dp[i+1][j-1]==true)&&(s[i]==s[j])。

代码:

class Solution { public: string longestPalindrome(string s) { return code1(s); } string code1(string s) { if(s.empty()||s.length()==1) return s; int n=s.length(); bool dp[n][n]; //初始化为false for(int i=0;i<n;++i) { for(int j=0;j<n;++j) { dp[i][j]=false; } } //单个字母是回文序列,即d[i][i]==true //同时判断所有的两个字母的序列是不是回文序列 for(int i=0;i<n-1;++i) { dp[i][i]=true; dp[i][i+1]=(s[i]==s[i+1]); } dp[n-1][n-1]=true; //开始dp判断 for(int i=n-3;i>=0;--i) { for(int j=n-1;j>=2;--j) { if(dp[i+1][j-1]==true&&s[i]==s[j]) dp[i][j]=true; } } int sta=0;int end=-1;int len=1; for(int i=0;i<n;++i) { for(int j=0;j<n;++j) { //cout<<dp[i][j]<<' '; if(dp[i][j]&&len<j-i+1) { sta=i;end=j;len=j-i+1; } } } return s.substr(sta,len); } };

53. Maximum Subarray

题目描述:

Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.

Example:

Input: [-2,1,-3,4,-1,2,1,-5,4], Output: 6 Explanation: [4,-1,2,1] has the largest sum = 6.

Follow up:

If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.

思路:

动态规划。设置一个长度为n的数组ans[n],其含义为原序列中以第n个数结尾的子序列的最大连续值之和。则当ans[i-1]与nums[i]之和小于nums[i]本身时,说明此时需要舍弃前面的序列,只保留nums[i]这个数作为当前最大连续值序列的起始数。即

ans[i]=max(ans[i-1]+nums[i],nums[i]);且ans[0]=nums[0]。当求得所有的ans[n]的值之后遍历数组ans[n],最大值即为所求。

代码:

class Solution { public: int maxSubArray(vector<int>& nums) { return code(nums); } int code(vector<int>& nums) { int n=nums.size(); if(n==0) return 0; if(n==1) return nums[0]; int ans[n]; ans[0]=nums[0]; for(int i=1;i<n;++i) { if(ans[i-1]+nums[i]<nums[i]) ans[i]=nums[i]; else { ans[i]=ans[i-1]+nums[i]; } } int aans=INT_MIN; for(int c:ans) aans=aans>c?aans:c; return aans; } };

115. Distinct Subsequences

题目描述:

Given a string S and a string T, count the number of distinct subsequences of S which equals T.

A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, "ACE" is a subsequence of "ABCDE" while "AEC" is not).

Example 1:

Input: S = "rabbbit", T = "rabbit" Output: 3 Explanation: As shown below, there are 3 ways you can generate "rabbit" from S. (The caret symbol ^ means the chosen letters) rabbbit ^^^^ ^^ rabbbit ^^ ^^^^ rabbbit ^^^ ^^^

Example 2:

Input: S = "babgbag", T = "bag" Output: 5 Explanation: As shown below, there are 5 ways you can generate "bag" from S. (The caret symbol ^ means the chosen letters) babgbag ^^ ^ babgbag ^^ ^ babgbag ^ ^^ babgbag ^ ^^ babgbag ^^^

思路:

动态规划。建立一个int型二维数组dp[tn][sn],其中tn为target 字符串的长度,sn为源字符串的长度。该二维数组第i行保存的是以第i个字母结尾的target子字符串在源字符串中的个数。在第i行中,第j列保存的是当前以第i个字母结尾的target子字符串在以第j个源字符串中字母结尾的子字符串中的个数。

代码:

class Solution { public: int numDistinct(string s, string t) { return code1(s,t); } int code1(string& s,string& t) { if(s.empty()||t.empty()) return 0; int sn=s.length(); int tn=t.length(); int dp[tn][sn]; /* 思路如图: b a b g b a g b 1 0 1 0 1 0 0 a 0 1 0 0 0 3 0 g 0 0 1 0 0 4 0 */ for(int i=0;i<tn;++i) { for(int j=0;j<sn;++j) { dp[i][j]=0; } } //先填第一行,t中的子字符串t[0]在s中的数目 for(int k=0;k<sn;++k) { if(s[k]==t[0]) dp[0][k]=1; } //开始dp,count用来计数t[k]前一个字符t[k-1]的出现次数 for(int i=1;i<tn;++i) { long count=0; for(int j=i;j<sn;++j) { count+=dp[i-1][j-1]; if(t[i]==s[j]) dp[i][j]=count; } } //最后遍历一次dp的tn-1行求和即可得到总个数 long ans=0; for(int c:dp[tn-1]) ans+=c; return ans; } };

139. Word Break

题目描述:

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words.

Note:

The same word in the dictionary may be reused multiple times in the segmentation.You may assume the dictionary does not contain duplicate words.

Example 1:

Input: s = "leetcode", wordDict = ["leet", "code"] Output: true

Example 2:

Input: s = "applepenapple", wordDict = ["apple", "pen"] Output: true

Example 3:

Input: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"] Output: false

思路:

动态规划,例如s的长度为n,建立一个bool数组dp[n+1]用来表示s的第i位是否可以作为一个单词的开头字母。s[i]可以作为一个单词的开头字母的条件是它前面的字符串是一个单词。当遍历完了s之后,dp[n+1]==true就表示前面的所有字符都能组成多个单词,因此返回true,反之则返回false。

代码:

class Solution { public: bool wordBreak(string s, vector<string>& wordDict) { return find1(s,wordDict); } bool find1(string& s,vector<string>& wordDict) { if(s.empty()) return false; int n=s.length(); bool dp[n+1]; for(bool& c:dp) c=false; dp[0]=true;//dp[n+1]用于记录是否可以做为新的单词的开头 //每次从可以作为新单词开头的字符开始寻找下一个word,直到s末尾 //例如对于s=catsandog来说,其dp[]中的值最终结果为: //c a t s a n d o g //T F F T T F F T F F //因为最后一个值dp[n+1]==false,因此s不能被成功分割,从而return false //l e e t c o d e //T F F F T F F F T //dp[n+1]==true,因此s能被成功分割,return true unordered_set<string> word; for(string ss:wordDict) word.insert(ss); for(int i=0;i<n;++i) { if(dp[i]==false) continue;//不能作为首字母的直接跳过 string tmp=""; for(int j=i;j<n;++j) { tmp+=s[j]; if(word.find(tmp)!=word.end()) dp[j+1]=true;//找到的leet单词的之后一个字符c可以作为新单词的首字母 } } return dp[n]; } };

198. House Robber

题目描述:

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.

Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.

Example 1:

Input: [1,2,3,1] Output: 4 Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).   Total amount you can rob = 1 + 3 = 4.

Example 2:

Input: [2,7,9,3,1] Output: 12 Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1).   Total amount you can rob = 2 + 9 + 1 = 12.

思路:

动态规划。设置一个dp[n]数组,在nums中从左往右循环时dp[i]保存遍历到nums[i]时能够得到的最大值。则dp[0]=nums[0],dp[1]为nums[0]和nums[1]中较大的那个。从dp[3]开始,dp[i]要么取nums[i]与dp[i-2]之和,要么取dp[i-1],当然是二者取较大的那个。这样一直遍历到dp[n-1],输出dp[n-1]即为所求。

代码:

class Solution { public: int rob(vector<int>& nums) { //return find1(nums); return find2(nums); } int find1(vector<int>& nums) {//错误的示例,只想着隔一个取一个值,却没考虑[2,1,1,4]这种情况 if(nums.empty()) return 0; if(nums.size()==1) return nums[0]; int ans1=0;int ans2=0; for(int i=0;i<nums.size();++i) { if(i%2==0) ans1+=nums[i]; else ans2+=nums[i]; } return max(ans1,ans2); } int find2(vector<int>& nums) { if(nums.empty()) return 0; if(nums.size()==1) return nums[0]; int n=nums.size(); int dp[n]; for(int& c:dp) c=0; dp[0]=nums[0];//当只有第一个店铺时,只能取这家 dp[1]=max(nums[0],nums[1]);//当有两个店铺时,取其中值最大的一个 //往后的店铺,对于第i个来说,取i-1的值与i-2的值和i的值之和中更大的那个 for(int i=2;i<n;++i) { dp[i]=max(dp[i-1],dp[i-2]+nums[i]); } return dp[n-1]; } };

28. Implement strStr()

题目描述:

Implement strStr().

Return the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack.

需要注意的是:面试时要问清楚,当needle字符串为空时返回什么,这是非常重要的。而C的库函数中返回的是0;

Example 1:

Input: haystack = "hello", needle = "ll" Output: 2

Example 2:

Input: haystack = "aaaaa", needle = "bba" Output: -1

思路:

最简单的就是暴力破解了,这种方法的时间复杂度是O(n^2)。

巧妙的解法,估计也是C库函数中的方法是使用双指针,其时间复杂度为O(n)。

代码:

class Solution { public: int strStr(string haystack, string needle) { return code2(haystack,needle); } int code2(const string& hay,const string& needle) { if(needle.empty()) return 0; if(hay.empty()) return -1; int p1=0;int p2=0; int n1=hay.length();int n2=needle.length(); while(p1<n1&&p2<n2) { if(hay[p1]==needle[p2]) { ++p1;++p2; } else { p1=p1-p2+1;//当字符不相等时,p1要返回到上一次的起始位置的后一个字符 p2=0;//重新开始,而p2则直接从头开始 } } //如果p2==n2,说明needle完全遍历完了,则返回的是首字符的index,即p1-p2 //否则返回-1 if(p2==n2) return p1-p2; else return -1; } };

668. Kth Smallest Number in Multiplication Table

题目描述:

Given the height m and the length n of a m * n Multiplication Table, and a positive integer k, you need to return the k-th smallest number in this table.

Example 1:

Input: m = 3, n = 3, k = 5 Output: Explanation: The Multiplication Table: 1 2 3 2 4 6 3 6 9 The 5-th smallest number is 3 (1, 2, 2, 3, 3).

Note:

The m and n will be in the range [1, 30000].The k will be in the range [1, m * n]

思路:

因为这个矩阵会非常大,因此将其保存在vector中然后输入会报内存溢出的错;利用堆来线性查找又会报超时的错。因此可以的一个方法是二分查找。

查找过程中,起始left=1,right=m*n;起始mid=(left+right)/2,每次统计大于等于mid的数的个数num,如果num>=k,则从左边一半的范围继续;如果num<k则从右边一半的范围继续;一直到left==right时,left即为所求的数。

代码:

class Solution { public: int findKthNumber(int m, int n, int k) { return code2(m,n,k); } int code1(int m,int n,int k) { //方法1,二分查找 int left=1,right=m*n; //直到left==right时,left即为结果 while(left<right) { int mid=(left+right)/2; if(enough(mid,m,n,k)) { //当小于等于mid的数的个数≥k时,右边界重新赋值为mid right=mid; } else left=mid+1;//此处应当赋值mid+1,否则会产生死循环 } return left; } bool enough(int cur,int m,int n,int k) { //统计表中小于cur的数的个数,按照行来统计 int count=0; for(int i=1;i<=m;++i) { count+=min(cur/i,n); } //如果表中小于cur的数大于等于k,则返回true;否则返回false return count>=k; } };

35. Search Insert Position

题目描述:

Given a sorted array and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order.

You may assume no duplicates in the array.

Example 1:

Input: [1,3,5,6], 5 Output: 2

Example 2:

Input: [1,3,5,6], 2 Output: 1

Example 3:

Input: [1,3,5,6], 7 Output: 4

Example 4:

Input: [1,3,5,6], 0 Output: 0

思路:二分查找,若找到则返回,若没找到,则根据题意和规律,返回的index就是退出循环后的left 和right之间的较大值。

class Solution { public: int searchInsert(vector<int>& nums, int target) { return code1(nums,target); } int code1(vector<int>& nums,int t) { int n = nums.size(); if(n == 0) { return 0; } int left = 0;int right = n - 1; while(left <= right) { //注意循环条件是小于等于,否则的话当数列中只有一个数时就不对 int mid = (left + right)/2; if(nums[mid] < t) { left = mid + 1;//记得是mid + 1 } else if(nums[mid] > t) { right = mid - 1;//同理,mid - 1 } else return mid; } //最终未找到时,返回插入的位置,根据题意,总是返回的两个index的较大值 return max(left,right); } };

230. Kth Smallest Element in a BST

题目描述:

Given a binary search tree, write a function kthSmallest to find the kth smallest element in it.

Note: You may assume k is always valid, 1 ≤ k ≤ BST's total elements.

Example 1:

Input: root = [3,1,4,null,2], k = 1 3 / \ 1 4 \   2 Output: 1

思路:

根据BST的特点,其中序遍历之后的结果就是一个排好序的数列。因此在中序遍历的过程中访问到root的值时记录k的值,如果不等于1则将其减一,如果等于1则此时root 的值就是所求。

代码:

/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: int kthSmallest(TreeNode* root, int k) { return code1(root,k); } int code1(TreeNode* root,int k) { int ans=0; help1(root,k,ans); return ans; } void help1(TreeNode* root,int& k,int& ans) { //注意此处应当写k的引用,如果直接写int k,则递归过程中内层函数退出时 //k的值会回退到进入函数之前,这样就没有一直使k减小的效果了 if(root==nullptr) return; if(root->left) help1(root->left,k,ans); if(k==1) ans=root->val; --k; if(root->right) help1(root->right,k,ans); return; } };

378. Kth Smallest Element in a Sorted Matrix

题目内容:

Given a n x n matrix where each of the rows and columns are sorted in ascending order, find the kth smallest element in the matrix.

Note that it is the kth smallest element in the sorted order, not the kth distinct element.

Example:

matrix = [ [ 1, 5, 9], [10, 11, 13], [12, 13, 15] ], k = 8, return 13.

 

Note: You may assume k is always valid

思路:

(1)因为矩阵中的元素都是递增的,所以可以用二分查找。但是二分查找的边界需要设置为数值而不是index否则会得不到正确的结果。

(2)对于第k个这种问题,应当想到可以用堆来做。对于这道题,可以用priority_queue构建一个拥有k个元素的最大堆,然后遍历剩下的元素。当num小于对顶元素值时,将堆顶pop,将此值push进堆。同时这个堆能够自己调整使其一直是最大堆。遍历结束后输出堆顶元素即为所求。

代码:

class Solution { public: int kthSmallest(vector<vector<int>>& matrix, int k) { return code3(matrix,k); } int code1(vector<vector<int>>& mat,int k) { //二分查找,用index作为边界,会出现错误情况 if(mat.empty()||k<1) return 0; int n = mat.size(); int l = 0;int r = n*n-1; int ans; while(l<r) { int sum=0; int mid = (l+r)/2; int row = mid/n;int col = mid%n; cout<<mid<<' '<<row<<' '<<col<<' '; int cur = mat[row][col]; for(int i = 0;i<n;++i) { for(int j = 0;j<n;++j) { if(mat[i][j]<=cur) sum++; else break; } } if(sum>=k) r=mid; else l=mid+1; cout<<sum<<endl; } ans=mat[(l)/n][(l)%n]; return ans; } int code2(vector<vector<int>>& mat,int k) { //二分查找,这次以值为边界 if(mat.empty()||k<1) return 0; int n = mat.size(); int l = 0;int r = n*n-1; int ans; int lv = mat[0][0];int rv = mat[n-1][n-1]; while(lv<rv) { int mid = (lv+rv)/2; int sum = 0; for(int i = 0;i<n;++i) { for(int j = 0;j<n;++j) { if(mat[i][j]<=mid) sum++; else break; } } if(sum<k) lv=mid+1; else rv=mid; } return lv; } int code3(vector<vector<int>>& mat,int k) { //用最大堆实现 if(mat.empty()||k<1) return 0; int n = mat.size(); vector<int>hp; for(int i=1;i<=k;++i) hp.push_back(mat[(i-1)/n][(i-1)%n]); priority_queue<int,vector<int>,less<int>>h(hp.begin(),hp.end()); int row = k/n;int col = k%n;//注意此处要从下一个开始遍历,否则堆中会重复第k个数字 for(int i=row;i<n;++i) { for(int j=0;j<n;++j) { if(i==row&&j<col) continue;//注意此处要从第k+1个数开始遍历,但是会是一个不完整的矩形 //此时应当将第一行的前面几个已经添加到堆中的元素跳过,避免重复添加 int cur = mat[i][j]; //<<cur<<' '<<h.top()<<endl; if(cur<h.top()) { h.pop(); h.push(cur); } } } return h.top(); } };

1143. Longest Common Subsequence

题目描述:

Given two strings text1 and text2, return the length of their longest common subsequence.

A subsequence of a string is a new string generated from the original string with some characters(can be none) deleted without changing the relative order of the remaining characters. (eg, "ace" is a subsequence of "abcde" while "aec" is not). A common subsequence of two strings is a subsequence that is common to both strings.

If there is no common subsequence, return 0.

Example 1:

Input: text1 = "abcde", text2 = "ace" Output: 3 Explanation: The longest common subsequence is "ace" and its length is 3.

思路:

动态规划的思路,建立一个2维数组,dp[i][j]表示到str1[i]和str2[j]时最长的公共子序列。则状态转移方程为:

if(str1[i] == str2[j])

    dp[i][j] = dp[i-1][j-1] + 1; else     dp[i][j] = max(dp[i-1][j],dp[i][j-1]);

代码:

class Solution { public: int longestCommonSubsequence(string text1, string text2) { return code1(text1,text2); } int code1(string str1,string str2) { if(str1.empty()||str2.empty()) return 0; int n1 = str1.length();int n2 = str2.length(); vector<vector<int>> dp(n1,vector<int>(n2,0)); if(str1[0] == str2[0]) dp[0][0] = 1; for(int i=1;i<n2;++i) { if(str1[0] == str2[i]) { dp[0][i] = 1; } else dp[0][i] = dp[0][i-1]; } for(int i=1;i<n1;++i) { if(str2[0] == str1[i]) { dp[i][0] = 1; } else dp[i][0] = dp[i-1][0]; } for(int i = 1;i<n1;++i) { for(int j = 1;j<n2;++j) { if(str1[i] == str2[j]) dp[i][j] = dp[i-1][j-1] + 1; else dp[i][j] = max(dp[i-1][j],dp[i][j-1]); } } return dp[n1-1][n2-1]; } };

最长公共子串问题

与最长公共子序列相似的是最长公共子串问题。此处相同的字串必须是连续的序列,而不像最长公共子序列问题中序列是可以不相连的。例如12321和23291的最长公共子序列为2321,而最长公共子串是232.

思路:

只需要修改状态转换方程:

if(str1[i] == str2[j])

    dp[i][j] = dp[i-1][j-1] + 1; else     dp[i][j] = 0;

代码:

#include <iostream> #include <string> #include <vector> #include <algorithm> using namespace std; int find(vector<int>& nums1,vector<int>& nums2,int n1,int n2) { if(nums1.empty()||nums2.empty()) return 0; int result = 0; vector<vector<int>>dp(n1,vector<int>(n2,0)); for(int i=0;i<n1;++i) { if(nums1[i]==nums2[0]) dp[i][0]=1; } for(int i=0;i<n2;++i) { if(nums1[0]==nums2[i]) dp[0][i]=1; } for(int i=1;i<n1;++i) { for(int j=1;j<n2;++j) { if(nums1[i]==nums2[j]) dp[i][j]=dp[i-1][j-1]+1; else dp[i][j]=0; result=max(result,dp[i][j]); } } return result; } int main() { int n1,n2; vector<int>nums1;vector<int>nums2; int inp; cin>>n1; for(int i=0;i<n1;++i) { cin>>inp;nums1.push_back(inp); } cin>>n2; for(int i=0;i<n2;++i) { cin>>inp;nums2.push_back(inp); } int ans=find(nums1,nums2,n1,n2); cout<<ans<<endl; return 0; }

239. Sliding Window Maximum

题目描述:

Given an array nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position. Return the max sliding window.

Example:

Input: nums = [1,3,-1,-3,5,3,6,7], and k = 3 Output: [3,3,5,5,6,7] Explanation: Window position Max --------------- ----- [1 3 -1] -3 5 3 6 7 3 1 [3 -1 -3] 5 3 6 7 3 1 3 [-1 -3 5] 3 6 7 5 1 3 -1 [-3 5 3] 6 7 5 1 3 -1 -3 [5 3 6] 7 6 1 3 -1 -3 5 [3 6 7] 7

思路:

如果遍历序列,然后对于每一个窗口中的值再遍历来求出最大值,这种暴力解法的时间复杂度是O(nk),k为窗口长度。需要考虑更优的方法。

时间复杂度为O(n):具体想法是使用一个双端队列,c++中是用deque实现。建立一个双端队列window用来保存最大值的index。首先起始的时候先判断窗口中的值,将最大的值的index保存在window中,例如上述例子中保存的是3的index:1。然后从-3开始遍历,相当于开始滑动窗口。首先遍历的过程中需要在最开始判断window的头部的值是不是已经被窗口滑过了,如果是则需要将其pop出来,判断方法是直接用当前num的index-window.front()==k。

上述判断之后,当碰到的num小于window头部front的值时,num在后续的窗口序列中有可能成为最大值,因此需要把这个num的index push到window的尾部,与此同时若num大于window尾部的值,则这个尾部的值不可能成为最大值,需要pop出来。并且需要循环一直将这样的尾部pop出来,直到尾部的值大于num或者window为空,再将num的index push到window的尾部。这样一直循环遍历下去。每次循环中window的头部index对应的值就是窗口的最大值,将其保存在vector中。

代码:

class Solution { public: vector<int> maxSlidingWindow(vector<int>& nums, int k) { return code1(nums,k); } vector<int> code1(vector<int>& nums,int k) { deque<int> window;vector<int> ans; if(nums.empty()||k<1||k>nums.size()) return ans; int i=0; while(i<k) { while(!window.empty()&&nums[i]>nums[window.back()]) window.pop_back();//注意是循环中判断,需要将所有小于nums[i]的数都pop出去 window.push_back(i); ++i; } ans.push_back(nums[window.front()]); for(i;i<nums.size();++i) { if(i-window.front()>=k) window.pop_front();//front的最大值已经滑出窗口范围,需要pop出去 while(!window.empty()&&nums[i]>nums[window.back()]) window.pop_back();//当队列的尾部元素小于当前nums[i]时,其不可能再成为最大值,因此将其pop出去 window.push_back(i); ans.push_back(nums[window.front()]); } return ans; } };

464. Can I Win

参考链接:https://blog.csdn.net/liuchuo/article/details/54729227

题目描述:

In the "100 game," two players take turns adding, to a running total, any integer from 1..10. The player who first causes the running total to reach or exceed 100 wins.

What if we change the game so that players cannot re-use integers?

For example, two players might take turns drawing from a common pool of numbers of 1..15 without replacement until they reach a total >= 100.

Given an integer maxChoosableInteger and another integer desiredTotal, determine if the first player to move can force a win, assuming both players play optimally.

You can always assume that maxChoosableInteger will not be larger than 20 and desiredTotal will not be larger than 300.

意思就是:两个人玩游戏,从1~maxChoosableInteger中任选一个数字,第一个人先选,第二个人后选,每个人选过的数字就不能再选了~两个人谁先加起来总和超过desiredTotal谁就赢,问给出数字,第一个人能否赢得比赛。

思路:

分析:两个特殊情况:1.如果能够选的最大数字大于等于desiredTotal,第一个人又不傻肯定选最大的值~那样他就赢啦~即: if(maxn >= desiredTotal) return true;2.如果所有能够选的数字的总和都小于desiredTotal,再怎么选两个人都不可能赢,所以肯定输~: 然后建立一个canWin函数,需要用visited标记某个数字是否被选过~因为可选的数字最大不超过20,则可以用一个整型数组标记,因为整型有32位~如果1被选过就把第1位(不是第0位哦~当然也可以从0开始啦~)标记为1,如果2被选过就把第2位标记为1~这样保证所有数字不重复~ 用map标记当前情况在map表中是否存在,存在的话结果保存在map里面~如果我们发现这个visited也就是这个数字选择的情况已经被保存过了,就直接返回在map里面保存的结果~ 遍历i从1到maxn(maxn是可以选择的最大值,即maxChoosableInteger),表示考虑选择i的情况,用mask = 1 << i,如果mask和visited进行与运算,如果等于0说明当前的visited没有被访问过,就可以考虑这个i的情况,如果要选的这个i大于target,不傻的这个人就会选择i因为肯定能赢啦~还有种情况自己能赢,就是对方输了,即:canWin(target - i, mask | visited) == false,(mask | visited表示把i那位也标记为1~)这个时候把visited情况存起来并且return true,表示赢了~如果所有数字都遍历完还是没有return true,那就最后return false~return false之前不要忘记把当前状态存储起来~也就是m[visited] = false; 

代码:

class Solution { public: bool canIWin(int maxChoosableInteger, int desiredTotal) { return code1(maxChoosableInteger,desiredTotal); } bool code1(int maxn,int tar) { int sum = (1 + maxn)* maxn/2; if(sum<tar) return false; if(maxn >= tar) return true; int visit = 0;map<int,bool>mp; return canWin(tar,mp,visit,maxn); } bool canWin(int target,map<int,bool>& mp,int visit,int maxn) { if(mp.find(visit) != mp.end()) return mp[visit];//当查询到visit时,如果mp中已经有结果则直接返回 //开始遍历 for(int i=1;i<=maxn;++i) { int mask = (1<<i); //当还没有选择过i的时候,有两种情况能赢 //一种是当前选的i>target;另一种是当我选择i时,对方使用canWin函数得到的结果是输 if((mask&visit)==0&&(i>=target||canWin(target-i,mp,visit|mask,maxn)==false)) {//注意== 的优先级高于& 因此必须加括号 mp[visit] = true; return true;//要将赢的结果记录到map中,并返回true } } mp[visit]=false;//以上的循环之后都没有能赢的方案,则说明选择visit这种方案不能最终得到赢的结果, return false;//因此将这个false也记录在map中,并返回false。 } };

413. Arithmetic Slices

题目描述:

A sequence of number is called arithmetic if it consists of at least three elements and if the difference between any two consecutive elements is the same.

For example, these are arithmetic sequence:

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

The following sequence is not arithmetic.

1, 1, 2, 5, 7

A zero-indexed array A consisting of N numbers is given. A slice of that array is any pair of integers (P, Q) such that 0 <= P < Q < N.

A slice (P, Q) of array A is called arithmetic if the sequence: A[P], A[p + 1], ..., A[Q - 1], A[Q] is arithmetic. In particular, this means that P + 1 < Q.

The function should return the number of arithmetic slices in the array A.

Example:

A = [1, 2, 3, 4] return: 3, for 3 arithmetic slices in A: [1, 2, 3], [2, 3, 4] and [1, 2, 3, 4] itself.

思路:

其实就是找数列中所有的连续子数列,满足等差数列的条件。且数列的最小长度为3.

方法1:暴力解法,时间复杂度O(n^3),空间复杂度O(1).

方法2:将暴力解法优化,时间复杂度O(n^2),空间复杂度O(1).

方法3:dp,建立dp[n],dp[i]表示以A[i]结尾的数列中有多少满足条件。时间复杂度O(1),空间复杂度O(n).

方法4:优化dp,时间复杂度O(1),空间复杂度O(1).

代码:

class Solution { public: int numberOfArithmeticSlices(vector<int>& A) { return code3(A); } int code1(vector<int>& A) {//纯暴力解法,遍历A所有可能的subset并判断,如果满足条件则count++ if(A.size()<=2) return 0; int n = A.size(); int count = 0; for(int i=0;i<=n-3;++i) { for(int j=i+2;j<=n-1;++j) { bool is = true; int diff = A[i+1] - A[i]; for(int k=i+2;k<=j;++k) { if(A[k]-A[k-1]!=diff) { is = false;break; } } if(is) ++count; } } return count; } int code2(vector<int>& A) {//暴力解法修改版,每次遍历的时候,如果相邻的数之差不等于diff则break,如果等于则count++, //因为前面的相邻数之差已经计算过且等于diff,不用重新计算。 if(A.size()<=2) return 0; int n = A.size(); int count = 0; for(int i=0;i<=n-3;++i) { int diff = A[i+1] - A[i];//先得出以i为开头的序列的diff值 for(int j=i+2;j<n;++j) {//从第三个数开始往后遍历,每次如果差值都等于diff则count++,一直到数值的差值不等于diff时break if(A[j] - A[j-1]==diff) count++;//若后续的相邻数差值也等于diff则count++,若不等则直接break else break; } } return count; } int code3(vector<int>& A) {//动态规划的思路,建立的dp[n]表示以A[n]结尾的数列有多少个满足条件。判断方式就是以一个长度为3的滑窗遍历 if(A.size()<=2) return 0; int n = A.size(); int count = 0;int dp[n] = {0}; for(int i=2;i<n;++i) { if(A[i] - A[i-1] == A[i-1] - A[i-2]) {//若满足条件,则有下状态转移式 dp[i] = dp[i-1] + 1; count+=dp[i];//此方法的空间复杂度为O(n),实际上通过该行看出,经过这行的运算,前面的dp[n] //的值已经没用了,所以其实只用一个变量dp保存临时的dp[i]就行,从而将space降为了O(1) } } return count; } };

108. Convert Sorted Array to Binary Search Tree

题目描述:

Given an array where elements are sorted in ascending order, convert it to a height balanced BST.

For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differ by more than 1.

Example:

Given the sorted array: [-10,-3,0,5,9], One possible answer is: [0,-3,9,-10,null,5], which represents the following height balanced BST: 0 / \ -3 9 / / -10 5

思路:

对有序数列做二分查找,同时以先序遍历的方式构建二叉树。注意边界判断条件。

代码:

/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: TreeNode* sortedArrayToBST(vector<int>& nums) { return code2(nums); } TreeNode* code1(vector<int>& nums) { if(nums.empty()) return nullptr; TreeNode* root = new TreeNode(0); code1_build(root,nums,0,nums.size()-1); return root; } void code1_build(TreeNode* & root,vector<int>& nums,int s,int e) { if(s>e)//注意此处的判断 return; int mid = (e + s)/2; root = new TreeNode(nums[mid]); code1_build(root->left,nums,s,mid-1); code1_build(root->right,nums,mid+1,e); return ; } TreeNode* code2(vector<int>& nums) { if(nums.empty()) return nullptr; TreeNode* root = code2_build(nums,0,nums.size()-1); return root; } TreeNode* code2_build(vector<int>& nums,int s,int e) { if(s>e)//注意此处的判断 return nullptr; int mid = (s + e)/2; TreeNode* node = new TreeNode(nums[mid]); node->left = code2_build(nums,s,mid-1); node->right = code2_build(nums,mid+1,e); return node; } };

390. Elimination Game

题目描述:

There is a list of sorted integers from 1 to n. Starting from left to right, remove the first number and every other number afterward until you reach the end of the list.

Repeat the previous step again, but this time from right to left, remove the right most number and every other number from the remaining numbers.

We keep repeating the steps again, alternating left to right and right to left, until a single number remains.

Find the last number that remains starting with a list of length n.

Example:

Input: n = 9, 1 2 3 4 5 6 7 8 9 2 4 6 8 2 6 6 Output: 6

思路:

消除数字的游戏,如果按照题目建立一个数组老老实实一步一步消除数字,会出现TLE的错误,无法AC,因此需要通过找规律来做。通过每次消除后记录数列左边的值,直到数列中剩余数字只有一个时,这个左边的数字就是答案。用int left = 1记录左边数字。

当从左边开始消除时,左边的数字必定会右移,具体右移的大小是2的幂次,如2,4,8等;当从右边开始消除时,若此时数列的数目为偶数时,消除之后左边数字不变,若数列的数目为奇数时,消除后左边数字也会右移,右移大小为2的幂次。

循环中每次更新剩余的数目rest,右移的step,直到剩余数为1时输出left即可。

代码:

class Solution { public: int lastRemaining(int n) { return code1(n); } int code1(int n) { if(n==1)return 1; int left = 1;int rest = n;int step = 1;bool left2right = true; while(rest!=1) { if(left2right||(!left2right&&rest%2==1)) left+=step; rest/=2; left2right = left2right ? false : true; step*=2; } return left; } };

1104. Path In Zigzag Labelled Binary Tree

题目描述:

In an infinite binary tree where every node has two children, the nodes are labelled in row order.

In the odd numbered rows (ie., the first, third, fifth,...), the labelling is left to right, while in the even numbered rows (second, fourth, sixth,...), the labelling is right to left.

Given the label of a node in this tree, return the labels in the path from the root of the tree to the node with that label.

Example 1:

Input: label = 14 Output: [1,3,4,14]

思路:

找规律的题目。首先根据给出的label值可以得到这个节点处于二叉树的第几层,设这个层数是level。然后得到这个level之后,可以得出这一层数字的最大值和最小值,进而求得二者之和sum。若二叉树中数字的排布不是z字型而是正常的一行一行,则每个节点的父节点的值就是parent = label/2,而对于z字型排布,有parent = sum - label/2。从而可以由后往前循环地得到所求路径。

代码:

class Solution { public: vector<int> pathInZigZagTree(int label) { return code1(label); } vector<int> code1(int label) { if(label<1) return vector<int>(0); int level = 0;int tmp = 1; while(tmp<=label) { ++level;tmp*=2; } vector<int> ans(level,0); ans[0] = 1;ans[level-1] = label; int copylevel = level;level-=1; for(int i=copylevel-2;i>=1;--i) { level--; int sum = (1<<level) + (1<<(level+1))-1; ans[i] = sum - ans[i+1]/2; } return ans; } };

394. Decode String

题目描述:

Given an encoded string, return its decoded string.

The encoding rule is: k[encoded_string], where the encoded_string inside the square brackets is being repeated exactly k times. Note that k is guaranteed to be a positive integer.

You may assume that the input string is always valid; No extra white spaces, square brackets are well-formed, etc.

Furthermore, you may assume that the original data does not contain any digits and that digits are only for those repeat numbers, k. For example, there won't be input like 3a or 2[4].

Examples:

s = "3[a]2[bc]", return "aaabcbc". s = "3[a2[c]]", return "accaccacc". s = "2[abc]3[cd]ef", return "abcabccdcdcdef".

思路:

需要用栈来实现。遍历字符串的过程中将字符入栈,当遍历到了']'时,再将栈中的字符挨个弹出,直到遇到栈中的一个'['。此时弹出这个左括号后接下来是一系列的数字字符(注意数字是有可能大于10的,所以会是一连串的数字字符构成一个数字)。用这个数字num和前面得到的字串str生成num个str构成的substr。此时当栈为空时把subtr加到结果字串后边;当栈不为空时需要把这个字串再次入栈回去,并且需要注意的是在入栈之前要将其反转,保证顺序不出错。

遍历的过程中当栈为空且遍历到的当前字符c不是数字时,说明字符串后续没有被压缩,因此直接将c加到结果字符串的后边;若c为数字,说明后续还有被压缩的字串,因此需要将它们入栈。

代码:

class Solution { public: string decodeString(string s) { return code1(s); } string code1(string s) { stack<char> st; string ans = ""; for(auto c:s) { if(c == ']') {//当读到一个]时,开始在stack中找与之对应的[ 并得到其中的字符串 string reverse_s = ""; while(st.top()!='[') { reverse_s += st.top(); st.pop(); } st.pop();//将stack中的[ pop出去 string s_num = "";//要考虑数字大于1位的情况,将连续数字字符串暂存在string中 while(!st.empty()&&(st.top()>='0'&&st.top()<='9')) { s_num+=st.top();st.pop();//得到[ 前面的数字字符串,如100,99等 } reverse(s_num.begin(),s_num.end()); int num = stoi(s_num);//将其反转后转为int类型 if(!st.empty()) {//如果stack不为空,则需要将上述得到的字符串按照num展开后再push进stack中 reverse(reverse_s.begin(),reverse_s.end());//注意需要将其先反转再入栈!! for(int i=0;i<num;++i) { for(auto cc:reverse_s) { st.push(cc); } } } else {//如果提取出了一个子串后stack为空了,则直接将当前子串+=到最终的结果中 reverse(reverse_s.begin(),reverse_s.end());//由于栈的特点,需要先反转 for(int i=0;i<num;++i) { ans+=reverse_s; } } }//当读到的字符不是']'时,如果此时stack为空并且c为字符,则直接将其+=到最终结果中 else if(st.empty()&&!(c>='0'&&c<='9')) { ans+=c; } else//当读到的字符不是']',且stack不为空或者c是数字,则将c入栈 { st.push(c); } } return ans; } };

92. Reverse Linked List II

题目描述:

Reverse a linked list from position m to n. Do it in one-pass.

Note: 1 ≤ m ≤ n ≤ length of list.

Example:

Input: 1->2->3->4->5->NULL, m = 2, n = 4 Output: 1->4->3->2->5->NULL

思路:

只遍历一次,因此基本思路是将m与n划定的范围内的链表反转,同时得到这个新的子链表的头结点和尾结点,然后再分别与断开前的节点相连,最后返回整个链表的头节点。

代码:

/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* reverseBetween(ListNode* head, int m, int n) { return code1(head,m,n); } ListNode* code1(ListNode* head,int m, int n) { if(!head||!head->next||m==n) return head;//注意当m==n时实际上不需要操作直接返回即可 int k=1; ListNode* node = head;//node记录需要反转的子链表的当前节点 ListNode* front = nullptr;//front用来记录第m-1个节点,从而在最后能够将front与反转后的子链表相连 while(k!=m) { front = node; node=node->next; ++k; } ListNode* cur=nullptr;ListNode* next=nullptr;ListNode* tail = node; //此处即为链表反转的循环写法,tail记录的是反转后的子链表的最后一个节点 while(k!=n+1) { next = node->next; node->next = cur; cur = node; node = next; ++k; }//反转之后要分情况讨论,因为当m==1时,front为nullptr此时新的链表就是cur ListNode* ans = head; if(front)//当m>1时,将front的下一个节点指向新的子链表的头结点cur front->next = cur; else ans = cur; if(tail)//当n<链表总节点数时,将tail的下一个节点指向后续不需要反转的第一个节点,也就是迭代之后的node tail->next = node; return ans; } };

109. Convert Sorted List to Binary Search Tree

题目描述:

Given a singly linked list where elements are sorted in ascending order, convert it to a height balanced BST.

For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differ by more than 1.

Example:

Given the sorted linked list: [-10,-3,0,5,9], One possible answer is: [0,-3,9,-10,null,5], which represents the following height balanced BST: 0 / \ -3 9 / / -10 5

思路:

比较普通的解法就是前序遍历,每次找到有序数列的中点的数作为二叉树的根节点,然后递归地得到左右子树。但是由于是有序链表而不是有序数组,所以每次取这个中点数时需要从头节点开始遍历到中点数节点。

另一个比较好的思路是中序遍历。因为二叉搜索树的中序遍历结果就是一个排序了的数列。而中序遍历的过程中,还是用左右边界left 和right来控制每次的递归,而在中序遍历过程中取到了root的值之后,将链表的head指向下一个节点。

代码:

/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: TreeNode* sortedListToBST(ListNode* head) { return code2(head); } TreeNode* code1(ListNode* head) { if(head==nullptr) return nullptr; int n=0; ListNode* node = head; while(node) {node=node->next; ++n;}; return help1(head,1,n); } TreeNode* help1(ListNode* head,int left,int right) { if(left>right) return nullptr; int mid = (left+right)/2; auto node = head; int c = 1; while(c!=mid) {node=node->next; ++c; } TreeNode* root = new TreeNode(node->val); root->left = help1(head,left,mid-1); root->right = help1(head,mid+1,right); return root; } TreeNode* code2(ListNode* head) { if(head==nullptr) return nullptr; int n=0; ListNode* node = head; while(node){ ++n; node=node->next;} //TreeNode* root; hd = head; return help2(1,n); //return roo } TreeNode* help2(int left,int right) { if(left>right) return nullptr; int mid = (left+right)/2; TreeNode* ln = help2(left,mid-1); TreeNode* root = new TreeNode(hd->val); root->left = ln; hd=hd->next; root->right = help2(mid+1,right); return root; } private: ListNode* hd; };

153. Find Minimum in Rotated Sorted Array

题目描述:

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e.,  [0,1,2,4,5,6,7] might become  [4,5,6,7,0,1,2]).

Find the minimum element.

You may assume no duplicate exists in the array.

Example 1:

Input: [3,4,5,1,2] Output: 1

Example 2:

Input: [4,5,6,7,0,1,2] Output: 0

思路:

因为数列中没有重复数字,所以还是可以用二分查找来做。循环中每次取数列中间数字与最右端数字比较。如果该数字大于最右端数字,则说明它处于数列的前半段,因此令left=mid+1;若该数字小于最右端数字,则说明它处于数列的后半段,因此令right=mid。如此循环下去,直到left==right,此时的nums[left]就是所求的最小值。

代码:

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

154. Find Minimum in Rotated Sorted Array II

题目描述:

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e.,  [0,1,2,4,5,6,7] might become  [4,5,6,7,0,1,2]).

Find the minimum element.

The array may contain duplicates.

Example 1:

Input: [1,3,5] Output: 1

Example 2:

Input: [2,2,2,0,1] Output: 0

思路:

与上一道基本思路一样。不过这次数列中存在重复数字,因此当遇到特殊情况时只能老老实实顺序遍历了。

代码:

class Solution { public: int findMin(vector<int>& nums) { return code1(nums); } int code1(vector<int>& nums) { if(nums.empty()) return 0; if(nums.size()==1) return 1; int left = 0;int right = nums.size()-1; while(left<right) { int mid = (left+right)/2; if(nums[mid]>nums[right]) { left = mid+1; } else if(nums[mid]<nums[right]) { right=mid; } else {//此时遇到重复数字,无法判断最小数在哪个范围,因此从left-right顺序查找 int ans = nums[0]; for(int i=left;i<=right;++i) { ans=min(ans,nums[i]); } return ans; } } return nums[left]; } };

10. Regular Expression Matching

题目描述:

Given an input string (s) and a pattern (p), implement regular expression matching with support for '.' and '*'.

'.' Matches any single character. '*' Matches zero or more of the preceding element.

The matching should cover the entire input string (not partial).

Note:

s could be empty and contains only lowercase letters a-z.p could be empty and contains only lowercase letters a-z, and characters like . or *.

Example 1:

Input: s = "aa" p = "a" Output: false Explanation: "a" does not match the entire string "aa".

Example 2:

Input: s = "aa" p = "a*" Output: true Explanation: '*' means zero or more of the preceding element, 'a'. Therefore, by repeating 'a' once, it becomes "aa".

Example 3:

Input: s = "ab" p = ".*" Output: true Explanation: ".*" means "zero or more (*) of any character (.)".

Example 4:

Input: s = "aab" p = "c*a*b" Output: true Explanation: c can be repeated 0 times, a can be repeated 1 time. Therefore, it matches "aab".

Example 5:

Input: s = "mississippi" p = "mis*is*p*." Output: false

思路:

每次判断两个字符串的第一个字符,然后取这个字符之后的子串继续判断第一个字符,这样递归下去。过程中当p[1]为*时需要考虑两种情况:*代表了前面字符的零次;*代表了它前面字符的多次。具体思路见代码注释。

代码:

class Solution { public: bool isMatch(string s, string p) { return code1(s,p); } bool code1(string s,string p) { if(p.empty()) return s.empty();//当p为空时,若s空则返回true,否则返回false bool first_match = false;//判断s和p的第一个字符是否相等 if(!s.empty()&&!p.empty())//前提是s不为空,否则会出现out of range的错误 { first_match = (s[0]==p[0])||(p[0]=='.'); }//当p的长度大于等于2时,递归地往后判断 if(p.length()>=2) { if(p[1]=='*') {//当p的第二个字符为*时,分两种情况 //一种是*代表了它前面字符重复出现k次,此时要保证字符串的第一个字符是相等的, //然后取s的第二个字符开始的字串继续判断 //第二种是*代表了其前面的字符出现了0次,这时只需要将p取*后面的子串继续递归判断 return (first_match&&code1(s.substr(1),p) )||(code1(s,p.substr(2)) ); } //当p的第二个字符不是*时,只需要判断s和p的第一个字符是否相等,以及二者第一个字符后的子串是否匹配 else return (first_match&&code1(s.substr(1),p.substr(1))); } else//当p的长度小于2时,只需要判断s和p的第一个字符是否相等,以及二者第一个字符后的子串是否匹配 //return first_match&&code1(s.substr(1),p.substr(1)); return (s==p)||(p[0]=='.'&&s.length()==1);//或者,其实当p长度等于1时,只有当s长度也是1且二者相等才返回true } };

322. Coin Change

题目描述:

You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.

Example 1:

Input: coins = [1, 2, 5], amount = 11 Output: 3 Explanation: 11 = 5 + 5 + 1

Example 2:

Input: coins = [2], amount = 3 Output: -1

Note: You may assume that you have an infinite number of each kind of coin.

思路:

动态规划,建立一个dp数组用来记录当目标数值为i时需要的最少硬币数。

代码:

class Solution { public: int coinChange(vector<int>& coins, int amount) { return code1(coins,amount); } int code1(vector<int>& coins,int amount) { int n=coins.size(); if(n==0) return -1; int dp[amount+1];//建立dp数组,dp[i]表示构成数字i的最小硬币数 for(auto& c:dp) c=amount+1;//初始化dp[i]为amount+1,相当于一个MAX值,因为dp[i]最大值只可能是amount个1元硬币 dp[0]=0;//构成0元需要0个硬币,这个在后续dp中很重要 for(int i=1;i<=amount;++i)//从dp[1]开始 { for(int j=0;j<n;++j)//遍历所有的coins { if(i-coins[j]>=0)//i表示当前的目标值,coins[j]表示当前的硬币值 {//因此只有当i>=coins[j]时才能有后续操作 dp[i]=min(dp[i],dp[i-coins[j]]+1); //若将值为coins[j]的硬币使用,则总共的硬币数为dp[i-coins[j]]+1 } } }//若dp[amount]值依然为amount+1,则说明无法得到数额amount,返回-1 return dp[amount]==amount+1?-1:dp[amount]; } };

494. Target Sum

题目描述:

You are given a list of non-negative integers, a1, a2, ..., an, and a target, S. Now you have 2 symbols + and -. For each integer, you should choose one from + and - as its new symbol.

Find out how many ways to assign symbols to make sum of integers equal to target S.

Example 1:

Input: nums is [1, 1, 1, 1, 1], S is 3. Output: 5 Explanation: -1+1+1+1+1 = 3 +1-1+1+1+1 = 3 +1+1-1+1+1 = 3 +1+1+1-1+1 = 3 +1+1+1+1-1 = 3 There are 5 ways to assign symbols to make the sum of nums be target 3.

 

Note:

The length of the given array is positive and will not exceed 20.The sum of elements in the given array will not exceed 1000.Your output answer is guaranteed to be fitted in a 32-bit integer.

思路:

三种方法。具体见注释。

代码:

class Solution { public: int findTargetSumWays(vector<int>& nums, int S) { return code3(nums,S); } int code1(vector<int>& nums,int S) { int count=0; calcu(nums,0,0,S,count); return count; } void calcu(vector<int>& nums,int sum,int i,int S,int& count) {//暴力的dfs方法,挨个遍历并做加减法,当遍历结束时判断结果与目标值是否相等 if(i==nums.size()) { //cout<<sum<<' '<<count<<endl; if(sum==S) count++; return; } //cout<<i<<endl; calcu(nums,sum+nums[i],i+1,S,count); calcu(nums,sum-nums[i],i+1,S,count); } int code2(vector<int>& nums,int S) { vector<vector<int>>memo(nums.size()+1,vector<int>(2001,INT_MAX)); return cal2(nums,S,0,0,memo); } int cal2(vector<int>& nums,int S,int i,int sum,vector<vector<int>>&memo) {//在方法一的基础上,定义一个二维数组memo,memo[i][j]表示当遍历到第i个数时值为j的路径个数 if(i==nums.size()) { if(sum==S) return 1; else return 0; } else { if(memo[i][sum+1000]!=INT_MAX) return memo[i][sum+1000]; else { int add = cal2(nums,S,i+1,sum+nums[i],memo); int sub = cal2(nums,S,i+1,sum-nums[i],memo); memo[i][sum+1000] = add+sub; return memo[i][sum+1000]; } } } int code3(vector<int>& nums,int S) {//动态规划的方法,转化为01背包问题,找出哪些数能构成sump,求最终的组数 //sum(P)+sum(N)=sum(nums);sum(P)-sum(N)=S //=> sum(P) = (sum(nums)+S)/2 int sum = 0; for(auto& c:nums) sum += c; if(sum<S||(sum+S)&1==1) return 0; int sump = (sum+S)/2; vector<int>dp(sump+1,0); dp[0]=1;//很重要!!构成0的方案有一种 for(int i=0;i<nums.size();++i) { for(int j=sump;j>=nums[i];--j) {//注意内层循环要从大到小,否则从小到大会叠加很多多余值 dp[j] += dp[j-nums[i]];//注意是+= 而不是+1 } } return dp[sump]; } int code4(vector<int>& nums,int S) {//错误的方法 int sum = 0; for(auto& c:nums) sum += c; if(sum<S||(sum+S)&1==1) return 0; int sump = (sum+S)/2; vector<int>dp(sump+1,0); dp[0]=1;//很重要!!构成0的方案有一种 //sort(nums.begin(),nums.end()); for(int i=0;i<nums.size();++i) { for(int j=0;j<=sump;++j) {//注意此处不能从小到大 if(j>=nums[i]) dp[j] += dp[j-nums[i]]; } } return dp[sump]; } };

416. Partition Equal Subset Sum

题目描述:

Given a non-empty array containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal.

Note:

Each of the array element will not exceed 100.The array size will not exceed 200.

Example 1:

Input: [1, 5, 11, 5] Output: true Explanation: The array can be partitioned as [1, 5, 5] and [11].

思路:

首先判断数列的和能否被2整除,如果能,则设target=sum/2,问题转换为给定数列中选出某些数字之后能否和为target。很明显这个是01背包问题。因此可以用dp来做。dp[i]表示能否得到和为i的数。外层循环挨个遍历nums,内层循环遍历dp数组,不断更新。同时注意因为是01背包,即每个数只能选择一次,所以内层循环需要从后往前遍历,否则从前往后遍历有累积效应。同时状态转移方程为dp[j]=dp[j] || dp[j-nums[i]],而不能是简单的dp[j]=dp[j-nums[i]]。

代码:

class Solution { public: bool canPartition(vector<int>& nums) { return code2(nums); } bool code1(vector<int>& nums) { int sum = accumulate(nums.begin(),nums.end(),0); if(sum&1==1) return false; int target = sum>>1; bool dp[target+1]; for(auto& c:dp) c=false; dp[0] = true; for(int i=0;i<nums.size();++i) { for(int j=nums[i];j<=target;++j) {//错误的示例 dp[j] = dp[j]||dp[j-nums[i]]; } } return dp[target]; } bool code2(vector<int>& nums) { int sum = accumulate(nums.begin(),nums.end(),0); if(sum&1==1) return false; int target = sum>>1; bool dp[target+1]; for(auto& c:dp) c=false; dp[0] = true; for(int i=0;i<nums.size();++i) { for(int j=target;j>=nums[i];--j) {//正确的循环方式 dp[j]=dp[j]||dp[j-nums[i]];//注意有或运算|| } } return dp[target]; } };

698. Partition to K Equal Sum Subsets

题目描述:

Given an array of integers nums and a positive integer k, find whether it's possible to divide this array into k non-empty subsets whose sums are all equal.

Example 1:

Input: nums = [4, 3, 2, 3, 5, 2, 1], k = 4 Output: True Explanation: It's possible to divide it into 4 subsets (5), (1, 4), (2,3), (2,3) with equal sums.

Note:

1 <= k <= len(nums) <= 16.0 < nums[i] < 10000.

思路:

用dfs暴力搜索解决。定义一个bool型数组used,其中used[i]表示原数组nums[i]有没有用过。具体见代码注释。

代码:

class Solution { public: bool canPartitionKSubsets(vector<int>& nums, int k) { return code1(nums,k); } bool code1(vector<int>& nums,int k) { int sum = accumulate(nums.begin(),nums.end(),0); if(sum==0||sum%k!=0||k>nums.size()) return false; int target = sum/k;//目标数 int cursum = 0; vector<bool>used(nums.size(),false);//false表示没用过,true表示用过了 return dfs1(nums,used,cursum,k,target,0); } bool dfs1(vector<int>& nums,vector<bool>& used,int cursum,int k,int target,int start) {//cursum为当前的数值之和,start用来记录遍历过程中的index,通过这个变量记录一轮dfs的过程中遍历的起始index //从而不用每次从头开始遍历,降低运行时间。否则会TLE if(cursum>target) return false;//如果当前数值和大于目标值,则直接返回false if(k==0) return true;//如果k==0,说明已经找到了k组可行解,返回true if(cursum==target)//如果cursum==target说明当前找到了一组可行解, return dfs1(nums,used,0,k-1,target,0);//此时将cursum置为0,k减去1,start从头开始 for(int i=start;i<nums.size();++i) {//从start开始遍历 if(!used[i])//如果nums[i]没有被使用过 { used[i]=true;//置为true,将nums[i]使用,加到cursum中,同时start要从i+1开始 if(dfs1(nums,used,cursum+nums[i],k,target,i+1)) return true;//如果这个递归调用返回true说明这条路径可行,返回true used[i]=false;//否则,说明这个nums[i]不可行,则不使用它,将used[i]重新置为false } } return false;//前面的遍历都没有返回true,说明此时不可行,返回false } };

543. Diameter of Binary Tree

题目描述:

Given a binary tree, you need to compute the length of the diameter of the tree. The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or may not pass through the root.

Example: Given a binary tree

1 / \ 2 3 / \ 4 5

Return 3, which is the length of the path [4,2,1,3] or [5,2,1,3].

Note: The length of path between two nodes is represented by the number of edges between them.

思路:

结合求二叉树深度的题目思路。求二叉树深度的题目中,递归地求每个节点对应的深度最大值,最终返回的是树的最大深度。

应用到这道题中,简单的思路是针对树的每一个节点来求以这个节点为根节点的树的左右子树最大深度L和R,则diameter=L+R。最后输出最大的diameter即可。但是这种方法在计算过程中会有很多冗余计算。

优化的思路是:在求树的最大深度的过程中每次都求一个diameter=L+R,并不断更新,保留最大值。则针对根节点的一次递归求最大深度之后就能得到所求的diameter。

代码:

/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: int diameterOfBinaryTree(TreeNode* root) { return code2(root); } int code1(TreeNode* root) {//优化前的方法,很多冗余计算 if(!root) return 0; int ans=-1; find(root,ans); return ans; } void find(TreeNode* root,int& ans) { if(!root) return; ans=max(ans,help1(root->left)+help1(root->right)); find(root->left,ans); find(root->right,ans); return; } int help1(TreeNode* root) { if(!root) return 0; int left = help1(root->left)+1; int right = help1(root->right)+1; return max(left,right); } int code2(TreeNode* root) { if(!root) return 0; int ans=-1; int tmp = help2(root,ans); return ans; } int help2(TreeNode* root,int & ans) {//优化后的方法 if(!root) return 0; int left = help2(root->left,ans)+1; int right = help2(root->right,ans)+1; ans=max(ans,left+right-2); return max(left,right); } };

Path Sum系列问题

112. Path Sum

题目描述:

Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum.

Note: A leaf is a node with no children.

Example:

Given the below binary tree and sum = 22,

5 / \ 4 8 / / \ 11 13 4 / \ \ 7 2 1

思路:

借用前序遍历来递归地求和,注意题目要求是根节点到叶节点,因此要到了叶节点的时候再判断此时的和与sum是否相等。

代码:

/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: bool hasPathSum(TreeNode* root, int sum) { return code1(root,sum); } bool code1(TreeNode* root,int sum) { if(!root) return false; return help1(root,sum,0);//初始cur为0 } bool help1(TreeNode* root,int sum, int cur) { if(!root) return false; cur+=root->val;//cur加上当前节点的值 if(!root->left&&!root->right) {//当此节点是叶节点时判断cur是否与sum相等 return cur==sum; } //递归到左右子树去寻找 return help1(root->left,sum,cur)|| help1(root->right,sum,cur); } };

113. Path Sum II

题目描述:

Given a binary tree and a sum, find all root-to-leaf paths where each path's sum equals the given sum.

Note: A leaf is a node with no children.

Example:

Given the below binary tree and sum = 22,

5 / \ 4 8 / / \ 11 13 4 / \ / \ 7 2 5 1

Return:

[ [5,4,11,2], [5,8,4,5] ]

思路:

这次要将所有的可能路径都保存下来输出,因此在遍历的过程中需要同时用一个vector来保存当前的节点值。并且当递归时退出当前递归层时,要将此节点的值再pop出去。

代码:

/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: vector<vector<int>> pathSum(TreeNode* root, int sum) { return code1(root,sum); } vector<vector<int>> code1(TreeNode* root,int sum) { vector<vector<int>>ans; if(!root) return ans; vector<int>one; help1(root,sum,0,ans,one); return ans; } void help1(TreeNode* root,int sum,int cur,vector<vector<int>>& ans,vector<int>& one) { if(!root) return ; cur+=root->val; one.push_back(root->val); if(!root->left && !root->right) { if(cur==sum) ans.push_back(one); } help1(root->left,sum,cur,ans,one); help1(root->right,sum,cur,ans,one); one.pop_back();//很重要,退出此层的递归前要将当前的节点pop出去 return ; } };

437. Path Sum III

题目描述:

You are given a binary tree in which each node contains an integer value.

Find the number of paths that sum to a given value.

The path does not need to start or end at the root or a leaf, but it must go downwards (traveling only from parent nodes to child nodes).

The tree has no more than 1,000 nodes and the values are in the range -1,000,000 to 1,000,000.

Example:

root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8 10 / \ 5 -3 / \ \ 3 2 11 / \ \ 3 -2 1 Return 3. The paths that sum to 8 are: 1. 5 -> 3 2. 5 -> 2 -> 1 3. -3 -> 11

思路:

这次不一定是根节点到叶节点了。因此基于前面的题目思路,每次都要判断当前求和值与sum是否相等,而不需要先判断是否到了叶节点。同时开头的节点也不一定根节点,所以需要对每一个子树的根节点作为开头都查找一次。

代码:

/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: int pathSum(TreeNode* root, int sum) { return code1(root,sum); } int code1(TreeNode* root,int sum) { int ans=0; help1(root,sum,ans); return ans; } void help1(TreeNode* root,int sum,int& count) { if(!root) return; cal1(root,0,sum,count); help1(root->left,sum,count); help1(root->right,sum,count); return; } void cal1(TreeNode* root,int cur,int sum,int& count) { if(!root) return ; cur+=root->val;//注意此处要先把节点的值加上再作为递归调用的输入 if(cur==sum) {count++;} cal1(root->left,cur,sum,count); cal1(root->right,cur,sum,count); return; } };

124. Binary Tree Maximum Path Sum

题目描述:

Given a non-empty binary tree, find the maximum path sum.

For this problem, a path is defined as any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The path must contain at least one node and does not need to go through the root.

Example 2:

Input: [-10,9,20,null,null,15,7]   -10    / \   9  20     /  \    15   7 Output: 42

思路:

用递归的方法,相当于是自底向上来求解。自底向上的过程之中,以每一个叶节点为根节点时,最大值就是这个值,向上的过程中,返回的是较大的那条支路与根节点值之和,而当前最大值是(左支路最大值+右支路最大值+根节点值)。这样一直递归到树的根节点,此时遍历完成,输出最大值即可。

代码:

/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: int maxPathSum(TreeNode* root) { return code1(root); } int code1(TreeNode* root) { if(!root) return 0; int mvalue=INT_MIN; int tmp = 0; tmp = cal1(root,mvalue); return mvalue; } int cal1(TreeNode* root,int& mvalue) { if(!root) return 0; int left = max(0,cal1(root->left,mvalue)); int right = max(0,cal1(root->right,mvalue)); mvalue = max(mvalue,left+right+root->val); return max(left,right)+root->val; } };

96. Unique Binary Search Trees

题目描述:

Given n, how many structurally unique BST's (binary search trees) that store values 1 ... n?

Example:

Input: 3 Output: 5 Explanation: Given n = 3, there are a total of 5 unique BST's: 1 3 3 2 1 \ / / / \ \ 3 2 1 1 3 2 / / \ \ 2 1 2 3

思路:

找规律问题,后面的答案要用到前面的结果,因此建立一个数组nums保存节点个数为i时的答案。递推到nums[n]。

代码:

class Solution { public: int numTrees(int n) { return code1(n); } int code1(int n) {//找规律问题,求解后面的值要用到前面的结果 if(n<=2) return n; vector<int>nums(n+1,0); nums[0]=1;nums[1]=1;nums[2]=2; for(int i=3;i<=n;++i) { for(int left=i-1;left>=0;--left) {//不断挪动根节点的位置,依次统计每次挪动后的个数 int right = i-1-left; nums[i]+=nums[left]*nums[right]; } } return nums[n]; } };

62. Unique Paths

题目描述:

A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below).

The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below).

How many possible unique paths are there?

Above is a 7 x 3 grid. How many possible unique paths are there?

Note: m and n will be at most 100.

Example 1:

Input: m = 3, n = 2 Output: 3 Explanation: From the top-left corner, there are a total of 3 ways to reach the bottom-right corner: 1. Right -> Right -> Down 2. Right -> Down -> Right 3. Down -> Right -> Right

思路:

建立一个二维数组dp,dp[i]j[j]表示从起点到第i行j列的路径数目。则首先对于所有的第一行和第一列的格子,路径数都为1;对于剩下的格子,路径数等于这个格子左边一个格子的路径数和上边一个格子的路径数之和。

代码:

int find3(int m,int n) { vector<vector<int>>dp(m,vector<int>(n,1)); for(int i=1;i<m;++i) { for(int j=1;j<n;++j) { dp[i][j]=dp[i-1][j]+dp[i][j-1]; } } return dp[m-1][n-1]; }

64. Minimum Path Sum

题目描述:

Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.

Note: You can only move either down or right at any point in time.

Example:

Input: [   [1,3,1], [1,5,1], [4,2,1] ] Output: 7 Explanation: Because the path 1→3→1→1→1 minimizes the sum.

思路:

建立二维数组dp,dp[i][j]表示从左上角起点到第i,j个位置时的最小值。则对于dp的第一行和第一列来说,dp的值就是不断地累加。然后从第二行第二列开始,dp[i][j]=min(dp[i-1][j],dp[i][j-1])+g[i][j]

代码:

int find2(vector<vector<int>>& g) { if(g.empty()||g[0].empty()) return 0; int m=g.size();int n=g[0].size(); vector<vector<int>>dp(m,vector<int>(n,0)); dp[0][0]=g[0][0]; for(int i=1;i<m;++i) dp[i][0]=dp[i-1][0]+g[i][0]; for(int i=1;i<n;++i) dp[0][i]=dp[0][i-1]+g[0][i]; for(int i=1;i<m;++i) { for(int j=1;j<n;++j) { dp[i][j]=min(dp[i-1][j],dp[i][j-1])+g[i][j]; } } return dp[m-1][n-1]; }

39. Combination Sum

题目描述:

Given a set of candidate numbers (candidates) (without duplicates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.

The same repeated number may be chosen from candidates unlimited number of times.

Note:

All numbers (including target) will be positive integers.The solution set must not contain duplicate combinations.

Example 1:

Input: candidates = [2,3,6,7], target = 7, A solution set is: [ [7], [2,2,3] ]

思路:

方法一:回溯结合dfs的方法,每遍历到一个数字时,可以选择这个数字加到和里面去,也可以不选这个数字。当当前和与目标值相等时,将当前数列添加到答案数列中。一直遍历到最后一个数。

方法二:回溯法循环方式,需要注意的是递归调用的时候,因为所给的数列是排序的,因此递归调用的start index直接就是当前的index,而不用从0开始,这样避免了重复计算。

参考:

https://leetcode.com/problems/combination-sum/discuss/16496/Accepted-16ms-c%2B%2B-solution-use-backtracking-easy-understand.

代码:

class Solution { public: vector<vector<int>> combinationSum(vector<int>& candidates, int target) { //return solu1(candidates,target); return code3(candidates,target); } vector<vector<int>> solu1(vector<int>& can,int t) { int sum = 0; vector<vector<int>> ans; vector<int> combi; solu1_help2(ans,can,t,0,combi); return ans; } void solu1_help2(vector<vector<int>>& ans,vector<int>& can,int t,int index,vector<int> combi) { if(t==0) { ans.push_back(combi); return ; } if(index == can.size()||t<0) return ; combi.push_back(can[index]); solu1_help2(ans,can,t-can[index],index,combi); combi.pop_back(); solu1_help2(ans,can,t,index + 1,combi); } vector<vector<int>>code2(vector<int>& candi,int t) { vector<vector<int>> ans; vector<int>sum; code2_help(ans,sum,t,0,candi); return ans; } void code2_help(vector<vector<int>>& ans,vector<int> sum,int target,int start,vector<int>candi) {//code2会产生重复的结果,code3是正确的代码 for(int i=start;i<candi.size();++i) { if(target>0) { sum.push_back(candi[i]); code2_help(ans,sum,target-candi[i],i,candi); sum.pop_back(); } else if(target==0) { ans.push_back(sum); //sum.clear(); } } return; } vector<vector<int>> code3(vector<int>& candi,int t) { vector<vector<int>> ans; vector<int>sum; code3_help(ans,sum,t,0,candi); return ans; } void code3_help(vector<vector<int>>& ans,vector<int>& sum,int target,int start,vector<int>& candi) { if(target==0) { ans.push_back(sum); return ; } else if(target<0) return; for(int i=start;i<candi.size();++i) { sum.push_back(candi[i]); code3_help(ans,sum,target-candi[i],i,candi); sum.pop_back(); } return; } };

 

 


46. Permutations

题目描述:

Given a collection of distinct integers, return all possible permutations.

Example:

Input: [1,2,3] Output: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]

思路:

以第一个排列为基础,通过互换数字的位置得到全排列。互换的方式为循环并递归地互换,需要注意的是在递归退出后数列需要退回原状态,因此要把互换过的数字再换回来。

代码:

class Solution { public: vector<vector<int>> permute(vector<int>& nums) { return code1(nums); } vector<vector<int>> code1(vector<int>& nums) { vector<vector<int>>ans; vector<int>per; //help1(nums,ans,0,per); help2(nums,ans,0); return ans; } void help1(vector<int>& nums,vector<vector<int>>& ans,int i,vector<int>& per) { if(i==nums.size()) { ans.push_back(per); return; } per.push_back(nums[i]); for(int j=i+1;j<nums.size();++j) { help1(nums,ans,j,per); } per.pop_back(); return ; } void help2(vector<int>& nums,vector<vector<int>>& ans,int i) { if(i==nums.size()) { ans.push_back(nums); return; } for(int k=i;k<nums.size();++k) { swap(nums[i],nums[k]); help2(nums,ans,i+1); swap(nums[i],nums[k]); } return ; } void swap(int& a,int& b) { int tmp=a; a=b; b=tmp; return; } };

204. Count Primes

题目描述:

Count the number of prime numbers less than a non-negative number, n.

Example:

Input: 10 Output: 4 Explanation: There are 4 prime numbers less than 10, they are 2, 3, 5, 7.

思路:

判断一个数num是不是质数的方法是:在一个循环中,判断从2到sqrt(num)的数中有没有能被num整除的数,如果有则num不是质数;如果没有则num是质数。

如果依次判断到n的范围中的每个数是不是质数,这样的时间复杂度较高;

时间复杂度较低的方法:如果2是质数,则2*2,3*2,4*2等等一系列的数都不是质数了;当3是质数时,2*3,3*3,4*3等等一系列的数都不是质数。这样子将所有的非质数标出后,剩余的就是质数。

参考:

https://leetcode-cn.com/problems/count-primes/solution/ru-he-gao-xiao-pan-ding-shai-xuan-su-shu-by-labula/

代码:

class Solution { public: int countPrimes(int n) { return code2(n); } int code1(int n) { vector<bool>isPrime(n,true); for(int i=2;i<n;++i) { if(isPrime[i]) { for(int j=2*i;j<n;j+=i) isPrime[j]=false; } } int ans=0; for(int i=2;i<n;++i) { if(isPrime[i]) ans++; } return ans; } int code2(int n) { vector<bool>isPrime(n,true); for(int i=2;i*i<n;++i) {//优化,i只需要遍历到sqrt(n)即可 if(isPrime[i]) { for(int j=i*i;j<n;j+=i)//优化,其实j从2*i开始更直观,从i*i开始略高效 isPrime[j]=false; } } int ans=0; for(int i=2;i<n;++i) { if(isPrime[i]) ans++; } return ans; } };

 

最新回复(0)