leetcode solutions in cpp
借鉴了cs-notes中的顺序
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
int begin = 0;
int last = numbers.size() - 1;
while (begin < last) {
int sum = numbers[begin] + numbers[last];
if (sum == target) {
int res[] = {begin+1, last+1};
return vector<int>(res, res+2);
// return {begin+1, last+1};
} else if (sum > target) {
last--;
} else {
begin++;
}
}
return vector<int>(2, -1);
}
};
class Solution {
public:
bool judgeSquareSum(int c) {
int low = 0;
int high = (int)sqrt((double)c);
while(low <= high) {
long sum = (long)pow(low, 2) + (long)pow(high, 2);
if (sum == c) {
return true;
} else if (sum > c) {
high--;
} else {
low++;
}
}
return false;
}
};
class Solution {
public:
string reverseVowels(string s) {
string letter = "AEIOUaeiou";
int low = 0;
int high = s.length() - 1;
while (low < high) {
if (letter.find(s[low]) == letter.npos){
low++;
} else if (letter.find(s[high]) == letter.npos) {
high--;
} else {
swap(s[low], s[high]);
low++;
high--;
}
}
return s;
}
};
class Solution {
public:
bool validPalindrome(string s) {
int low = 0;
int high = s.length() - 1;
while (low < high) {
if (s[low] != s[high]) {
return isPalindrome(s, low+1, high) || isPalindrome(s, low, high - 1);
} else {
low++;
high--;
}
}
return true;
};
bool isPalindrome(string s, int low, int high) {
while (low < high) {
if (s[low++] != s[high--]) {
return false;
}
}
return true;
}
};
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int index1 = m - 1;
int index2 = n - 1;
int indexMerge = m + n - 1;
while (index1 >=0 || index2 >=0) {
if (index2 < 0) {
return;
} else if (index1 < 0) {
nums1[indexMerge--] = nums2[index2--];
} else {
nums1[indexMerge--] = nums1[index1] > nums2[index2] ? nums1[index1--] : nums2[index2--];
}
}
}
};
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if (head == nullptr) {
return false;
}
ListNode* pre = head;
ListNode* cur = head->next;
while (pre != nullptr && cur != nullptr && cur->next != nullptr) {
if (pre == cur) {
return true;
}
pre = pre->next;
cur = cur->next->next;
}
return false;
}
};
class Solution {
public:
string findLongestWord(string s, vector<string>& d) {
string ret = "";
for (int index = 0; index < d.size(); index++) {
int i, j;
if (d[index].length() < ret.length() ||(d[index].length() == ret.length() && d[index] > ret)) {
continue;
}
for (i = 0, j = 0; i < s.length() && j < d[index].length(); i++) {
if (s[i] == d[index][j]) {
j++;
}
}
if (j == d[index].length()) {
if (j > ret.length() || (j == ret.length() && d[index] < ret)) {
ret = d[index];
}
}
}
return ret;
}
};
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
priority_queue<int> max_heap;
if (nums.empty() || nums.size() < k) {
return -1;
}
k = nums.size() - k + 1;
for (int i = 0; i < k; i++) {
max_heap.push(nums[i]);
}
for (int i = k; i < nums.size(); i++) {
if (max_heap.top() > nums[i]) {
max_heap.pop();
max_heap.push(nums[i]);
}
}
return max_heap.top();
}
};
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
vector<int> ret;
if (nums.empty() || nums.size() < k) {
return ret;
}
int n = nums.size();
map<int,int> frequency;
for (int i=0; i < n; i++) {
if (frequency.find(nums[i]) == frequency.end()) {
frequency[nums[i]] = 1;
} else{
frequency[nums[i]] = frequency[nums[i]] + 1;
}
}
vector<vector<int>> buckets(n+1);
for (map<int, int>::iterator i=frequency.begin(); i!=frequency.end();) {
int key = i->first;
int frequency = i->second;
buckets[frequency].push_back(key);
i++;
}
int i = 0;
int j = buckets.size();
while(i < k) {
while(buckets[--j].size()==0);
for (int h = 0; h < buckets[j].size(); h++) {
if (i == k) {
break;
}
ret.push_back(buckets[j][h]);
i++;
}
}
return ret;
}
};
class Solution {
public:
string frequencySort(string s) {
int len = s.length();
if (len <= 0) {
return s;
}
string ret;
map<char,int> frequency;
for (int i = 0; i < len; i++) {
char c = s.at(i);
if (frequency.find(c) == frequency.end()) {
frequency[c] = 1;
} else {
frequency[c] += 1;
}
}
vector<vector<char>> buckets(len+1);
for(std::map<char, int> :: iterator it = frequency.begin(); it != frequency.end();) {
char c = it->first;
int cur_frequency = it->second;
buckets[cur_frequency].push_back(c);
it++;
}
for(int i = len; i > -1; i--) {
if (!buckets[i].empty()) {
for (auto c : buckets[i]) {
for (int j = 0; j < i; j++) {
ret.push_back(c);
}
}
}
}
return ret;
}
};
class Solution {
public:
void sortColors(vector<int>& nums) {
vector<int> frequency(3);
for (int i = 0; i < nums.size(); i++) {
frequency[nums[i]] ++;
}
int idx = 0;
for (int i = 0; i < frequency.size(); i++) {
for (int j = 0; j < frequency[i]; j++) {
nums[idx++] = i;
}
}
}
};
// 第二种解法
class Solution {
public:
void sortColors(vector<int>& nums) {
int zeros = -1;
int one = 0;
int two = nums.size();
while (one < two) {
if (nums[one] == 0) {
swap(nums, ++zeros, one++);
} else if (nums[one] == 2) {
swap(nums, --two, one);
} else {
one++;
}
}
}
void swap(vector<int>& nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
};
class Solution {
public:
int climbStairs(int n) {
//
if (n <= 2) {
return n;
}
vector<int> dp(n+1);
dp[0] = 0;
dp[1] = 1;
dp[2] = 2;
for (int i = 3; i < dp.size(); i++) {
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
};
class Solution {
public:
int rob(vector<int>& nums) {
// dp[i] : 在第i号房屋可以偷到的最高金额; 状态转移:dp[i] = max(dp[i-2]+nums[i], dp[i-1])
// 初始状态: dp[0] = nums[0]; dp[1] = max(dp[0], nums[1])
if (nums.size() <= 0) {
return 0;
} else if (nums.size() == 1) {
return nums[0];
} else if (nums.size() == 2) {
return max(nums[0], nums[1]);
} else {
vector<int> dp(nums.size());
dp[0] = nums[0];
dp[1] = max(nums[0], nums[1]);
for (int i = 2; i < dp.size(); i++) {
dp[i] = max(dp[i-2]+nums[i], dp[i-1]);
}
int n = dp.size();
return max(dp[n-1], dp[n-2]);
}
}
int max(int a, int b) {
return (a > b) ? a : b;
}
};
// 解法2:
class Solution {
public:
int rob(vector<int>& nums) {
if (nums.size() <= 0) {
return 0;
} else if (nums.size() == 1) {
return nums[0];
} else if (nums.size() == 2) {
return max(nums[0], nums[1]);
} else {
int pre2 = nums[0];
int pre1 = max(nums[0], nums[1]);
for (int i = 2; i < nums.size(); i++) {
int cur = max(pre2 + nums[i], pre1);
pre2 = pre1;
pre1 = cur;
}
return pre1;
}
}
int max(int i, int j) {
return (i > j) ? i : j;
}
};
class Solution {
public:
int rob(vector<int>& nums) {
if (nums.size() == 0) {
return 0;
} else if (nums.size() == 1) {
return nums[0];
} else if (nums.size() == 2) {
return max(nums[0], nums[1]);
} else {
int n = nums.size();
return max(rob(nums, 0, n - 1), rob(nums, 1, n));
}
}
int rob(vector<int>& nums, int begin, int end) {
int pre1 = 0;
int pre2 = 0;
for (int i = begin; i < end; i++) {
int cur = max(pre2 + nums[i], pre1);
pre2 = pre1;
pre1 = cur;
}
return pre1;
}
int max(int i, int j) {
return (i > j) ? i : j;
}
};
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
//状态 : dp[i][j], 表示 text1 第 i 位与 text2 第 j 位的公共子序列长度;
// 状态转移 :若 text1[i] == text2[j], 则 dp[i][j] = dp[i-1][j-1] + 1 ; 否则, dp[i][j] = max(dp[i][j-1], dp[i-1][j])
// 使用到 dp[i-1][j-1], dp[i-1][j], dp[i][j-1] 等左上,上,左三个过往值,用一个值存储 dp[i-1][j-1],即可
int n = text1.size();
int m = text2.size();
if (n == 0 || m == 0) {
return 0;
}
/*
vector<vector <int>> dp(n + 1, vector<int>(m + 1, 0));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (text1[i-1] == text2[j-1]) {
dp[i][j] = dp[i-1][j-1] + 1;
}
else{
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
}
}
return dp[n][m];
*/
vector<int> dp(m+1, 0);
int last, temp; // last : 存储 dp[i-1][j-1]; temp : 暂时存储
for (int i = 1; i <= n; i++, last=0) {
for (int j = 1; j <= m; j++) {
temp = dp[j]; // 存储当前 dp[i][j], 因为即将修改 dp[i][j], 而下一次循环dp[i][j+1]时,要用到当前的 dp[i][j]
if (text1[i-1] == text2[j-1]) {
dp[j] = last + 1;
}
else{
dp[j] = max(dp[j], dp[j-1]);
}
last = temp;
}
}
return dp[m];
}
int max(int a, int b){
return a > b ? a : b;
}
};
class Solution {
public:
bool canPartition(vector<int>& nums) {
// 状态:dp[i][j], 表示前i个数中, 是否有和恰好为j的子集;
/*
状态转移:
1. 不取第 i 个数: dp[i][j] = dp[i-1][j]; 当前 i - 1个数已有和为 j 的子集时,
2. 取第 i 个数: dp[i][j] = dp[i-1][j-nums[i]]; 当前 i-1 个数已有和为 j - nums[i] 的子集时,加上nums[i]即可得到和为 j 的子集;
3. 特殊情况,可置于初始化状态中, j == nums[i]; 若 j == nums[i] , 表示nums[i]自身构成一个和为 j 的子集;若置于初始化状态中,则表示 dp[i][0] = true; 因为 j-nums[i] = 0;
*/
// 看作体积为 sum/2 的 0-1背包问题,这里每个体积的背包要恰好装满,所以每个背包的初始状态应设为无穷,保证每个背包的状态都是由其初始状态转换而来,详见《背包九讲》第6页。先行判断,总和是否为2的倍数,否则不能分割。
int sum = computeSum(nums);
if (sum % 2 != 0) {
return false;
}
sum /= 2;
int n = nums.size() + 1;
int m = sum + 1;
/* 二维数组解法
vector<vector<bool>> dp(n, vector<bool>(m, false));
for (int i = 1; i < n; i++) {
for (int j = 1; j < m; j++) {
dp[i][j] = dp[i-1][j];
if (j == nums[i-1]) {
dp[i][j] = true;
//continue;
} else if (j > nums[i-1]) {// j 小于 nums[i-1],肯定不取nums[i-1]。 这里的nums[i-1]就是第 i 个数,因为循环中的 i 从1开始到nums.size(); 而 nums数组下标从0开始;
dp[i][j] = dp[i-1][j] || dp[i-1][j - nums[i-1]]; // j 大于 nums[i-1], 则可取可不取 nums[i-1]
}
}
}
return dp[n-1][m-1];
*/
/*
一维数组解法:从二维数组中可看到,第 i 行只与第 i-1 行的值相关,可用m列一维数组存储并更新,但是要注意的是,状态转移变为:
1. 不取第i个数: dp[j] = dp[j]
2. j == nums[i] : dp[0] = true
3. 取第 i 个数: dp[j] = dp[j-nums[i]]
状态转移中, dp[i][j] = dp[i-1][j-nums[i]] || dp[i-1][j]; 而 j-nums[i] 比 j 小,若从前往后更新,则会先更新 dp[i][j-nums[i]],此时已经丢失了 dp[i-1][j-nums[i]] 的信息
*/
vector<bool> dp(m, false);
dp[0] = true; // dp[i][0] 表示了两种含义, 一种是 j == num; 一种是 容积为 0;
for (int i = 0; i < n-1; i++) {
for (int j = m; j >= nums[i]; j--) {
dp[j] = dp[j] || dp[j-nums[i]];
}
}
return dp[m-1];
}
int computeSum(vector<int>& nums){
int sum = 0;
for (auto num : nums){
sum += num;
}
return sum;
}
};
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int S) {
/*
设所有元素和为 M , 正元素和为 P, 负元素和为 N;
则有: P + N = S; P - N = M; ==> 2P = S + M ==> P = (S+M)/2
则问题等价为求 nums 中和为 P 的子集数,一共有多少个子集的和为 P
*/
int sum = computeSum(nums);
if (sum < S) {
return 0;
}
sum = sum + S;
if (sum % 2 != 0) {
return 0;
}
sum /= 2;
/* 枚举,对每个 num, 计算 +num, -num 的个数
return dfs(nums, 0, 0, 0, S);
*/
/*
状态 : dp[i][j] : 前 i 个数中, 和为 j 的子集个数
状态转移:
1. nums[i] 不取: dp[i][j] = dp[i-1][j]
2. nums[i] 取: dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i]]
vector<int> dp(sum+1, 0);
dp[0] = 1; // 初始状态,容积为0时,子集为0,是为一种取法
for (auto num : nums){
for (int j = sum; j >= num; j--) {
dp[j] = dp[j] + dp[j - num];
}
}
return dp[sum];
*/
}
int computeSum(vector<int>& nums){
int sum = 0;
for (auto num : nums){
sum += num;
}
return sum;
}
int dfs(vector<int>& nums, int i, int sum, int count, int target) {
if (i == nums.size()) {
if (sum == target) {
return ++count;
}
return count;
}
else {
return dfs(nums, i+1, sum + nums[i], count, target) + dfs(nums, i+1, sum - nums[i], count, target);
}
}
};
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
// 2维的 0-1背包问题,有两个限制: m, n; 每个物品的价值均为 1
// 状态 : dp[i][j] : 最多使用 i 个 0 和 j 个 1 的情况下,
// 可以拼出的最大最多字符串个数
// 状态转移: dp[i][j] = max(dp[i-1][j-1], dp[i-zero(str)][j-one(str)] + 1)
// 不取当前字符串 str : dp[i-1][j-1]
// 取当前字符串 str : dp[i - zero(str)][j - one(str)] + 1
if (strs.size() == 0) {
return 0;
}
vector<vector<int>> dp(m+1, vector<int>(n+1, 0));
// 困扰多年的初始化问题, 这里并不需要保证 m 跟 n 恰好用完,所以dp[0-m][0-n]都初始化为 dp[0][0] 的状态,即 0
for (auto str : strs) {
int zeros = 0;
int ones = 0;
for (int k = 0; k < str.length(); k++) {
if (str[k] == '0') {
zeros++;
}
else if (str[k] == '1') {
ones++;
}
}
for (int i = m; i >= zeros; i--) {
for (int j = n; j >= ones; j--) {
dp[i][j] = max(dp[i][j], dp[i - zeros][j - ones] + 1);
}
}
}
return dp[m][n];
}
int max(int a, int b) {
return a > b ? a : b;
}
};
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
//背包问题,这里是完全背包,每个硬币可选无数次,注意的是要取的最小次数
// 初始化有所不同,dp[0][0]=0 表示需要 0 枚硬币, 但 j == nums[i] 时,需要1枚硬币;所以要将 j == nums[i] 与 dp[0][0] 分别计算
// amount 是体积, coins 是物品, 物品代价是 硬币面额, 价值是 1 (个数),
// 这里要注意的是取价值最小而非价值最大
if (coins.size() == 0) {
return -1;
}
if (amount == 0) { // amount 为0时,输出为0,我也不懂
return 0;
}
vector<int> dp(amount + 1, -1);
dp[0] = 1;
for (auto coin : coins) {
for (int j = coin; j <= amount; j++) {
if (j == coin) {
dp[j] = 1;
}
else if (dp[j-coin] != -1) {// dp[j-coin] 已更新,表明有硬币组合能够拼成 j-coin的总金额,那么加上当前的coin,一定可以拼成 j 的总金额
if (dp[j] == -1) {
dp[j] = dp[j-coin] + 1; //当前dp[j] 尚未从初始值 -1 开始更新
}
else {// d[j] != -1, 表示 dp[j] 已经包含了某种组合,这时才能取最小的组合
dp[j] = min(dp[j], dp[j-coin] + 1) ;
}
}
}
}
return dp[amount];
}
int min(int a, int b) {
return a < b ? a : b;
}
};
class Solution {
public:
int change(int amount, vector<int>& coins) {
if (amount == 0) {
return 1;
}
if (coins.size() == 0) {
return 0;
}
vector<int> dp(amount+1, 0);
dp[0] = 1;
for (auto coin : coins){
for (int j = coin; j <= amount; j++) {
dp[j] = dp[j] + dp[j-coin]; // 取或不取的总组合数
}
}
return dp[amount];
}
int max(int a, int b) {
return a > b ? a : b;
}
};
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
if (s.length() == 0) {
return true;
}
if (wordDict.size() == 0) {
return false;
}
// 状态 : dp[j] : 前 j 个字符是否可以用字典的单词组成
// 状态转移 :dp[j] = dp[j] || dp[j - len]; 不取当前单词/取当前单词
vector<bool> dp(s.length() + 1, false);
dp[0] = true;
// 完全背包问题,为保证wordDict中放入的顺序问题,应该将循环顺序倒置,先循环 容积,再循环 物品, 物品: wordDict, 容积 : s
for (int j = 1; j < s.length()+ 1; j++) { // 完全背包问题,j 从头开始遍历
for (auto word : wordDict) {
int len = word.length();
if (len <= j) {
if (word.compare(s.substr(j - len, len)) == 0) { // substr 的参数是 (起点,长度), 与 Java的 (起点,终点) 不同
dp[j] = dp[j] || dp[j - len];
}
}
}
}
return dp[s.length()];
}
};
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
// 带顺序的完全背包问题,循环顺序不一样,先 target,再 nums
if (nums.size() == 0) {
return 0;
}
if (target == 0) {
return 1;
}
int gd = nums[0];
for (int num : nums) {
gd = gcd(gd, num);
}
if (target % gd != 0) {
return 0;
}
// vector<int> dp;
// int init = 1;
vector<unsigned long long> dp;
unsigned long long init = 1;
dp.push_back(init);
for (int i = 1; i <= target; i++) {
init = 0;
dp.push_back(init);
}
// 找出最大公约数,若 target 不能被最大公约数整除, 则返回0
// 找最大公约数用辗转相除法
// 将 num 排序
sort(nums.begin(), nums.end());
for (int j = 1; j <= target; j++) {
for (auto num : nums) {
if (j >= num) {
dp[j] += dp[j - num]; // 不取num: dp[j], 取num : dp[j-num]
}
}
}
return dp[target];
}
int gcd(int a, int b) {
int temp = 0;
while (a % b) {
temp = b;
b = a % b;
a = temp;
}
return b;
}
};
class Solution {
public:
int minDistance(string word1, string word2) {
// 转化为最长公共子串的问题,
// 极端情况, word1 与 word2 没有公共子串, 所需最小步数是 m + n
// 若有公共子串长度为 cls, 则word1 与 word2 都可以减少 cls 次操作
// 所以所需最小步数是 m + n - 2*cls
// 最长公共子串问题 :
// 状态 : dp[i][j] : word1 的前 i 位 与 word2 的前 j 位的最长公共子串长度
// 状态转移 : 若 word1[i] == word2[j], 则dp[i][j] = dp[i-1][j-1] + 1;
// 若 word1[i] != word2[j], 则 dp[i][j] = max(dp[i-1][j], dp[i][j-1])
// 初始状态 : dp[0][0] : 长度为0, 最长公共子串为0, 为保证 dp[i][j]从 dp[0][0] 转移过来,所以所有的dp[i][j] 的初始状态都是 dp[0][0], 详情看背包九讲 第 6 页;
// 因为只需用到 i-1 的状态,所以可以用一行数组,用一个变量记录 dp[i-1][j-1]即可
if (word1.size() == 0 || word2.size() == 0) {
return word1.size() + word2.size();
}
int dp_ij = 0;
int m = word1.size(), n = word2.size();
vector<int> dp(n+1, 0);
for (int i = 0; i < m; i++) {
dp_ij = dp[0];
for (int j = 1; j <= n; j++) {
int temp = dp[j];
if (word1[i] == word2[j-1]) {
dp[j] = dp_ij + 1; // dp[i-1][j-1] + 1
}
else {
dp[j] = max(dp[j], dp[j-1]); // dp[i-1][j], dp[i][j-1]
}
dp_ij = temp;
}
}
int cls = dp[n];
return m + n - 2 * cls;
}
};
class Solution {
public:
int minDistance(string word1, string word2) {
// dp[i][j] : word1[i]与word2[j]的最少操作数
// 状态转移: 若 word1[i] == word2[j]; 则无需做任何操作,
// 此时, dp[i][j] = dp[i-1][j-1];
// 若 word1[i] != word2[j], 则可以选择三种操作 : 插入,删除,替换
// 替换 : 使word1[i] == word2[j], 则dp[i][j] = dp[i-1][j-1] + 1
// 插入 : 插入一个字符,使word1[i+1] == word2[j], 此时word1[i]要与word2[j-1]之前的字符做比较,则dp[i][j] = dp[i][j-1] + 1
// 删除 : 将 word1[i]删掉,则此时要比较 word1[i-1] 与 word2[j]之间的关系, dp[i][j] = dp[i-1][j] + 1
int n = word1.size();
int m = word2.size();
vector<vector<int>> dp(n+1, vector<int>(m+1, 0));
for (int i = 1; i < n+1; i++) {
dp[i][0] = dp[i-1][0] + 1;
}
for (int j = 1; j < m+1; j++) {
dp[0][j] = dp[0][j-1] + 1;
}
for (int i = 1; i < n+1; i++) {
for (int j = 1; j < m+1; j++) {
if (word1[i-1] == word2[j-1]) {
dp[i][j] = dp[i-1][j-1];
}else {
dp[i][j] = min(dp[i-1][j-1], min(dp[i-1][j], dp[i][j-1])) + 1; // 替换,删除, 插入
}
}
}
return dp[n][m];
}
};
#include <limits>
class Solution {
public:
int minSteps(int n) {
// 暴力动归
// 状态 : dp[i] : 打印出 i 个 A 的最少次数
// 初始状态 : dp[1] = 0; 因为初始只有一个字符 'A';
// 状态转移 : dp[i] = dp[j] + i / j; 假设对第 j 次的字符进行复制,那么每次粘贴都增加 j 个字符;复制 1 次 + 粘贴 (i-j)/j 次,所以从 j 到 i 要增加 i / j 次操作;
if (n == 1) {
return 0;
}
vector<int> dp(n+1, INT_MAX);
dp[0] = 0;
dp[1] = 0;
for (int i = 2; i <= n; i++) {
for (int j = 1; j < i; j++) {
if (i % j == 0) {
dp[i] = min(dp[i], dp[j] + i / j);
}
}
}
return dp[n];
}
};