二叉搜索树的中序遍历是从小到大排序的,如果先右后左则刚好满足题意,递归之
/**
* 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
{
int nTotal;
public:
TreeNode* bstToGst(TreeNode* root)
{
nTotal = 0;
ChangeVal(root);
return root;
}
private:
void ChangeVal(TreeNode* pNode)
{
if (pNode == NULL) return;
ChangeVal(pNode->right);
pNode->val += nTotal;
nTotal = pNode->val;
ChangeVal(pNode->left);
}
};
只递归会带来大量的重复计算导致栈溢出,采用一个数组存储可以起到剪枝作用。
class Solution {
public:
int memo[55][55];
int dfs(const vector<int>& values, int l, int r) // 从第 l 点到第 r 个点所形成的三角形的最小面积
{
if (r - l + 1 < 3)
return 0;
if (memo[l][r])
return memo[l][r];
int res = 0x3f3f3f3f;
for (int k = l+1; k < r; k++)
{
res = min(res, values[l] * values[k] *values[r] + dfs(values, l, k) + dfs(values, k, r));
}
memo[l][r] = res;
return res;
}
int minScoreTriangulation(vector<int>& values) {
memset(memo, 0, sizeof(memo));
return dfs(values, 0, values.size() - 1);
}
};
·1040. 移动石子直到连续 II
要使游戏结束,你可以执行的最小和最大移动次数分别是多少? 以长度为 2 的数组形式返回答案:answer = [minimum_moves, maximum_moves] 。
最关键的是理解题意:每次移动只能动端点,但是端点移动只能插在内部,如果是端点插成端点是不可以的。因此先求出最大可移动空间,之后每次移动一步,这就是最大移动次数。而最小移动次数,则是采用滑动空间找到当前已满足连续的最大区间,然后努力靠进去即可。
class Solution {
public:
vector<int> numMovesStonesII(vector<int>& stones) {
sort(stones.begin(),stones.end());
int n = stones.size();
int mx = stones[n - 1] - stones[0] + 1 - n;
mx -= min(stones[n-1]-stones[n-2] - 1, stones[1]-stones[0] -1);
int mi = mx;
int i = 0;
int j = 0;
for(i = 0; i < n; ++i)
{
while(j + 1 < n && stones[j + 1] - stones[i] + 1 <= n)
++j;
int cost = n - (j - i + 1);
if(j - i + 1 == n - 1 && stones[j] - stones[i] + 1 == n - 1)
cost = 2;
mi = min(mi, cost);
}
return vector<int>{mi, mx};
}
};
一轮执行结束后,方向与初始方向一致(向北),并且不是位于初始地点(0,0),那么将永远不会循环;否则,一定会产生循环
class Solution {
public:
bool isRobotBounded(string instructions) {
vector<pair<int, int>> dir = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
int face = 0, x = 0, y = 0;
for (int i = 0; i < instructions.size(); ++i) {
if (instructions[i] == 'L') {
face--;
}
else if (instructions[i] == 'R') {
face++;
}
else {
if (face < 0) {
face = 4 + (face % 4);
}
x += dir[face % 4].first;
y += dir[face % 4].second;
}
}
return (x == 0 && y == 0) || (face % 4 != 0);
}
};
这里其实就是回溯的一个特例:因为题意保证了不会重复,所以没有试错的说法,只要遍历一遍基本就完事了。
class Solution {
public:
vector<int> gardenNoAdj(int n, vector<vector<int>>& paths) {
vector<vector<int>>node2nodes(n+1);
for(int i=0;i<paths.size();i++){
int node1=paths[i][0],node2=paths[i][1];
node2nodes[node1].push_back(node2);
node2nodes[node2].push_back(node1);
}
vector<int>ans;
for(int i=1;i<=n;i++){
auto &nextNodes=node2nodes[i];
for(int flower=1;flower<=4;flower++){
bool flag=false;
for(auto&nextNode:nextNodes){
if(nextNode-1>=ans.size())continue;
if(ans[nextNode-1]==flower){
flag=true;
break;
}
}
if(flag==false){
ans.push_back(flower);
break;
}
}
}
return ans;
}
};
因为只能拆分子数组,所以用动态规划比较合理。当长度小于k的时候,dp可以直接乘最大值,当大于k的时候,需要考虑当前i和前面结合、不结合各种情况,需要用一个循环来取最大值才可
class Solution {
public:
int maxSumAfterPartitioning(vector<int>& arr, int k) {
int n=arr.size();
vector<int>dp(n,0);
int curMax=arr[0];
dp[0]=curMax;
for(int i=1;i<k;i++){
curMax=max(curMax,arr[i]);
dp[i]=curMax*(i+1);
}
for(int i=k;i<n;i++){
for(int j=i-k;j<i;j++){
dp[i]=max(dp[i],dp[j]+*max_element(arr.begin()+j+1,arr.begin()+i+1)*(i-j));
}
}
return dp[n-1];
}
};
二分查找 + Rabin-Karp 字符串编码
typedef pair<long long, long long> pll;
class Solution {
public:
long long pow(int a, int m, int mod) {
long long ans = 1;
long long contribute = a;
while (m > 0) {
if (m % 2 == 1) {
ans = ans * contribute % mod;
if (ans < 0) {
ans += mod;
}
}
contribute = contribute * contribute % mod;
if (contribute < 0) {
contribute += mod;
}
m /= 2;
}
return ans;
}
int check(const vector<int> & arr, int m, int a1, int a2, int mod1, int mod2) {
int n = arr.size();
long long aL1 = pow(a1, m, mod1);
long long aL2 = pow(a2, m, mod2);
long long h1 = 0, h2 = 0;
for (int i = 0; i < m; ++i) {
h1 = (h1 * a1 % mod1 + arr[i]) % mod1;
h2 = (h2 * a2 % mod2 + arr[i]) % mod2;
if (h1 < 0) {
h1 += mod1;
}
if (h2 < 0) {
h2 += mod2;
}
}
// 存储一个编码组合是否出现过
set<pll> seen;
seen.emplace(h1, h2);
for (int start = 1; start <= n - m; ++start) {
h1 = (h1 * a1 % mod1 - arr[start - 1] * aL1 % mod1 + arr[start + m - 1]) % mod1;
h2 = (h2 * a2 % mod2 - arr[start - 1] * aL2 % mod2 + arr[start + m - 1]) % mod2;
if (h1 < 0) {
h1 += mod1;
}
if (h2 < 0) {
h2 += mod2;
}
// 如果重复,则返回重复串的起点
if (seen.count(make_pair(h1, h2))) {
return start;
}
seen.emplace(h1, h2);
}
// 没有重复,则返回-1
return -1;
}
string longestDupSubstring(string s) {
srand((unsigned)time(NULL));
// 生成两个进制
int a1 = random()%75 + 26;
int a2 = random()%75 + 26;
// 生成两个模
int mod1 = random()%(INT_MAX - 1000000006) + 1000000006;
int mod2 = random()%(INT_MAX - 1000000006) + 1000000006;
int n = s.size();
// 先对所有字符进行编码
vector<int> arr(n);
for (int i = 0; i < n; ++i) {
arr[i] = s[i] - 'a';
}
// 二分查找的范围是[1, n-1]
int l = 1, r = n - 1;
int length = 0, start = -1;
while (l <= r) {
int m = l + (r - l + 1) / 2;
int idx = check(arr, m, a1, a2, mod1, mod2);
if (idx != -1) {
// 有重复子串,移动左边界
l = m + 1;
length = m;
start = idx;
} else {
// 无重复子串,移动右边界
r = m - 1;
}
}
return start != -1 ? s.substr(start, length) : "";
}
};