[LEETCODE]算法进阶自练习15-16 贪心算法&回溯算法
15.贪心算法
简单
- 分发饼干 leetcode 455 贪心算法,需要先将孩子的胃口值和饼干的大小进行排序,从小到大,然后尽量将满足一个孩子胃口最小值的饼干分配给孩子即可。
int findContentChildren(vector<int>& g, vector<int>& s) {
sort(g.begin(),g.end());
sort(s.begin(),s.end());
int gsize = g.size(), ssize = s.size(), j = 0, count = 0;
while(count<gsize && j<ssize){
if(s[j] >= g[count]){
count++;
}
j++;
}
return count;
}
中等
- 买卖股票的最佳时机含手续费 714 动态规划解法,每次买股票的时候减掉手续费就可以了:
int maxProfit(vector<int>& prices, int fee) {
int psize = prices.size();
if(psize < 2) return 0;
vector<vector<int>> dp(psize, vector<int>(2,0));
dp[0][1] = -prices[0];
for(int i=1; i<psize; i++){
dp[i][0] = max(dp[i-1][0],dp[i-1][1] + prices[i] - fee);
dp[i][1] = max(dp[i-1][1],dp[i-1][0] - prices[i]);
}
return dp[psize-1][0];
}
动态规划压缩空间解法:
int maxProfit(vector<int>& prices, int fee) {
int psize = prices.size();
if(psize < 2) return 0;
int preCash = 0;
int preStock = -prices[0];
for(int i=1; i<psize; i++){
preCash = max(preCash, preStock + prices[i] - fee);
preStock = max(preStock, preCash - prices[i]);
}
return preCash;
}
16.回溯算法
简单
- 字母大小写全排列 leetcode 784
vector<char> getLetters(const char c){
vector<char> ret;
ret.push_back(c);
if(c >= 'a' && c <= 'z') ret.push_back(c + 'A'-'a');
if(c >= 'A' && c <= 'Z') ret.push_back(c + 'a' - 'A');
return ret;
}
vector<string> letterCasePermutation(string S) {
vector<string> ret;
int ssize = S.size();
if(ssize == 0) return ret;
for(int i=0; i<ssize; i++){
vector<string> tmp;
tmp.swap(ret);
int tsize = tmp.size();
vector<char> alphas = getLetters(S[i]);
int asize = alphas.size();
if(tsize == 0){
for(int j=0; j<asize; j++){
string t(1,alphas[j]);
ret.emplace_back(t);
}
continue;
}
for(int k=0; k<tsize; k++){
for(int j=0; j<asize; j++){
ret.emplace_back(tmp[k] + alphas[j]);
}
}
}
return ret;
}
中等
- 子集 leetcode 78 递归解法:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> ret(1);
int nsize = nums.size();
for(int i=0; i<nsize; i++){
int rsize = ret.size();
for(int j=0; j<rsize; j++){
vector<int> t = ret[j];
ret[j].emplace_back(nums[i]);
ret.emplace_back(t);
}
}
return ret;
}
回溯解法:
class Solution {
public:
void backtrack(vector<vector<int>>& ret, vector<int>& nums, int start, vector<int>& track){
ret.push_back(track); // 不继续往下选的情况
int nsize = nums.size();
for(int i=start; i<nsize; i++){
track.push_back(nums[i]); // 继续往下选的情况
backtrack(ret, nums, i+1, track);
track.pop_back();
}
}
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> ret;
vector<int> track;
backtrack(ret,nums,0,track);
return ret;
}
};
二进制位映射字典解法:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> ret;
int nsize = nums.size(), mask = 1 << nsize;
for(int i=0; i<mask; i++){
vector<int> t;
for(int j=0; j<nsize; j++){ // 判断每一位上是否为1,为1则将对应的数字push进去
if(i&(1<<j)) t.push_back(nums[j]);
}
ret.push_back(t);
}
return ret;
}
- 全排列 leetcode 46 回溯解法:
void backtrack(vector<vector<int>>& ret, vector<int>& nums, int first, int len){
if(first == len){ // 得到一个解,直接放入到ret中。
ret.emplace_back(nums);
return;
}
int nsize = nums.size();
for(int i=first; i<nsize; i++){
swap(nums[i], nums[first]); // 交换第i个和第first个的位置
backtrack(ret, nums, first+1, len); // 继续递归往后面走
swap(nums[i], nums[first]); // 将第i个和第first交换回来
}
}
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> ret;
backtrack(ret, nums, 0, (int)nums.size());
return ret;
}
- 组合 leetcode 77 回溯解法:
void backtrack(vector<vector<int>>& ret, vector<int>& track, int start, int n, int k){
if(track.size() == k){
ret.push_back(track);
return;
}
for(int i=start; i<=n - (k - track.size()) + 1; i++){
track.push_back(i);
backtrack(ret, track, i+1, n, k);
track.pop_back();
}
}
vector<vector<int>> combine(int n, int k) {
vector<vector<int>> ret; vector<int> track;
backtrack(ret, track, 1, n, k);
return ret;
}
字典序解法:
vector<vector<int>> combine(int n, int k) {
vector<int> t;
vector<vector<int>> ret;
for(int i=1; i<=k; i++) t.push_back(i);
t.push_back(n+1);
int j = 0;
while(j<k){
ret.emplace_back(t.begin(),t.begin()+k);
j = 0;
while(j<k && t[j]+1 == t[j+1]){
t[j] = j+1;
++j;
}
++t[j];
}
return ret;
}
困难
- N皇后 leetcode 51 基于集合的回溯解法:
vector<string> generateBoard(vector<int>& queen, int n){
vector<string> ret = vector<string>();
for(int i=0; i<n; i++){
string row = string(n,'.');
row[queen[i]] = 'Q';
ret.emplace_back(row);
}
return ret;
}
void backtrack(vector<vector<string>>& ret, vector<int> &queen, int n, int row, unordered_set<int>& col, unordered_set<int>& c1, unordered_set<int>& c2){
if(n == row){
ret.emplace_back(generateBoard(queen,n));
return;
}
else{
for(int i=0; i<n; i++){
int i1 = row-i, i2 = row+i;
if(col.find(i) != col.end()) continue;
if(c1.find(i1) != c1.end()) continue;
if(c2.find(i2) != c2.end()) continue;
queen[row] = i;
col.insert(i);
c1.insert(i1);
c2.insert(i2);
backtrack(ret, queen, n, row+1, col, c1, c2);
queen[row] = -1;
col.erase(i);
c1.erase(i1);
c2.erase(i2);
}
}
}
vector<vector<string>> solveNQueens(int n) {
vector<vector<string>> ret;
vector<int> queen(n,-1);
unordered_set<int> col, c1, c2;
backtrack(ret, queen, n, 0, col, c1, c2);
return ret;
}
基于位运算的回溯解法:
vector<string> generateBoard(vector<int>& queen, int n){
vector<string> ret = vector<string>();
for(int i=0; i<n; i++){
string row = string(n,'.');
row[queen[i]] = 'Q';
ret.emplace_back(row);
}
return ret;
}
void solve(vector<vector<string>> &ret, vector<int> &queen, int n, int row, int col, int c1, int c2) {
if (row == n) {
ret.emplace_back(generateBoard(queen, n));
} else {
int a_pos = ((1 << n) - 1) & (~(col | c1 | c2));
while (a_pos != 0) {
int pos = a_pos & (-a_pos);
a_pos = a_pos & (a_pos - 1);
int col = __builtin_ctz(pos);
queen[row] = col;
solve(ret, queen, n, row + 1, col | pos, (c1 | pos) >> 1, (c2 | pos) << 1);
queen[row] = -1;
}
}
}
vector<vector<string>> solveNQueens(int n) {
vector<vector<string>> ret;
vector<int> queen(n,-1);
unordered_set<int> col, c1, c2;
solve(ret, queen, n, 0, 0, 0, 0);
return ret;
}