文章目录
括号生成(dfs)
class Solution {
public:
vector<string> ans;
vector<string> generateParenthesis(int n) {
/*
括号序列的性质:(重要,常用!)
1. 任何前缀里 '(' 的数量 >= ')'的数量
2. 左右括号数量相等
*/
dfs(n,0,0,"");
return ans;
}
void dfs(int n,int lc,int rc,string seq)
{
if(lc == n && rc == n) ans.push_back(seq);
else
{
if(lc < n) dfs(n,lc + 1,rc , seq + '(');
if(rc < n && lc > rc) dfs(n,lc,rc + 1,seq + ')');
}
}
};
最长公共子序列(DP)(求具体方案)
记录方案,记录转移状态,这里记录最前面,也就是第一个可以转移的
class Solution {
public:
/**
* longest common subsequence
* @param s1 string字符串 the string
* @param s2 string字符串 the string
* @return string字符串
*/
string LCS(string s1, string s2) {
// write code here
int n = s1.size(), m = s2.size();
vector<vector<int>> f(n + 1,vector<int>(m + 1));
for(int i = 1;i <= n;i ++ )
for(int j = 1;j <= m;j ++ )
{
f[i][j] = max(f[i-1][j],f[i][j - 1]);
if(s1[i - 1] == s2[j - 1]) f[i][j] = max(f[i][j], f[i-1][j-1] + 1);
}
if(f[n][m] == 0) return "-1";
// 最长公共子序列记录方案
string res;
int i = n,j = m;
while(f[i][j] > 0){
// 记录方案,记录转移状态,这里记录最前面,也就是第一个可以转移的
while(f[i - 1][j] == f[i][j]) i --; // 12C4B6
while(f[i][j - 1] == f[i][j]) j --;
res += s1[i - 1]; // i从下标1开始,所以要-1
i --,j -- ;
}
reverse(res.begin(),res.end());
return res;
}
};
丢棋子问题(动态规划)
法一:爆搜(超时)
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 返回最差情况下扔棋子的最小次数
* @param n int整型 楼层数
* @param k int整型 棋子数
* @return int整型
*/
int solve(int n, int k) {
// write code here
return dfs(n,k);
}
int dfs(int n,int k)
{
if(n == 0) return 0;
if(k == 1) return n;
int minv = INT_MAX;
for(int i = 1;i <= n;i ++)
{
minv = min(minv,max(dfs(n - i,k),dfs(i - 1,k - 1)));
}
return minv + 1;
}
};
法二:暴搜改动态规划,时间复杂度 O ( n ∗ n ∗ K ) O(n * n * K) O(n∗n∗K)
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 返回最差情况下扔棋子的最小次数
* @param n int整型 楼层数
* @param k int整型 棋子数
* @return int整型
*/
int solve(int n, int k) {
// write code here
if(n == 0 || k == 0) return 0;
if(k == 1) return n;
vector<vector<int>> f(n + 1,vector<int>(k + 1));
for(int i = 1;i <= n;i ++ ) f[i][1] = i;
for(int i = 1;i <= n;i ++ )
for(int j = 2;j <= k;j ++ )
{
int minv = INT_MAX;
for(int k = 1;k <= i ;k ++)
minv = min(minv,max(f[k - 1][j - 1],f[i - k][j]));
f[i][j] = minv + 1;
}
return f[n][k];
}
};
法三:打表,找规律
考虑n个棋子扔1.2.3.。。k次能够解决多少层问题,打表,找规律。
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 返回最差情况下扔棋子的最小次数
* @param n int整型 楼层数
* @param k int整型 棋子数
* @return int整型
*/
int solve(int n, int k) {
// write code here
if(n == 0 || k == 0) return 0;
if(k == 1) return n;
int logk = log2(n) + 1;
if(k >= logk) return logk;
vector<vector<int>> f(n + 1,vector<int>(k + 1,0)); // 棋子数,楼层数
for(int i = 1;i <= k;i ++ ) f[1][i] = i;
for(int i = 2;i <= n;i ++ )
for(int j = 0;j <= k;j ++ )
{
if(j == 0){
f[i][j] = 0;
continue;
}
f[i][j] = f[i - 1][j - 1] + f[i- 1][j] + 1;
if(f[i][j] >= n) return i;
}
return 0;
}
};
合并区间(排序)
先对区间排序,最后的时候记得把最后一段区间加入
#include <algorithm>
/**
* Definition for an interval.
* struct Interval {
* int start;
* int end;
* Interval() : start(0), end(0) {}
* Interval(int s, int e) : start(s), end(e) {}
* };
*/
bool cmp(Interval &a,Interval &b)
{
return a.start < b.start;
}
class Solution {
public:
vector<Interval> merge(vector<Interval> &intervals) {
vector<Interval> ans;
int n = intervals.size();
sort(intervals.begin(),intervals.end(),cmp);
int st = -2e9,ed = -2e9;
for(int i = 0;i < n;i ++ )
{
if(ed < intervals[i].start)
{
if(st!=-2e9) ans.push_back({
st,ed});
st = intervals[i].start,ed = intervals[i].end;
}else ed = max(ed,intervals[i].end);
}
if(st != -2e9) ans.push_back({
st,ed}); // 把最后一段区间加入
return ans;
}
};
回文数字(数学)
class Solution {
public:
/**
*
* @param x int整型
* @return bool布尔型
*/
bool isPalindrome(int x) {
// write code here
// 翻转一半
if(x < 0 || (x % 10 == 0 && x != 0)) return false;
int res = 0;
while(x > res)
{
res = res * 10 + x % 10;
x /= 10;
}
return res == x || res / 10 == x;
}
};
顺时针旋转矩阵(数学)
class Solution {
public:
vector<vector<int> > rotateMatrix(vector<vector<int> > mat, int n) {
// write code here
for(int i = 0;i < n;i ++ )
for(int j = 0;j < i;j ++ )
swap(mat[i][j],mat[j][i]);
for(int i = 0;i < n;i ++ )
for(int j = 0,k = n - 1;j < k;j ++,k -- )
swap(mat[i][j],mat[i][k]);
return mat;
}
};
判断一棵二叉树是否是二叉搜索树和完全二叉树(递归)
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
class Solution {
public:
/**
*
* @param root TreeNode类 the root
* @return bool布尔型vector
*/
vector<bool> judgeIt(TreeNode* root) {
// write code here
return {
dfs1(root,INT_MIN,INT_MAX),dfs2(root)};
}
bool dfs1(TreeNode *root,long long minv,long long maxv) // 判断是否是二叉搜索树
{
if(!root) return true;
int v = root->val;
if(v < minv || v > maxv) return false;
return dfs1(root->left,minv,v - 1ll) && dfs1(root->right,v + 1ll,maxv);
}
bool dfs2(TreeNode *root) // 判断是否是完全二叉树
{
if(!root) return true;
if(root->left && root->right == NULL) return dfs2(root->left);
if(root->left == NULL && root->right) return false;
return dfs2(root->left) && dfs2(root->right);
}
};
判断是否是二叉搜索树的迭代版本
/**
* 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 isValidBST(TreeNode* root) {
/*
迭代:二叉搜索树前序遍历是递增数组
*/
stack<TreeNode*> stk;
long long inorder = INT_MIN - 1ll;
// 迭代二叉树模板
while(root || !stk.empty())
{
while(root != NULL){
stk.push(root);
root = root->left;
}
root = stk.top();
stk.pop();
if(root->val <= inorder) return false;
inorder = root->val;
root = root->right; // 转向右边
}
return true;
}
};
重排链表(链表)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
void reorderList(ListNode* head) {
if(!head) return ;
auto fast = head,slow = head;
while(fast->next != NULL && fast->next->next != NULL) // 这样写,偶数找第一个中点
{
// 规定前半段比后半段多1
fast = fast->next->next;
slow = slow->next;
}
//cout << slow->val; 1. 看中点是否正确
auto p = reverse(slow->next);
slow->next = NULL;
/* 2. 看反转链表是否正确
while(head) cout<<head->val <<' ',head=head->next;
cout << endl;
while(p) cout << p->val <<' ', p = p->next;
*/
// 3.合并链表
merge(head,p);
}
ListNode* reverse(ListNode* head)
{
ListNode* pre = NULL,*cur = head;
while(cur){
auto next = cur->next;
cur->next = pre;
pre = cur,cur = next;
}
return pre;
}
void merge(ListNode* l1,ListNode* l2)
{
while(l1 && l2)
{
auto l1_next = l1->next, l2_next = l2->next;
l1->next = l2;
l1 = l1_next;
l2->next = l1;
l2 = l2_next;
}
}
};
二叉树中是否有从根节点到叶子节点的节点值之和等于 sum 的路径(递归)
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
class Solution {
public:
/**
*
* @param root TreeNode类
* @param sum int整型
* @return bool布尔型
*/
bool hasPathSum(TreeNode* root, int sum) {
// write code here
return dfs(root,sum);
}
bool dfs(TreeNode* root,int sum)
{
if(!root) return false;
sum -= root->val;
if(!root->left && !root->right && sum == 0) return true;
return dfs(root->left,sum) || dfs(root->right,sum);
}
};
有重复数字的所有排列(递归)
去重技巧
class Solution {
public:
vector<vector<int> > ans;
vector<int> path;
vector<bool> st;
vector<vector<int> > permuteUnique(vector<int> &num) {
sort(num.begin(),num.end());
st = vector<bool>(num.size());
dfs(num,0);
return ans;
}
void dfs(vector<int>& num,int u)
{
if(u == num.size())
{
ans.push_back(path);
return;
}
for(int i = 0;i < num.size();i ++ )
{
if(!st[i])
{
st[i] = true;
path.push_back(num[i]);
dfs(num,u + 1);
path.pop_back();
st[i] = false;
while(i+ 1 < num.size() && num[i + 1] == num[i]) i ++ ; // 去重
}
}
}
};