0%

698. 划分为k个相等的子集

参考经典回溯算法:集合划分问题「重要更新 🔥🔥🔥」

球来选择桶

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Solution {
public:
bool canPartitionKSubsets(vector<int>& nums, int k) {
auto sum = accumulate(nums.begin(), nums.end(), 0);
if(sum % k != 0)
return false;
sum /= k;
// 排列,从大到小排,是为了让回溯过程更早地触发剪枝条件,越早剪枝,剪掉的小枝就越多
sort(nums.begin(), nums.end(), greater<int>());
vector<int> buckets(k);
return helper(nums, buckets, 0, sum);
}

private:
bool helper(vector<int> &nums, vector<int> &buckets, int index, int target) {
// 当所有球都被用完了,说明所有球都各个放入桶中,并且由于bucket + nums[index] > target约束地存在,说明所有桶的和都为target,所以返回true
if(index == nums.size())
return true;
// 下标为index的小球选择自己要去哪个桶。对桶进行遍历。如果一个桶都容不下他了,就得递归返回了,让前面去修复。
for(int i = 0; i < buckets.size(); ++i) {
auto &bucket = buckets[i];
// 如果放入后的和>target了,说明不能放
if(bucket + nums[index] > target)
continue;
// 如果前一个桶的和和现在的桶一样,由于桶本身是没有顺序标号的,所以选择前一个桶和选择当前桶的结果一样,所以为了减少无用功,跳过。
if(i > 0 && buckets[i - 1] == bucket)
continue;
// 与上述原因相同,第一个球始终让他进第一个桶,没必要重复操作。
if(index == 0 && i > 0)
break;
// 球入桶
bucket += nums[index];
// 让下一个球选
if(helper(nums, buckets, index + 1, target))
return true;
// 回溯,球出桶
bucket -= nums[index];
}
return false;
}
};

桶来选择球

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class Solution {
public:
bool canPartitionKSubsets(vector<int>& nums, int k) {
auto sum = accumulate(nums.begin(), nums.end(), 0);
if(sum % k != 0)
return false;
sum /= k;
// 从大到小排,更快剪枝
sort(nums.begin(), nums.end(), greater<int>());
vector<int> buckets(k);
return helper(nums, buckets, 0, 0, sum, k - 1);
}

private:
// 来记录一个桶被装满时候,接下来的路能不能到达。防止重复走这种状态
unordered_map<int, bool> memo;
bool helper(vector<int> &nums, vector<int> &buckets, int visited, int index, int target, int cur_level) {
// 当桶全都装了,那就说明能装
if(cur_level < 0)
return true;
// 当前桶被装满了
if(buckets[cur_level] == target) {
// 看看当前桶在这种选择下被装满的情况下,能否
auto r = helper(nums, buckets, visited, 0, target, cur_level - 1);
// 记录当前状态,免得别的桶也走到了
memo.emplace(visited, r);
return r;
}
// 如果当前状态走过,由于桶是无序的所以直接返回
if(memo.count(visited))
return memo.at(visited);
for(int i = index; i < nums.size(); ++i) {
// 球用过了,pass
if((1 << i) & visited)
continue;
// 放不下这个球,pass
if(buckets[cur_level] + nums[i] > target)
continue;
buckets[cur_level] += nums[i];
visited ^= 1 << i;
// 让下一个球来选,同时也会根据情况进行下一个桶的判断
if(helper(nums, buckets, visited, i + 1, target, cur_level))
return true;
buckets[cur_level] -= nums[i];
visited ^= 1 << i;
// 如果下一个球和当前球数值相同,由于当前球已经返回了,且包含了下一个球能包含的所有情况,所以直接跳过(需要在有序的前提下)
if(i < nums.size() - 1 && nums[i] == nums[i + 1])
++i;
}
return false;
}

};

450. 删除二叉搜索树中的节点

只改值不改节点引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/**
* 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:
TreeNode* deleteNode(TreeNode* root, int key) {
if(!root)
return nullptr;
if(key > root->val)
root->right = deleteNode(root->right, key);
else if(key < root->val)
root->left = deleteNode(root->left, key);
else
{
if(!root->left && !root->right)
{
delete root;
return nullptr;
}
if(root->right)
{
root->val = rightMin(root->right);
root->right = deleteNode(root->right, root->val);
}else
{
root->val = leftMax(root->left);
root->left = deleteNode(root->left, root->val);
}
}
return root;
}
private:
int rightMin(TreeNode* node)
{
while(node->left)
node = node->left;
return node->val;
}

int leftMax(TreeNode* node)
{
while(node->right)
node = node->right;
return node->val;
}
};

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int begin{0}, end{0};
int ret{0};
int n = s.size();
unordered_map<char, int> memo;
while(end < n)
{
begin = max(begin, memo[s[end]]); // 我上一步走的这个字母,你要跳到最近的不和我重复的那个位置了,每走一个end都会尝试更新一次begin的位置
ret = max(ret, end - begin + 1);
memo[s[end]] = end + 1; // 记录为+1,表示是当前位置之后的,不然begin跳过来还是那个字母
++end;
}
return ret;
}
};

224. 基本计算器

递归处理括号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class Solution {
public:
int calculate(string s) {
int i = 0;
return helper(s, i);
}
private:
int helper(string& s, int& i) {
stack<int> stk;
int num = 0;
char preop = '+';
int n = s.size();
for(; i <= n; ++i)
{
auto ch = s[i];
if(ch == '(')
{
num = helper(s, ++i);
continue;
}
if(ch == ' ')
continue;
if(i == n || ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == ')')
{
switch(preop)
{
case '-':
stk.push(-num);
break;
case '+':
stk.push(num);
}
if(ch == ')')
break;
num = 0;
preop = ch;
}else
num = num * 10 + (ch - '0');
}
int ret = 0;
while(!stk.empty())
{
ret += stk.top();
stk.pop();
}
return ret;
}
};

没有*/这些,只需要处理符号就可以了,主要处理括号前符号会让括号内符号都变号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Solution {
public:
int calculate(string s) {
int ret{ 0 };
int i{ 0 };
int n = s.size();
int sign{ 1 };
stack<int> stk;
stk.push(1);
while(i < n)
{
if(s[i] == ' ')
++i;
else if(s[i] == '+')
{
++i;
sign = stk.top();
}else if(s[i] == '-')
{
++i;
sign = stk.top() * -1;
}else if(s[i] == '(')
{
stk.push(sign);
++i;
}else if(s[i] == ')')
{
stk.pop();
++i;
}else
{
long cur{ 0 };
while(i < n && s[i] <= '9' && s[i] >= '0')
cur = cur * 10 + (s[i++] - '0');
ret += sign * cur;
}
}
return ret;
}
};

518. 零钱兑换 II

这里最重要的是将coin循环放在外层

因为放在外层就能保证使用coin的顺序固定

就不会出现3=1+2然后3=2+1的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public:
int change(int amount, vector<int>& coins) {
vector<int> dp(amount + 1);
dp[0] = 1;
unordered_set<int> set;
for(auto coin : coins)
set.insert(coin);
for(auto coin : coins)
for(int i = coin; i <= amount; ++i)
dp[i] += dp[i - coin];
return dp.back();
}
};

402. 移掉 K 位数字

12342的话就删除4,也就是说当出现一个逆序对时候,删除前面那个可以获取到更小的数字,用栈维持单调栈,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution {
public:
string removeKdigits(string num, int k) {
vector<char> s;
for(auto ch : num)
{
while(k && !s.empty() && s.back() > ch)
{
s.pop_back();
--k;
}
s.push_back(ch);
}
while(k--)
s.pop_back();
string ret;
bool leadingZero{ true };
for(auto ch : s)
{
if(leadingZero && ch == '0')
continue;
if(ch != '0')
leadingZero = false;
ret += ch;
}
return ret.empty() ? "0" : ret;
}
};

460. LFU 缓存

自己维护一个双向链表

记录每个count的值的最后一位是哪个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
class LFUCache {
public:
LFUCache(int capacity) : head(new Node(-1, 0, -1)), tail(new Node(-1, 0, INT_MAX)), size(0), capacity(capacity) {
head->next = tail;
tail->prev = head;
}

int get(int key) {
if(capacity == 0)
return -1;
if(!map.count(key))
return -1;
auto ret = map[key]->value;
int oldc = map[key]->count++;
if(oldc != map[key]->prev->count && oldc != map[key]->next->count)
location.erase(oldc);
else if(oldc == map[key]->prev->count && oldc != map[key]->next->count)
location[oldc] = map[key]->prev;
int c = oldc + 1;
if(c >= map[key]->next->count)
moveToNext(location[map[key]->next->count], map[key]);
if(c == map[key]->next->count)
moveToNext(location[c], map[key]);
location[c] = map[key];
return ret;
}

void put(int key, int value) {
if(capacity == 0)
return;
if(!map.count(key))
{
if(++size > capacity)
{
--size;
removeHead();
}
addToHead(key, value);
}else
{
map[key]->value = value;
int oldc = map[key]->count++;
if(oldc != map[key]->prev->count && oldc != map[key]->next->count)
location.erase(oldc);
else if(oldc == map[key]->prev->count && oldc != map[key]->next->count)
location[oldc] = map[key]->prev;
}
auto c = map[key]->count;
if(c >= map[key]->next->count)
moveToNext(location[map[key]->next->count], map[key]);
if(c == map[key]->next->count)
moveToNext(location[c], map[key]);
location[c] = map[key];
}
private:
struct Node
{
int key;
int count;
int value;
Node* prev;
Node* next;
Node(int key, int value, int count = 1) : key(key), value(value), count(count) {}
};
Node *head, *tail;
int size, capacity;
unordered_map<int, Node*> map;
unordered_map<int, Node*> location;
private:
void exchangeNearbyNodes(Node* n1, Node* n2)
{
n1->next = n2->next;
n2->next->prev = n1;
n2->next = n1;
n2->prev = n1->prev;
n1->prev->next = n2;
n1->prev = n2;
}
void removeHead()
{
if(head->next == tail)
return;
auto node = head->next;
head->next->next->prev = head;
head->next = head->next->next;
map.erase(node->key);
if(node->next->count != node->count)
location.erase(node->count);
delete node;
}
void addToHead(int key, int value)
{
auto node = new Node(key, value);
node->next = head->next;
head->next->prev = node;
head->next = node;
node->prev = head;
map.emplace(key, node);
}
void isolatedNode(Node* node)
{
node->prev->next = node->next;
node->next->prev = node->prev;
}
void moveToNext(Node* prev, Node* node)
{
isolatedNode(node);
node->next = prev->next;
node->prev = prev;
prev->next->prev = node;
prev->next = node;
}
};

/**
* Your LFUCache object will be instantiated and called as such:
* LFUCache* obj = new LFUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/

get: O(1)

put: O(1)

使用红黑树(c++中的set)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
class LFUCache {
public:
LFUCache(int capacity) : time(0), capacity(capacity) {

}

int get(int key) {
if(capacity == 0)
return -1;
if(!map.count(key))
return -1;
set.erase(map[key]);
map[key].time = ++time;
++map[key].count;
set.insert(map[key]);
return map[key].value;
}

void put(int key, int value) {
if(capacity == 0)
return;
if(map.count(key))
{
set.erase(map[key]);
map[key].time = ++time;
++map[key].count;
map[key].value = value;
set.insert(map[key]);
}else
{
if(map.size() == capacity)
{
auto node = set.begin();
map.erase(node->key);
set.erase(node);
}
Node node{key, value, 1, ++time};
map.emplace(key, node);
set.insert(std::move(node));
}
}
private:
struct Node
{
int key;
int value;
int count;
int time;
Node() = default;
Node(int key, int value, int count, int time) : key(key), value(value), count(count), time(time) {}
bool operator < (const Node& rhs) const
{
return count == rhs.count ? time < rhs.time : count < rhs.count;
}
};
int time;
unordered_map<int, Node> map;
set<Node> set;
int capacity;
};

/**
* Your LFUCache object will be instantiated and called as such:
* LFUCache* obj = new LFUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/

get: O(logn)

put: O(logn)

使用双哈希表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
class LFUCache {
public:
LFUCache(int capacity) : capacity(capacity), minFreq(1) {

}

int get(int key) {
if(capacity == 0)
return -1;
if(!map.count(key))
return -1;
auto it = map[key];
auto count = it->count;
auto value = it->value;
freq[count].erase(it);
if(freq[count].size() == 0)
{
freq.erase(count);
if(count == minFreq)
++minFreq;
}
++count;
freq[count].push_front(Node(key, value, count));
map[key] = freq[count].begin();
return value;
}

void put(int key, int value) {
if(capacity == 0)
return;
if(!map.count(key))
{
if(map.size() == capacity)
{
auto it = --freq[minFreq].end(); // 不是随机迭代器,不能用-1
map.erase(it->key);
freq[minFreq].pop_back();
if(freq[minFreq].size() == 0)
freq.erase(minFreq);
}
freq[1].push_front(Node(key, value, 1));
map.emplace(key, freq[1].begin());
minFreq = 1;
}else
{
auto it = map[key];
auto count = it->count;
freq[count].erase(it);
if(freq[count].size() == 0)
{
freq.erase(count);
if(count == minFreq)
++minFreq;
}
++count;
freq[count].push_front(Node(key, value, count));
map[key] = freq[count].begin();
}
}
private:
struct Node
{
int key;
int value;
int count;
Node(int key, int value, int count) : key(key), value(value), count(count) {}
};
unordered_map<int, list<Node>::iterator> map;
unordered_map<int, list<Node>> freq;
int capacity;
int minFreq;
};

/**
* Your LFUCache object will be instantiated and called as such:
* LFUCache* obj = new LFUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/

498. 对角线遍历

单行单列的有点烦,没来得及转弯就结束了

模拟大法好

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Solution {
public:
vector<int> findDiagonalOrder(vector<vector<int>>& mat) {
int m = mat.size(), n = mat[0].size();
if(m == 1)
return mat[0];
if(n == 1)
{
vector<int> v;
for(auto v1 : mat)
v.push_back(v1[0]);
return v;
}
int i{ 0 }, j{ 0 };
vector<int> ret;
int dir{ 1 };
int d[2][2]{ { 1, -1 }, {-1, 1} };
while(i < m && j < n)
{
ret.push_back(mat[i][j]);
if(i == 0 || i == m - 1 || j == 0 || j == n - 1)
{
if(j == n - 1)
++i;
else if(i == 0)
++j;
else if(i == m - 1)
++j;
else if(j == 0)
++i;
ret.push_back(mat[i][j]);
dir = (dir + 1) % 2;
}
i += d[dir][0];
j += d[dir][1];
}
return ret;
}
};
};

662. 二叉树最大宽度

只要记录下来pos就没啥大问题

bfs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
* 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:
int widthOfBinaryTree(TreeNode* root) {
if(!root)
return 0;
queue<Node> q;
int ret = 1;
q.push(Node(root, 1));
while(!q.empty())
{
long left = q.front().pos;
for(int i = 0, sz = q.size(); i < sz; ++i)
{
auto p = q.front();
q.pop();
if(p.node->left)
q.push(Node(p.node->left, 2 * p.pos));
if(p.node->right)
q.push(Node(p.node->right, 2 * p.pos + 1));
if(i == sz - 1)
ret = max(ret, (int)(p.pos - left + 1));
}
}
return ret;
}
private:
struct Node
{
TreeNode* node;
long pos;
Node(TreeNode* node, int pos) : node(node), pos(pos) {}
};
};
dfs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* 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:
int widthOfBinaryTree(TreeNode* root) {
if(!root)
return 0;
dfs(std::move(Node(root, 1)), 0);
return ret;
}
private:
unordered_map<int, int> left;
int ret{ 1 };
struct Node
{
TreeNode* node;
long pos;
Node(TreeNode* node, int pos) : node(node), pos(pos) {}
};
private:
void dfs(Node node, int depth)
{
if(!left.count(depth))
left.emplace(depth, node.pos);
else
ret = max(ret, int(node.pos - left[depth]) + 1);
if(node.node->left)
dfs(std::move(Node(node.node->left, node.pos * 2)), depth + 1);
if(node.node->right)
dfs(std::move(Node(node.node->right, node.pos * 2 + 1)), depth + 1);
}
};

958. 二叉树的完全性检验

层序遍历
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
* 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:
bool isCompleteTree(TreeNode* root) {
queue<TreeNode*> q;
q.push(root);
int expect = 1;
while(!q.empty())
{
int sz = q.size();
if(sz != expect)
break;
expect <<= 1;
bool broke = false;
for(int i = 0; i < sz; ++i)
{
auto p = q.front();
q.pop();
if(broke && (p->left || p->right)) // 前面断裂了,如果下一层还不是最后一层,那没了啊
return false;
if(!p->left && p->right)
return false;
if(!p->right) // 出现了断层,如果不是最后一层,那你g了
broke = true;
if(p->left)
q.push(p->left);
if(p->right)
q.push(p->right);
}
}
while(!q.empty())
{
auto p = q.front();
q.pop();
if(p->left || p->right)
return false;
}
return true;
}
};

也算是层序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* 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:
bool isCompleteTree(TreeNode* root) {
queue<TreeNode*> q;
int i = 1;
q.push(root);
bool isBlock = false;
while(!q.empty())
{
auto p = q.front();
q.pop();
if(!p)
{
isBlock = true;
continue;
}
if(isBlock)
return false;
q.push(p->left);
q.push(p->right);
}
return true;
}
};