【Algorithm】Algorithm Learning
Daily Algorithm Learning.
《靡不有初系列》
算法学习:
靡不有初系列
LC Daily Problem
525. 连续数组、
前缀和+哈希+数学推导
class Solution {
public:
int findMaxLength(vector<int>& nums) {
int n = nums.size();
if(n <= 1) return 0;
//[j, i]
//2(pre[i+1] - pre[j]) = i+1 -j
//2pre[i+1] - (i+1) = 2pre[j] - j;
unordered_map<int, int> mp;
int pre = 0, res = 0;
mp[0] = 0;
for(int i=0; i<n; ++i) {
pre += nums[i];
int now = 2*pre - (i+1);
if(mp.count(now)) {
res = max(res, i+1-mp[now]);
} else {
mp[now] = i+1;
}
}
return res;
}
};
官方题解
class Solution {
public:
int findMaxLength(vector<int>& nums) {
int maxLength = 0;
unordered_map<int, int> mp;
int counter = 0;
mp[counter] = -1;
int n = nums.size();
for (int i = 0; i < n; i++) {
int num = nums[i];
if (num == 1) {
counter++;
} else {
counter--;
}
if (mp.count(counter)) {
int prevIndex = mp[counter];
maxLength = max(maxLength, i - prevIndex);
} else {
mp[counter] = i;
}
}
return maxLength;
}
};
523. 连续的子数组和
数学题:
同余定理:如果A%k == 0, B % k == 0,那么(A - B)% k == 0
优化题:
剪枝
class Solution {
public boolean checkSubarraySum(int[] nums, int k) {
// 当出现两个连续的0时,直接返回true,因为 0 % k = 0
for (int i = 0; i < nums.length - 1; i++) {
if (nums[i] == 0 && nums[i + 1] == 0) {
return true;
}
}
// 其中i为左端点,j为右端点,遍历每种情况
for (int i = 0; i < nums.length; i++) {
int sum = nums[i];
for (int j = i + 1; j < nums.length; j++) {
sum += nums[j];
if (sum % k == 0) {
return true;
}
}
// 加到一起之后发现都没k大,后面的也不会再比k大了,跳过
if (sum < k) {
break;
}
}
return false;
}
}
342. 4的幂
位运算
数学题
下标从0位开始,偶数位为4的幂,1010 = a
func isPowerOfFour(n int) bool {
return n > 0 && n&(n-1) == 0 && n&0xaaaaaaaa == 0
}
461. 汉明距离
内置函数
位运算
go
func hammingDistance(x, y int) int {
return bits.OnesCount(uint(x ^ y))
}
c++
class Solution {
public:
int hammingDistance(int x, int y) {
return __builtin_popcount(x ^ y);
}
};
class Solution {
public:
int hammingDistance(int x, int y) {
int s = x ^ y, ret = 0;
while (s) {
s &= s - 1; // 消除最低位的1
ret++;
}
return ret;
}
};
1190. 反转每对括号间的子串
栈进行模拟
class Solution {
public:
string reverseParentheses(string s) {
stack<string> stk;
string str;
for (auto &ch : s) {
if (ch == '(') { // 左括号表示进入新一层 需要将之前的str保留 再与下一层作叠加
stk.push(str);
str = "";
} else if (ch == ')') { // 已经到最里层 将最里层的字符串翻转 返回给上一层
reverse(str.begin(), str.end());
str = stk.top() + str;
stk.pop();
} else {
str.push_back(ch);
}
}
return str;
}
};
664. 奇怪的打印机
C++区间dp
class Solution {
public:
int strangePrinter(string s) {
int n = s.size();
// vector<vector<int>> dp(n, vector<int>(n, 0x3f3f3f3f));
int dp[n][n];
memset(dp, 0x3f3f3f3f, sizeof(dp));
for(int i = 0; i < n; i++)
dp[i][i] = 1;
for(int len = 2; len <= n; len++) {
for(int i = 0, j = len-1; j < n; i++, j++) {
if(s[i] == s[j])
dp[i][j] = min(dp[i+1][j], dp[i][j-1]);
else {
for(int k = i; k < j; k++)
dp[i][j] = min(dp[i][j], dp[i][k]+dp[k+1][j]);
}
}
}
return dp[0][n-1];
}
};
1707. 与数组中元素的最大异或值
超时做法:暴力+剪枝
class Solution {
public:
vector<int> maximizeXor(vector<int>& nums, vector<vector<int>>& queries) {
int n = nums.size();
int m = queries.size();
vector<int> ans(m);
sort(nums.begin(), nums.end(), greater<int>());
for (int i = 0; i < m; i++) {
int MAX = 0;
int x = queries[i][0], y = queries[i][1];
int pos = lower_bound(nums.begin(),nums.end(), y, greater<int>()) - nums.begin();
//cout << pos << endl;
if (pos == n) {
ans[i] = -1;
continue;
}
for (int j = pos; j < n ; j++) {
//if (y < nums[i]) break;
if (nums[j] + x >= MAX) {
MAX = max(MAX, nums[j] ^ x);
} else {
//cout << j << endl;
break;
}
}
ans[i] = MAX;
}
return ans;
}
};
字典树
810. 黑板异或游戏
数学题
如果数组长度为偶数,那么怎么拿,Alice都赢
但如果长度是奇数呢?奇数就输了吗?不一定,如果数组本来异或结果就为0,那么Alice还是赢
class Solution {
public:
bool xorGame(vector<int>& nums) {
if (nums.size() % 2 == 0) {
return true;
}
int xorsum = 0;
for (int num : nums) {
xorsum ^= num;
}
return xorsum == 0;
}
};
1035. 不相交的线
lcs最长公共子序列
class Solution {
public:
int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {
int n = nums1.size(), m = nums2.size();
vector<vector<int>> dp(n + 1, vector<int>(m + 1));
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if (nums1[i - 1] == nums2[j - 1]) {
dp[i][j] = max(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];
}
};
692. 前K个高频单词
map考察自定义比较函数
class Solution {
public:
typedef struct node {
string s;
int cnt;
}node;
static bool cmp(node a, node b) {
if (a.cnt == b.cnt) {
return a.s < b.s;
}
return a.cnt > b.cnt;
}
vector<string> topKFrequent(vector<string>& words, int k) {
vector<string> ans(k);
map<string, int> m_si;
vector<node> vn;
for (auto word : words) {
m_si[word]++;
}
for (auto x : m_si) {
vn.push_back({x.first, x.second});
}
sort(vn.begin(), vn.end(), cmp);
for (int i = 0; i < k; i++) {
ans[i] = vn[i].s;
}
return ans;
}
};
1738. 找出第 K 大的异或坐标值
二维前缀和
class Solution {
public:
int kthLargestValue(vector<vector<int>>& matrix, int k) {
int m = matrix.size(), n = matrix[0].size();
vector<vector<int>> pre(m + 1, vector<int>(n + 1));
vector<int> results;
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
pre[i][j] = pre[i - 1][j] ^ pre[i][j - 1] ^ pre[i - 1][j - 1] ^ matrix[i - 1][j - 1];
results.push_back(pre[i][j]);
}
}
sort(results.begin(), results.end(), greater<int>());
return results[k - 1];
}
};
1442. 形成两个异或相等数组的三元组数目
n ^ 3做法:暴力枚举三个点,前缀和
class Solution {
public:
int countTriplets(vector<int>& arr) {
int n = arr.size();
vector<int> sum(n, 0);
sum[0] = arr[0];
for (int i = 1; i < arr.size(); i++) {
sum[i] = sum[i - 1] ^ arr[i];
}
int ans = 0;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
for (int k = j; k < n; k++) {
int a = sum[i] ^ sum[j - 1] ^ arr[i];
int b = sum[k] ^ sum[j] ^ arr[j];
if (a == b) {
//cout << i << j << k << endl;
ans++;
}
}
}
}
return ans;
}
};
n^2做法:前缀和+问题转换
class Solution {
public:
int countTriplets(vector<int>& arr) {
// 类似前缀和
int n = arr.size();
int* pre_ = new int[n + 1]();
int sum = 0;
pre_[0] = 0;
for (int i = 1; i <= n; i++)
{
sum ^= arr[i - 1];
pre_[i] = sum;
}
int count = 0;
for (int i = 0; i < n; i++)
{
for (int j = i + 1; j <= n; j++)
{
if (pre_[j] == pre_[i])
{
count += j - i - 1;
}
}
}
return count;
}
};
993.二叉树的堂兄弟结点
BFS。
如果两个结点在每一层,那么在每一层for循环向队列添加元素的时候,就必然会在同一个for循环中被添加到队列中。
/**
* 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:
using PTT = pair<TreeNode*, TreeNode*>;
bool isCousins(TreeNode* root, int x, int y) {
// 使用队列q来进行bfs
// 其中pair中,p.first 记录当前结点的指针,p.second 记录当前结点的父结点的指针
queue<PTT> q;
q.push({root, nullptr});
while(!q.empty()) {
int n = q.size();
vector<TreeNode*> rec_parent; // 存放结点
for(int i = 0; i < n; i++) {
auto [cur, parent] = q.front(); q.pop();
if(cur->val == x || cur->val == y)
rec_parent.push_back(parent);
if(cur->left) q.push({cur->left, cur}); // 存放该当前结点子节点以及当前结点(父节点)
if(cur->right) q.push({cur->right, cur});
}
// `x` 和 `y` 都没出现
if(rec_parent.size() == 0)
continue;
// `x` 和 `y` 只出现一个
else if(rec_parent.size() == 1)
return false;
// `x` 和 `y` 都出现了
else if(rec_parent.size() == 2)
// `x` 和 `y` 父节点 相同/不相同 ?
return rec_parent[0] != rec_parent[1];
}
return false;
}
};
421.数组中两个数最大异或值
- Trie树
- 暴力+剪枝
- 如果i + j小于当前已知的ii ^ jj那么 i ^ j的值一定小于 ii ^ jj的值
- 因为进位如果还小的话,明显不需要了
class Solution {
public:
int findMaximumXOR(vector<int>& nums) {
sort(nums.begin(), nums.end());
int n = nums.size();
long long maxValue = 0;
for (int i = n -1; i >= 1; i--) {
for (int j = i -1; j >= 0; j--) {
if ((long long)(nums[i]) + nums[j] < maxValue) {
break;
}
maxValue = max(maxValue, (long long)(nums[i] ^ nums[j]));
}
}
return maxValue;
}
};
13. 罗马数字转整数
对于一个罗马数字字符串可以
- 对于每一个字符 str[i]
- 检查str[i - 1]str[i]组成的字符串是否在map映射中出现,如果出现则优先采用该双字符的,如果没有出现则采用单字符的
- 因为是从后往前推,所有对于IX中的I会计算两次,即需要将IX9映射为8
class Solution {
public:
int romanToInt(string s) {
unordered_map<string, int> m = {{"I", 1}, {"IV", 3}, {"IX", 8}, {"V", 5}, {"X", 10}, {"XL", 30}, {"XC", 80}, {"L", 50}, {"C", 100}, {"CD", 300}, {"CM", 800}, {"D", 500}, {"M", 1000}};
int r = m[s.substr(0, 1)];
for(int i=1; i<s.size(); ++i){
string two = s.substr(i-1, 2);
string one = s.substr(i, 1);
r += m[two] ? m[two] : m[one];
}
return r;
}
};
12. 罗马数字转整数
建立映射关系,然后按对应数字从大到小依次处理字符串和减去对应的值。
class Solution {
public:
string intToRoman(int num) {
string strs[]= {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
int nums[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
string ans;
for (int i = 0; num > 0 && i < 13; i++) {
while (nums[i] <= num) {
ans += strs[i];
num -= nums[i];
}
}
return ans;
}
};
1310.子数组异或查询
前缀和
class Solution {
public:
vector<int> xorQueries(vector<int>& arr, vector<vector<int>>& queries) {
int n = arr.size();
vector<int> sum(n);
int len = queries.size();
sum[0] = arr[0];
for (int i = 1; i < n; i++) {
sum[i] = sum[i - 1] ^ arr[i];
}
vector<int> ans(len);
int cnt = 0;
for (auto q : queries) {
ans[cnt++] = sum[q[0]] ^ sum[q[1]] ^ arr[q[0]];
}
return ans;
}
};
1734.解码异或后的排列
数学思维
class Solution {
public:
vector<int> decode(vector<int>& encoded) {
int n = encoded.size() + 1;
int total = 0;
for (int i = 1; i <= n; i++) {
total ^= i;
}
int odd = 0;
// encoded[i] = perm[i] ^ perm[i + 1]
// 步长为2, perm[0] ^ encoded[i] ^ encode[i + 2] ... = perm[0] ^ encoded[1] ... encoded[n - 1]
for (int i = 1; i < n - 1; i += 2) {
odd ^= encoded[i];
}
vector<int> perm(n);
perm[0] = total ^ odd; // 所以perm[0]就可以求出来了
for (int i = 0; i < n - 1; i++) {
// 有了perm[0], encoded[i] ^ perm[i] = perm[i + 1]
perm[i + 1] = perm[i] ^ encoded[i];
}
return perm;
}
};
叶子相似的树
深度前序遍历记录叶子结点
/**
* 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:
void preSearch(TreeNode* root1, vector<int> &seq) {
if (root1 == nullptr) return;
if (!root1 -> left & !root1 -> right) {
seq.push_back(root1 -> val);
return ;
}
preSearch(root1 -> left, seq);
preSearch(root1 -> right, seq);
}
bool leafSimilar(TreeNode* root1, TreeNode* root2) {
vector<int> seq1, seq2;
preSearch(root1, seq1);
preSearch(root2, seq2);
return seq1 == seq2;
}
};
1482. 制作m束花所需的最少天数
二分+贪心
class Solution {
public:
bool check(vector<int>& bloomDay, int m, int k, int limit) {
int total = 0;
int cur_cnt = 0;
// 枚举在limit天 花盛开的情况
for(auto day : bloomDay) {
// 花开的天数 如果大于 当前天数limit说明还没有开放 将cur_cnt 重置 断开
if(day > limit)
cur_cnt = 0;
else // 小于且连续 就++
cur_cnt++;
// 判断是否满足一束花
if(cur_cnt >= k) {
total++;
cur_cnt = 0;
}
}
return total >= m;
}
int minDays(vector<int>& bloomDay, int m, int k) {
int n = bloomDay.size();
if(m * k > n) return -1; // 不存在的情况是 花数 < 需要的花束
// 优化 枚举范围在 min - max ele
int left = *min_element(bloomDay.begin(), bloomDay.end());
int right = *max_element(bloomDay.begin(), bloomDay.end());
// 二分枚举天数 能缩小就缩小 不能缩小就扩大
while(left <= right) {
int mid = (left + right) / 2;
if(check(bloomDay, m, k, mid))
right = mid-1; // 缩小区间
else
left = mid+1; // 扩大区间
}
return right + 1;
}
};
1723. 完成所有工作的最短时间
二分、回溯、剪枝
class Solution {
public:
bool backtrack(vector<int>& jobs, vector<int>& workloads, int idx, int limit) {
if (idx >= jobs.size()) {
return true;
}
int cur = jobs[idx];
for (auto& workload : workloads) {
if (workload + cur <= limit) {
workload += cur; // 选择当前的进行分配
if (backtrack(jobs, workloads, idx + 1, limit)) {
return true;
}
workload -= cur; // 不选择当前的进行分配
}
// 如果当前工人未被分配工作,那么下一个工人也必然未被分配工作
// 或者当前工作恰能使该工人的工作量达到了上限
// 这两种情况下我们无需尝试继续分配工作
if (workload == 0 || workload + cur == limit) { // 因为是整个for循环 每个都要尝试 这里剪枝
break;
}
}
return false;
}
bool check(vector<int>& jobs, int k, int limit) {
vector<int> workloads(k, 0);
return backtrack(jobs, workloads, 0, limit);
}
int minimumTimeRequired(vector<int>& jobs, int k) {
sort(jobs.begin(), jobs.end(), greater<int>()); // 从大到小排列
int l = jobs[0], r = accumulate(jobs.begin(), jobs.end(), 0); // limit -> [min, max]工作量
while (l < r) { // 二分枚举limit
int mid = (l + r) >> 1;
if (check(jobs, k, mid)) {
r = mid;
} else {
l = mid + 1;
}
}
return l;
}
};
1486. 数组异或操作
简答模拟
class Solution {
public:
int xorOperation(int n, int start) {
int num = start;
for (int i = 1; i < n; i++) {
num ^= (start + 2 * i);
}
return num;
}
};
1720. 解码异或后的数组
简单数论
class Solution {
public:
vector<int> decode(vector<int>& encoded, int first) {
int n = encoded.size();
vector<int> ans(n + 1, first);
for (int i = 0; i < n; i++) {
ans[i + 1] = ans[i] ^ encoded[i];
}
return ans;
}
};
740. 删除并获得点数
动态规划
class Solution {
public:
int deleteAndEarn(vector<int>& nums) {
if(nums.size() < 1) return 0;
int maxn = 0;
for(int it : nums)
maxn = max(maxn, it);
vector<int> cnt(maxn+1), dp(maxn+1);
for(int it : nums)
cnt[it]++;
dp[1] = cnt[1];
for(int i = 2; i <= maxn; i++)
dp[i] = max(dp[i-1], dp[i-2] + cnt[i] * i);
return dp[maxn];
}
};
554. 砖墙
贪心 思维题
对于每一条垂线,穿过的砖块和穿过的间隙相加结果都是总高度 是个定值
所以只需要找出最多的间隙那个 就找到了高度减最多的间隙就找到了穿过的最少砖数。
class Solution {
public:
int leastBricks(vector<vector<int>>& wall) {
unordered_map<int, int> cnt;
for (auto& widths : wall) {
int n = widths.size();
int sum = 0;
for (int i = 0; i < n - 1; i++) {
sum += widths[i];
cnt[sum]++;
}
}
int maxCnt = 0;
for (auto& [_, c] : cnt) {
maxCnt = max(maxCnt, c);
}
return wall.size() - maxCnt;
}
};
690. 员工的重要性
bfs
/*
// Definition for Employee.
class Employee {
public:
int id;
int importance;
vector<int> subordinates;
};
*/
class Solution {
public:
int getImportance(vector<Employee*> employees, int id) {
int ans = 0;
//if (employees.size() == 0) return 0;
unordered_set<int> emp_record;
unordered_map<int, int> m;
for (int i = 0; i < employees.size(); i++) {
m[employees[i] -> id] = i;
}
queue<Employee> que_emp;
que_emp.push(*employees[m[id]]);
while (que_emp.size()) {
Employee temp = que_emp.front();
que_emp.pop();
ans += temp.importance;
for (auto num : temp.subordinates) {
if (emp_record.count(num) == 0) {
emp_record.insert(num);
que_emp.push(*employees[m[num]]);
}
}
}
return ans;
}
};
403. 青蛙过河
解法1:
DFS + 记忆化(记忆化搜索)
class Solution {
public:
using PII = pair<int, int>;
unordered_map<int, unordered_set<int>> visited;
unordered_set<int> stone_pos;
int done;
bool dfs(int prv_pos, int speed) {
int cur_pos = prv_pos + speed;
if(speed < 0 || !stone_pos.count(cur_pos)) // 不能向后跳 并且当前位置有石头存在
return false;
if(visited[prv_pos].count(speed)) // 防止重复计算 以同一个速度到达同一个位置 代表是同一个状态
return false;
visited[prv_pos].insert(speed);//将当前新状态插入到数组中
if(cur_pos == done) // 达到最后一个
return true;
// 进行递归搜索
return dfs(cur_pos, speed-1) ||
dfs(cur_pos, speed) || dfs(cur_pos, speed+1);
}
bool canCross(vector<int>& stones) {
int n = stones.size();
// 保存石头位置
stone_pos = unordered_set<int>(stones.begin(), stones.end());
// 目标为最后一个位置
done = stones.back();
// 从0开始向跳一个
return dfs(0, 1);
}
};
动态规划
d[i][speed]
表示以speed能否跳到第i个石头- 初始化:
d[0][0] = 1
dp[i][speed] = dp[j][speed - 1] or dp[j][spped] or dp[j][speed + 1]
class Solution {
public:
bool canCross(vector<int>& stones) {
int n = stones.size();
// dp[i][speed]:表示能否以speed的速度,到达第i个石头
vector<vector<int>> dp(n, vector<int>(n, 0));
dp[0][0] = 1;
for(int i = 1; i < n; i++) {
for(int j = 0; j < i; j++) {
int speed = stones[i] - stones[j]; // 需要speed的距离才能从j到i
if(speed <= 0 || speed > j+1) // 从j开始跳的距离最大为 j + 1, 距离超过 j + 1,说明不能一次跳跃到达
continue;
dp[i][speed] = dp[j][speed-1] ||
dp[j][speed] || dp[j][speed+1];
}
}
for (int i = 0; i < n; i++) {
if (dp[n - 1][i]) return true;
}
return false;
}
};
633. 平方数之和
暴力
class Solution {
public:
bool judgeSquareSum(int c) {
for (long a = 0; a * a <= c; a++) {
double b = sqrt(c - a * a);
if (b == (int)b) {
return true;
}
}
return false;
}
};
Go版本
func judgeSquareSum(c int) bool {
for a := 0; a*a <= c; a++ {
rt := math.Sqrt(float64(c - a*a))
if rt == math.Floor(rt) {
return true
}
}
return false
}
双指针
left = 0, right = sqrt(c)
if (乘积大于目标 ) right --
if (成绩小于) left++
终结条件(left > right), right == left 可能是8 = 2^2 + 2^2
func judgeSquareSum(c int) bool {
left, right := 0, int(math.Sqrt(float64(c)))
for left <= right {
sum := left*left + right*right
if sum == c {
return true
} else if sum > c {
right--
} else {
left++
}
}
return false
}
938.二叉搜索树的范围和
中序遍历
/**
* 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:
void midSearch(TreeNode *root, vector<TreeNode *> &v) {
if (!root) return ;
midSearch(root -> left, v);
v.push_back(root);
midSearch(root -> right, v);
}
int rangeSumBST(TreeNode* root, int low, int high) {
vector<TreeNode *> v;
int ans = 0;
midSearch(root, v);
int i = 0;
for (i = 0; i < v.size(); i++) {
if (v[i] -> val >= low) break;
}
for (int j = i; j < v.size(); j++) {
if (v[j]->val <= high) ans += v[j]->val;
else break;
}
return ans;
}
};
官方题解
class Solution {
public:
int rangeSumBST(TreeNode *root, int low, int high) {
if (root == nullptr) {
return 0;
}
// 当前值不在 [low, high]范围内
if (root->val > high) {
return rangeSumBST(root->left, low, high);
}
if (root->val < low) {
return rangeSumBST(root->right, low, high);
}
// 当前值 + 左孩子的值 + 右孩子的值
return root->val + rangeSumBST(root->left, low, high) + rangeSumBST(root->right, low, high);
}
};
1011. 在D天内送达包裹的能力
二分查找
二分枚举船的limit 然后检查该limit是否满足
class Solution {
public:
bool check(vector<int> &weights, int D, int limit) {
int cnt = 1, cur = 0;
for(auto &weight : weights) {
if(limit < weight) return false;
if(cur + weight > limit) {
cnt++;
cur = 0;
}
cur += weight;
}
return cnt <= D; // <= D说明可以在D天之内运输完毕
}
int shipWithinDays(vector<int>& weights, int D) {
int left = 1, right = 500*50000; // limit [1, 500 * 50000]
int ans = right; // ans保存limit
while(left <= right) {
int mid = (left + right) / 2;
if(check(weights, D, mid)) { // 可以满足 试图减少limit 因为要求最小值
right = mid - 1;
ans = mid;
}
else // 不能满足 增加limit
left = mid + 1;
}
return ans;
}
};
897. 递增搜索树
中序遍历存树的结点指针 然后重建树
/**
* 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:
void midSearch(TreeNode *root, vector<TreeNode *> &nodes) {
if (root == nullptr) return;
midSearch(root -> left, nodes);
nodes.push_back(root);
midSearch(root -> right, nodes);
}
TreeNode* increasingBST(TreeNode* root) {
vector<TreeNode *> nodes;
if (root == nullptr) return nullptr;
midSearch(root, nodes);
nodes.push_back(nullptr);
TreeNode *ans = nodes[0], *temp = nodes[0];
for (int i = 0; i < nodes.size() - 1; i++) {
nodes[i] -> left = nullptr;
nodes[i] -> right = nodes[i + 1];
}
return ans;
}
};
377.组合总数IV
DP
dp[i][j]
表示长度为i的 构成总和为j的方案数
dp[0][0] = 1
using ULL = unsigned long long;
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
int len = target;
vector<vector<ULL>> f(len + 1,vector<ULL>(target + 1,0));
f[0][0] = 1; // 数组长度为i 构成长度为j
int ans = 0;
for(int i = 1; i <= len; i++){
for(int j = 0; j <= target; j++){
for(auto x : nums){
if(j >= x) f[i][j] += f[i - 1][j - x];
// 从 i - 1状态转移过来 可能是数组任意一个数转移过来
}
}
// 将每一个长度 1 - target(全1) 的长度的方案书相加
ans += f[i][target];
}
return ans;
}
};
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
vector<int> f(target + 1,0); // or vector<unsigned long long> f(target + 1,0); 就不用做取模的操作了
f[0] = 1;
for(int i = 1; i <= target; i++){
for(auto x : nums){
//c++计算的中间结果会溢出,但因为最终结果是int
//因此每次计算完都对INT_MAX取模,0LL是将计算结果提升到long long类型
if(i >= x) f[i] =(0LL + f[i] + f[i - x]) % INT_MAX;
}
}
return f[target];
}
};
368. 最大整除子集
DP
dp[i] 表示以nums[i]为末尾元素的整除子集的个数
初始状态:dp[i] = 1
第一遍找到每个以nums[i]结尾的整除子集大小。并记录最大值和最大子集长度dp[i] =max(dp[i], dp[j] + 1);
第二步:从后往前遍历先找到满足最大值和最大子集长度的元素,然后每选一个元素就减去一个最大子集长度,如maxSize = 5,一直递减直到1为止。判断元素是否符合的条件就是当前dp[i] == maxSize, 且maxVlaue % nums[i] == 0
class Solution {
public:
vector<int> largestDivisibleSubset(vector<int>& nums) {
int len = nums.size();
sort(nums.begin(), nums.end());
// 第 1 步:动态规划找出最大子集的个数、最大子集中的最大整数
vector<int> dp(len, 1);
int maxSize = 1;
int maxVal = dp[0];
for (int i = 1; i < len; i++) {
for (int j = 0; j < i; j++) {
// 题目中说「没有重复元素」很重要
if (nums[i] % nums[j] == 0) {
dp[i] = max(dp[i], dp[j] + 1);
}
}
if (dp[i] > maxSize) {
maxSize = dp[i];
maxVal = nums[i];
}
}
// 第 2 步:倒推获得最大子集
vector<int> res;
if (maxSize == 1) {
res.push_back(nums[0]);
return res;
}
for (int i = len - 1; i >= 0 && maxSize > 0; i--) {
if (dp[i] == maxSize && maxVal % nums[i] == 0) {
res.push_back(nums[i]);
maxVal = nums[i];
maxSize--;
}
}
return res;
}
};
27. 移除元素
原地删除无序数组的特定项
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int cur = 0;
for (auto num : nums ){
if (val != num) {
nums[cur++] = num;
}
}
return cur;
}
};
26. 删除有序数组的重复项
原地删除有序数组的重复项
class Solution {
public int removeDuplicates(int[] nums) {
if (nums.length == 0) return 0;
int i = 0;
for (int j = 1; j < nums.length; j++) {
if (nums[j] != nums[i]) {
i++;
nums[i] = nums[j];
}
}
return i + 1;
}
}
220. 存在重复元素
滑动窗口 + set维护窗口内的状态
class Solution {
public:
bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
set<long> s;
for (int i = 0; i < nums.size(); ++i) {
// 指针定位比 long(nums[i])-t 大的数的位置
auto pos = s.lower_bound(long(nums[i]) - t);
// 如果存在且该数字也比 long(nums[i]) + t 小,说明存在我们想要的结果
if (pos!=s.end() && *pos <= long(nums[i]) + t) return true;
s.insert(nums[i]);
if (s.size() > k) s.erase(nums[i-k]); // 维护滑动窗口
}
return false;
}
};
213. 打家劫舍
参考198 做两次打家劫舍 分为:
- 打劫第一家
- 不打劫第一家
class Solution {
public:
int rob0(vector<int>& nums, int st, int end) {
int n = nums.size();
if (n == 0) return 0;
//vector<int> dp(n + 1, 0); // dp[i]表示偷前i个房子的最大金额
//dp[1] = nums[0];
int cur = nums[st], pre = 0;
for (int i = st + 2; i <= end; i++) {
int temp = cur;
cur = max(cur, pre + nums[i - 1]);
pre = temp;
//dp[i] = max(dp[i - 1], dp[i - 2] + nums[i - 1]);
}
return cur;
}
int rob(vector<int>& nums) {
int n = nums.size();
if (n == 1) return nums[0];
//return rob0(nums, 0, n);
return max(rob0(nums, 0, n - 1), rob0(nums, 1, n));
}
};
208.Trie树
class Trie {
private:
bool isEnd;
Trie* next[26]; // 每一个结点都可能有26个字母的分支
public:
Trie() {
isEnd = false;
memset(next, 0, sizeof(next));
}
void insert(string word) {
Trie* node = this;
// 存在就一直找到底 不存在就创建新的结点
for (char c : word) {
if (node->next[c-'a'] == NULL) {
node->next[c-'a'] = new Trie();
}
node = node->next[c-'a'];
}
node->isEnd = true; // 标记为存在
}
bool search(string word) {
Trie* node = this;
for (char c : word) {
node = node->next[c - 'a'];
if (node == NULL) { // 当前字字母还没有被建立过 肯定不存在
return false;
}
}
// 在Trie树中找到了对应的字符串 并且已经移动了该字符串的最后一个位置
return node->isEnd;
}
// 只要树中能找到对应的prefix的字符串就可 不管是否结尾
bool startsWith(string prefix) {
Trie* node = this;
for (char c : prefix) {
node = node->next[c-'a'];
if (node == NULL) {
return false;
}
}
return true;
}
};
783.二叉树节点的最小距离
二叉树中序遍历得到有序数组 最小的距离为相邻的两个值的差值最小
/**
* 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:
vector<int> nums;
void dfs(TreeNode*root) {
if (root == nullptr) return ;
dfs(root -> left);
nums.push_back(root -> val);
dfs(root -> right);
}
int minDiffInBST(TreeNode* root) {
dfs(root);
int len = nums.size();
int ans = INT_MAX;
for (int i = 1; i < len; i++) {
ans = min(ans, nums[i] - nums[i - 1]);
}
return ans;
}
};
264. 丑数II
小顶堆: 堆顶为x , 下一个点就是 2x, 3x, 5x
class Solution {
public:
int nthUglyNumber(int n) {
vector<int> factors = {2, 3, 5};
unordered_set<long> seen;
priority_queue<long, vector<long>, greater<long>> heap;
seen.insert(1L);
heap.push(1L);
int ugly = 0;
for (int i = 0; i < n; i++) {
long curr = heap.top();
heap.pop();
ugly = (int)curr;
for (int factor : factors) {
long next = curr * factor;
if (!seen.count(next)) {
seen.insert(next);
heap.push(next);
}
}
}
return ugly;
}
};
263. 丑数
class Solution {
public:
bool isUgly(int n) {
if (n <= 0) {
return false;
}
vector<int> factors = {2, 3, 5};
for (int factor : factors) {
while (n % factor == 0) {
n /= factor;
}
}
return n == 1;
}
};
33. 搜索旋转排序数组
二分搜索
class Solution {
public:
int search(vector<int>& nums, int target) {
int lo = 0, hi = nums.size() - 1;
while (lo < hi) {
int mid = (lo + hi) / 2;
if ((nums[0] > target) ^ (nums[0] > nums[mid]) ^ (target > nums[mid]))
lo = mid + 1;
else
hi = mid;
}
return lo == hi && nums[lo] == target ? lo : -1;
}
};
二分模板
class Solution {
public:
int search(vector<int>& nums, int target) {
int lo = 0, hi = nums.size() - 1;
while (lo < hi) {
int mid = (lo + hi) / 2;
if (check[nums[mid]])
lo = mid + 1; // target -> [mid + 1, r]
else
hi = mid;// target -> [lo, mid]
}
return l;//最后是l == r
}
};
80. 删除有序数组中的重复项II
双指针(快慢指针),cur表示当前遍历到的指针,fill表示需要填的指针,最后数组的长度就是填入了多少个数。
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int n = nums.size();
int cnt = 1;
int fill = 1;
// 这里直接从1开始避免后面条件判断
for (int cur = 1; cur < n; ++cur) {
if (nums[cur] != nums[cur-1])
cnt = 1;
else
++cnt;
if (cnt <= 2){
nums[fill] = nums[cur];
++fill;
}
}
return fill;
}
};
88.合并两个有序数组
2021-04-05
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int idx = m + n - 1;
int i = m - 1, j = n - 1;
while (i >= 0 || j >= 0) {
if (i >= 0 && j >= 0) {
if (nums1[i] > nums2[j]) {
nums1[idx--] = nums1[i];
i--;
} else {
nums1[idx--] = nums2[j];
j--;
}
} else {
if (i == -1) {
nums1[idx--] = nums2[j];
j--;
}else {
nums1[idx--] = nums1[i];
i--;
}
}
}
}
};
781. 森林里面的兔子
2021-04-04
class Solution {
public:
int numRabbits(vector<int> &answers) {
unordered_map<int, int> count;
for (int y : answers) {
++count[y];
}
int ans = 0;
for (auto &[y, x] : count) {
ans += (x + y) / (y + 1) * (y + 1);
}
return ans;
}
};
两只相同颜色的兔子看到的其他同色兔子数必然是相同的。反之,若两只兔子看到的其他同色兔子数不同,那么这两只兔子颜色也不同。
因此,将 \textit{answers}answers 中值相同的元素分为一组,对于每一组,计算出兔子的最少数量,然后将所有组的计算结果累加,就是最终的答案。
例如,现在有 13 只兔子回答 5。假设其中有一只红色的兔子,那么森林中必然有 6 只红兔子。再假设其中还有一只蓝色的兔子,同样的道理森林中必然有 66 只蓝兔子。为了最小化可能的兔子数量,我们假设这 12 只兔子都在这 13 只兔子中。那么还有一只额外的兔子回答 5,这只兔子只能是其他的颜色,这一颜色的兔子也有 6 只。因此这种情况下最少会有 18 只兔子。
一般地,如果有 x 只兔子都回答y,则至少有 x / (y + 1) 种不同的颜色,且每种颜色有 y+1 只兔子,因此兔子数至少为
我们可以用哈希表统计 answers 中各个元素的出现次数,对每个元素套用上述公式计算,并将计算结果累加,即为最终答案。
向上取整: (x + y) / (y + 1)
1143.最长上升公共子序列
2021-04-05
https://leetcode-cn.com/problems/longest-common-subsequence/
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
const int M = text1.size();
const int N = text2.size();
vector<vector<int>> dp(M + 1, vector<int>(N + 1, 0));
for (int i = 1; i <= M; ++i) {
for (int j = 1; j <= N; ++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[M][N];
}
};
class Solution {
public:
int longestCommonSubsequence(string s1, string s2) {
int n = s1.size(), m = s2.size();
s1 = " " + s1, s2 = " " + s2;
int f[n+1][m+1];
memset(f, 0, sizeof(f));
for(int i = 0; i <= n; i++) f[i][0] = 1;
for(int j = 0; j <= m; j++) f[0][j] = 1;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
if(s1[i] == s2[j])
f[i][j] = f[i-1][j-1] + 1;
else
f[i][j] = max(f[i-1][j], f[i][j-1]);
}
}
return f[n][m] - 1;
}
};
面试题17.21 直方图的水量
算法思想:总体积减去柱子的体积就是雨水的体积
class Solution {
public:
int trap(vector<int>& height) {
int Sum = accumulate(height.begin(), height.end(), 0); // 得到柱子的体积
int volume = 0; // 总体积和高度初始化
int high = 1;
int size = height.size();
int left = 0; // 双指针初始化
int right = size - 1;
while (left <= right) {
// 在左边找到第一个高度 >= height的
while (left <= right && height[left] < high) {
left++;
}
// 在右边找到第一个高度 >= height的
while (left <= right && height[right] < high) {
right--;
}
// 二者差值+ 1就是这一层的体积
volume += right - left + 1; // 每一层的容量都加起来
high++; // 高度加一
}
return volume - Sum; // 总体积减去柱子体积,即雨水总量
}
};
74.搜索二维矩阵
二分搜索。
class Solution {
public:
bool searchMatrix(vector<vector<int>> matrix, int target) {
auto row = upper_bound(matrix.begin(), matrix.end(), target, [](const int b, const vector<int> &a) {
return b < a[0];
});
if (row == matrix.begin()) {
return false;
}
--row;
return binary_search(row->begin(), row->end(), target);
}
};
lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
在从大到小的排序数组中,重载lower_bound()和upper_bound()
lower_bound( begin,end,num,greater
upper_bound( begin,end,num,greater
#include<bits/stdc++.h>
using namespace std;
int main() {
int a[] = {1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4};
cout << (lower_bound(a, a + 12, 4) - a) << endl; //输出 9
cout << (upper_bound(a, a + 12, 4) - a) << endl; //输出 12
cout << (lower_bound(a, a + 12, 1) - a) << endl; //输出 0
cout << (upper_bound(a, a + 12, 1) - a) << endl; //输出 3
cout << (lower_bound(a, a + 12, 3) - a) << endl; //输出 6
cout << (upper_bound(a, a + 12, 3) - a) << endl; //输出 9
cout << (lower_bound(a, a + 12, 5) - a) << endl; //输出 12
cout << (upper_bound(a, a + 12, 5) - a) << endl; //输出 12
cout << (lower_bound(a, a + 12, 0) - a) << endl; //输出 0
cout << (upper_bound(a, a + 12, 0) - a) << endl; //输出 0
return 0;
}
转变为一维二分
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int m = matrix.size(), n = matrix[0].size();
int low = 0, high = m * n - 1;
while (low <= high) {
int mid = (high - low) / 2 + low;
int x = matrix[mid / n][mid % n];
if (x < target) {
low = mid + 1;
} else if (x > target) {
high = mid - 1;
} else {
return true;
}
}
return false;
}
};
1006. 笨阶乘
2021-04-01
class Solution {
public:
int clumsy(int N) {
stack<int> stk;
stk.push(N);
N--;
int index = 0; // 用于控制乘、除、加、减
while (N > 0) {
if (index % 4 == 0) {
stk.top() *= N;
} else if (index % 4 == 1) {
stk.top() /= N;
} else if (index % 4 == 2) {
stk.push(N);
} else {
stk.push(-N);
}
index++;
N--;
}
// 把栈中所有的数字依次弹出求和
int sum = 0;
while (!stk.empty()) {
sum += stk.top();
stk.pop();
}
return sum;
}
};
// 还有这种用法
stk.top() *= N;
// 小技巧 面对要减去的数可以变为+(-N)
stk.push(-N);
// 相同的还有 堆默认大顶堆 如果要构建小顶堆 就把-element插入push进堆之中
Acwing每日一题
3617. 子矩形计数
思维题
将k分成两个数相乘的形式(假设为k = a * b)
在row[]中找出有多少个a个连续的1;
在col[]中找出有多少个b个连续的1;将两者相乘再累加;
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 40010;
int n, m, k;
bool row[N], col[N];
LL count(int x, bool arr[], int len)
{
int res = 0, cnt = 0;
for(int i = 1;i <= len;i ++)
if(arr[i]) cnt ++;
else
{
if(cnt >= x) res += cnt - x + 1;
cnt = 0;
}
if(cnt >= x) res += cnt - x + 1;
return res;
}
int main()
{
cin >> n >> m >> k;
for(int i = 1;i <= n;i ++) cin >> row[i];
for(int i = 1;i <= m;i ++) cin >> col[i];
LL res = 0;
for(int i = 1;i <= k && i <= 40010;i ++)
if(k % i == 0)
{
int a = i, b = k / i;
LL cnta = count(a, row, n), cntb = count(b, col, m);
res += cnta * cntb;
}
cout << res << endl;
return 0;
}
3583.整数分组
dp
dp[i][j]
表示前i个数分成j组的最大方案数转移方程
dp[i][j] = max(dp[i - 1][j], dp[k - 1][j - 1] + (i - k + 1))
表示将第i个字符划分到第j组中 且第j组是从k开始的 是当前组中最小的
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5010;
int n, m;
int w[N];
int f[N][N];
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
sort(w + 1, w + n + 1);
for (int i = 1, k = 1; i <= n; i ++ )
{
while (w[i] - w[k] > 5) k ++ ;
for (int j = 1; j <= m; j ++ )
f[i][j] = max(f[i - 1][j], f[k - 1][j - 1] + (i - k + 1));
}
printf("%d\n", f[n][m]);
return 0;
}
3580.整数配对
简单思维
1.先预处理 把成对的处理完毕
2.再对新数组做排序
3.最优解是低位与最近高位做配对
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5;
int a[N];
int main() {
int n;
cin>> n;
unordered_map<int, int> um;
for (int i = 0; i < n; i++) {
cin >> a[i];
um[a[i]]++;
}
vector<int> temp;
for (auto x : um) {
if (x.second % 2) {
temp.push_back(x.first);
}
}
n = temp.size();
sort(temp.begin(), temp.end());
int ans = 0;
for (int i = 1; i < n; i += 2) {
ans += abs(temp[i - 1] - temp[i]);
}
cout << ans << endl;
return 0;
}
3333.K-优字符串
思维题目
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 200010;
int n, k;
char str[N];
int main()
{
int T;
scanf("%d", &T);
for (int C = 1; C <= T; C ++ )
{
printf("Case #%d: ", C);
scanf("%d%d%s", &n, &k, str);
int cnt = 0;
for (int i = 0, j = n - 1; i < j; i ++, j -- )
if (str[i] != str[j])
cnt ++ ;
printf("%d\n", abs(cnt - k));
}
return 0;
}
3483.2的慕次方
递归
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
string dfs(int n)
{
string res;
for (int i = 14; i >= 0; i -- )
if (n >> i & 1)
{
// 不是第一个的话需要相加
if (res.size()) res += '+';
// i为0
if (!i) res += "2(0)";
// i为1
else if (i == 1) res += "2";
// i >= 2
else res += "2(" + dfs(i) + ")";
}
return res;
}
int main()
{
int n;
while (cin >> n)
cout << dfs(n) << endl;
return 0;
}
作者:yxc
链接:https://www.acwing.com/activity/content/code/content/1253289/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
3404.谁是你的潜在朋友
简单统计
#include <bits/stdc++.h>
using namespace std;
int a[202];
int main() {
int n, m;
cin >> n >> m;
unordered_map<int, int> his;
int x;
for (int i = 0; i < n; i++) {
cin >> x;
a[i] = x;
his[x] ++;
}
for (int i = 0; i < n; i++) {
if (his[a[i]] - 1) {
cout << his[a[i]] - 1 << endl;
} else {
cout << "BeiJu" << endl;
}
}
return 0;
}
3481.阶乘的和
启发式剪枝,二进制枚举,爆搜
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_set>
using namespace std;
int f[10];
unordered_set<int> S;
int main()
{
for (int i = 0; i < 10; i ++ )
{
f[i] = 1;
for (int j = i; j; j -- )
f[i] *= j;
}
// 总共有10个数字 所有就有2^10个状态
for (int i = 1; i < 1 << 10; i ++ )
{
int s = 0;
// 对当前i状态检查 每一位的值
for (int j = 0; j < 10; j ++ )
if (i >> j & 1)
s += f[j];
// 插入到集合中
S.insert(s);
}
int n;
while (cin >> n, n >= 0)
if (S.count(n))
puts("YES");
else
puts("NO");
return 0;
}
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_set>
using namespace std;
int f[10];
unordered_set<int> S;
void init() {
f[0] = f[1] = 1;
for (int i = 2; i < 10; i++) {
f[i] = f[i - 1] * i;
//cout << f[i] << endl;
}
}
bool flag;
void dfs(int cur, int sum, int &target) {
if (sum == target) {
flag = 1;
return ;
}
if (sum > target) {
return ;
}
for (int i = cur + 1; i < 10; i++) {
dfs(i, sum + f[i], target);
}
}
int main()
{
int target;
init();
while (cin >> target) {
if (target < 0) return 0;
if (target == 0) {
cout << "NO" <<endl;
continue;
}
for (int i = 0; i < 10; i++) {
dfs(i, f[i], target);
if (flag) {
break;
}
}
if (flag) {
flag = 0;
cout << "YES" << endl;
continue;
}
cout << "NO" << endl;
}
return 0;
}
3502.不同路径数
迷宫模板题目
#include <bits/stdc++.h>
using namespace std;
const int N = 7;
int n, m, k;
int a[N][N];
int dir[][2] = {
{-1, 0},
{1, 0},
{0, 1},
{0 , -1}
};
int v[N][N];
bool check(int x, int y ) {
if (x >= 0 && y >= 0 && x < n && y < m) {
return true;
}
return false;
}
set<string> strs;
void getNum(int i, int j, string s, int cnt) {
if (k + 1 == cnt) {
strs.insert(s);
return;
}
for (int x = 0; x < 4; x++) {
int dx = i + dir[x][0], dy = j + dir[x][1];
if (check(dx, dy) && !v[dx][dy]) {
//v[dx][dy] = 1;
getNum(dx, dy, s + to_string(a[dx][dy]), cnt + 1);
//v[dx][dy] = 0;
}
}
}
int main() {
cin >> n >> m >> k;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> a[i][j];
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
//string temp = "";
//v[i][j] = 1;
getNum(i, j, to_string(a[i][j]), 1);
//v[i][j] = 0;
}
}
// for (auto x : strs) {
// cout << x << endl;
// }
cout << strs.size() << endl;
return 0;
}
3493.最大的和
思维 + 滑动窗口 + 前缀和
可以选择的数是确定的。因为数>1,所有可以枚举每一个k区间长度内得不可直接选择的总和。然后相加。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
int a[N];
bool v[N];
long long sum[N];
int main() {
int n, k;
cin >> n >> k;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
long long ans = 0;
for (int i = 0; i < n; i++) {
cin >> v[i];
if (v[i]) {
ans += a[i];
}
}
sum[0] = !v[0]?0:a[0];
for (int i = 1; i< n; i++) {
sum[i] = sum[i - 1] + (!v[i]) * a[i];
}
long long temp = 0;
for (int j = 0; j <= n - k; j++) {
long long xx = sum[j + k - 1] - sum[j] + (!v[j]) * a[j];
temp = max(temp, xx);
}
cout << temp + ans << endl;
return 0;
}
3485.最大异或和(MT笔试)
分析:
对于20%的数据显然可以使用n^3的算法。枚举起始下标和结束下标。然后再计算该区间的值。
对于50%的数据可以采用n^2的算法,使用前缀和快速求取。
对于100%的数据,可以先求出异或前缀和数组,然后问题就等价于从一堆数中选出两个数,他们两个的异或和最大。
即:
si = a1^a2^..^ai
,sj = a1^a2^..^aj
,si ^ aj = a^i+1...aj
#include <bits/stdc++.h>
using namespace std;
const int N = 100010 * 31, M = 100010; // 元素数100010,trie树高31
int n, m; // n元素个数 m区间最大值
int s[M]; // s用于记录每次读取的值
int son[N][2], cnt[N], idx; // son是trim数
void insert(int x, int v) { // 将一个数字加入到trie树中
int p = 0; // 初始在头结点
for (int i = 30; i >= 0; i--) { // 一次判断31位数字
int u = x >> i & 1;
if (!son[p][u]) son[p][u] = ++idx; //如果还没有创建就创建该结点
p = son[p][u]; // 移动到左子树或者右子树
cnt[p] += v;// v = 1 新增加 , v = -1删除
}
}
int query(int x) {
int res = 0, p = 0;
for (int i = 30; i >= 0; i--) {
int u = x >> i & 1;
// 每次选择不一样的子树 因为是异或,两者不同必然异或为1
if (cnt[son[p][!u]]) p =son[p][!u] , res = res * 2 + 1;
else p = son[p][u] , res = res * 2;
}
return res;
}
int main() {
cin >> n >> m;
for (int i = 1; i<= n; i++) {
int x;
cin >> x;
s[i] = s[i - 1] ^ x;
}
int res = 0;
insert(s[0], 1);
for (int i = 1; i <= n; i++) {
if (i > m) insert(s[i - m - 1], -1); // 区间大于m就删除
res = max(res, query(s[i]));
insert(s[i], 1);
}
cout << res << endl;
return 0;
}
3489.星期几--模拟
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
using namespace std;
// 1-12月 正常日期
int months[13] = {
0, 31, 28, 31, 30, 31, 30, 31, 31,
30, 31, 30, 31
};
//建立map映射
unordered_map<string, int> month_name = {
{"January", 1},
{"February", 2},
{"March", 3},
{"April", 4},
{"May", 5},
{"June", 6},
{"July", 7},
{"August", 8},
{"September", 9},
{"October", 10},
{"November", 11},
{"December", 12},
};
// 星期几名字映射
string week_name[7] = {
"Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday",
"Sunday"
};
// 判断是否是leap年
int is_leap(int year)
{
return year % 4 == 0 && year % 100 || year % 400 == 0;
}
// 获取该年中某年某月的天数
int get_days(int year, int month)
{
int s = months[month];
if (month == 2) return s + is_leap(year);
return s;
}
int main()
{
int d, m, y;
string str;
while (cin >> d >> str >> y)
{
m = month_name[str];
int i = 1, j = 1, k = 1; // i = year, j = month, k = days (1 - 30)
int days = 0;
while (i < y || j < m || k < d) // 目标日期还没有达到
{
k ++, days ++ ; // 本月天数++, 总天数++
if (k > get_days(i, j)) // 如果本月天数 > 该月天数 即进入下一个月
{
k = 1; // 第一天
j ++ ; // 月份++
if (j > 12) // 如果月份大于 12 说明进入下一年
{
j = 1; // 重新回到1月
i ++ ; // year++
}
}
}
//结果 = 总天数%7
cout << week_name[days % 7] << endl;
}
return 0;
}
LC WeekContest
239场周赛
5746. 到目标元素的最小距离
class Solution {
public:
int getMinDistance(vector<int>& nums, int target, int start) {
int ans = INT_MAX;
for (int i = 0; i < nums.size(); i++) {
if (nums[i] == target) {
ans = min(ans, abs(i - start));
}
}
return ans;
}
};
5747. 将字符串拆分为递减的连续值
二进制枚举
class Solution {
public:
bool splitString(string s) {
int n = s.size();
for (int i = 1; i < 1 << (n - 1); i++) {
bool flag = true;
unsigned long long last = -1, x = s[0] - '0';
for (int j = 0; j < n - 1; j++) {
if (i >> j & 1) { // 当前位需要分割
// 检查合法性
if (last != -1 && x != last - 1) {
flag = false;
break;
}
// 记录上一个的数值
last = x;
// 新值为 j + 1
x = s[j + 1] - '0';
} else { // 不分割 继续相加
x = x * 10 + s[j + 1] - '0';
}
}
if (x != last - 1) flag = false;
if (flag) return true;
}
return false;
}
};
5749. 邻位交换的最小次数
求当前全排列的下k个排列
next_permutation(b.begin(), b.end())
先找到第k个数,再逆序对交换。等价求解逆数对
C存A的元素在B中的下标位置和次数,
class Solution {
public:
int getMinSwaps(string a, int k) {
string b = a;
while (k--) next_permutation(b.begin(), b.end());
int n = a.size();
vector<int> c(n);
int cnt[10] = {0};
for (int i = 0; i < n; i++) {
int x = a[i] - '0';
cnt[x]++;
int y = cnt[x];
for (int j = 0; j < n; j++) {
if (b[j] - '0' == x && --y == 0) {
c[i] = j;
break;
}
}
}
int res = 0;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (c[i] > c[j])
res++;
}
}
return res;
}
};
236场周赛
数组元素积的符号
签到
5727找出游戏的获胜者
约瑟夫环
循环链表模拟
递推
class Solution {
public:
typedef struct node {
int val;
node * next;
}node;
int findTheWinner(int n, int k) {
if (n == 1) return 1;
if (k == 1) return n;
node *head = new node();
head -> val = 1, head -> next = new node();
node *temp = head;
temp = temp -> next;
for (int i = 2;i <= n; i++) {
temp -> val = i;
if (i != n)
temp -> next = new node();
else
temp -> next = head;
temp = temp -> next;
}
int remain = n;
// for (int i = 0; i <= n; i++) {
// cout << head -> val << endl;
// head = head -> next;
// }
node * cur = head;
while (remain != 1) {
for (int i = 1; i < k - 1; i++) {
cur = cur -> next;
}
//cout << cur -> val << endl;
//cout << cur -> next -> val << endl;
cur -> next = cur -> next -> next;
cur = cur -> next;
remain--;
}
return cur -> val;
}
};
class Solution {
public:
int f(int n, int k) {
if (n == 1) return 0;
return (f(n - 1, k) + k) %n;
}
int findTheWinner(int n, int k) {
return f(n, k) + 1;
}
}
最少侧跳数
const int N = 500010, INF = 1e8;
int f[N][3];
class Solution {
public:
int minSideJumps(vector<int>& b) {
// 中间跑道 跳到其它跑道都需要1
f[0][1] = 0, f[0][0] = f[0][2] = 1;
int n = b.size() - 1;
for (int i = 1; i <= n; i ++ ) // 第i层
for (int j = 0; j < 3; j ++ ) { // 第i层的 三个点
f[i][j] = INF; // 初始状态为INF
if (b[i] == j + 1) continue;// 当前要求的该点位障碍物 跳过
for (int k = 0; k < 3; k ++ ) { // 枚举i - 1层的所有状态
if (b[i] == k + 1) continue; // 不能由i - 1层通过到达
int cost = 0; // 在同一个水平线上 cost = 0
if (k != j) cost = 1; // 说明不在同一个水平线上
f[i][j] = min(f[i][j], f[i - 1][k] + cost);
}
}
return min(f[n][0], min(f[n][1], f[n][2]));
}
};
前200道题目
1 两数之和
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> ans(2);
int len = nums.size();
for (int i = 0; i < len; i++) {
for (int j = i + 1; j < len; j++) {
if (nums[i] + nums[j] == target) {
ans[0] = i, ans[1] = j;
return ans;
}
}
}
return nums;
}
};
2. 两数相加
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode head = new ListNode(l1.val + l2.val);
ListNode cur = head;
while(l1.next != null || l2.next != null){
l1 = l1.next != null ? l1.next : new ListNode();
l2 = l2.next != null ? l2.next : new ListNode();
cur.next = new ListNode(l1.val + l2.val + cur.val / 10);
cur.val %= 10;
cur = cur.next;
}
if(cur.val >= 10){
cur.next = new ListNode(cur.val / 10);
cur.val %= 10;
}
return head;
}
}
3. 无重复的最长子串
class Solution {
public:
int lengthOfLongestSubstring(string s) {
if(s.size() == 0) return 0;
unordered_set<char> lookup;
int maxStr = 0;
int left = 0;
for(int i = 0; i < s.size(); i++){
// 查找到了s[i] 删除左边
while (lookup.find(s[i]) != lookup.end()){
lookup.erase(s[left]);
left ++;
}
maxStr = max(maxStr,i-left+1);
lookup.insert(s[i]);
}
return maxStr;
}
};
4. 寻找两个正序数组中的中位数
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int len1 = nums1.size(), len2 = nums2.size();
int n = len1 + len2;
for (auto num : nums2) {
nums1.push_back(num);
}
sort(nums1.begin(), nums1.end());
if (n % 2 == 1) return nums1[n / 2];
else {
int mid = n / 2 - 1;
double a = nums1[mid], b = nums1[mid + 1];
return (a + b) / 2;
}
}
};
5. 最长回文串
class Solution {
public:
string longestPalindrome(string s) {
int n = s.size();
vector<vector<int>> dp(n, vector<int>(n));
string ans;
for (int l = 0; l < n; ++l) {
for (int i = 0; i + l < n; ++i) {
int j = i + l;
if (l == 0) {
dp[i][j] = 1;
} else if (l == 1) {
dp[i][j] = (s[i] == s[j]);
} else {
dp[i][j] = (s[i] == s[j] && dp[i + 1][j - 1]);
}
if (dp[i][j] && l + 1 > ans.size()) {
ans = s.substr(i, l + 1);
}
}
}
return ans;
}
};
6. Z字形变换
class Solution {
public:
string convert(string s, int numRows) {
if (numRows == 1) return s;
vector<string> rows(min(numRows, int(s.size())));
int curRow = 0;
bool goingDown = false;
for (char c : s) {
rows[curRow] += c;
if (curRow == 0 || curRow == numRows - 1) goingDown = !goingDown;
curRow += goingDown ? 1 : -1;
}
string ret;
for (string row : rows) ret += row;
return ret;
}
};
7. 整数翻转
class Solution {
public:
int reverse(int x) {
string MAX = to_string(0x7fffffff);
int MIN_ = 0x80000000;
string MIN = to_string(MIN_);
bool sign = 0; // 0 positive
if (x == MIN_) return 0;
if (x < 0) x = -x, sign = 1;
string old = to_string(x);
std::reverse(old.begin(), old.end()); // 321
if (sign) { // 负数
old = '-' + old;
if (MIN.length() <= old.length()) {
if (old > MIN) return 0;
}
} else {
if (MAX.length() <= old.length()) {
if (MAX < old) return 0;
}
}
//cout << old << endl;
return atoi(old.c_str());
}
};
8. 字符串转换整数
class Solution {
public:
int myAtoi(string s) {
int sign = 1, tmp = 0, i = 0;
while(s[i] == ' ') ++i; //1.忽略前导空格
if(s[i] == '+' || s[i] == '-') //2.确定正负号
sign = (s[i++] == '-') ? -1 : 1; //s[i]为+的话sign依旧为1,为-的话sign为-1
while(s[i] >= '0' && s[i] <= '9') //3.检查输入是否合法
{
if(tmp > INT_MAX / 10 || (tmp == INT_MAX / 10 && s[i] - '0' > 7)) //4.是否溢出
return sign == 1 ? INT_MAX : INT_MIN;
tmp = tmp * 10 + (s[i++] - '0'); //5.不加括号有溢出风险
}
return tmp * sign;
}
};
9. 判断是否为回文字符串
class Solution {
public:
bool isPalindrome(int x) {
string posiX = to_string(x);
string temp = posiX;
reverse(posiX.begin(), posiX.end());
return temp == posiX;
}
};
class Solution {
public:
bool isPalindrome(int x) {
if (x < 0) return false;
long temp = 0;
int r = x;
while (r != 0) {
if (temp > x) return false;
temp = temp * 10 + r % 10;
r /= 10;
}
return temp == x;
}
};
10.正则表达式匹配
11.盛水最多的容器
class Solution {
public int maxArea(int[] height) {
int i = 0, j = height.length - 1, res = 0;
while(i < j){
res = height[i] < height[j] ?
Math.max(res, (j - i) * height[i++]):
Math.max(res, (j - i) * height[j--]);
}
return res;
}
}
12. 整数转罗马数字
13 个数字 从大到小 依次减到不能减
class Solution {
public:
string intToRoman(int num) {
string strs[]= {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
int nums[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
string ans;
for (int i = 0; num > 0 && i < 13; i++) {
while (nums[i] <= num) {
ans += strs[i];
num -= nums[i];
}
}
return ans;
}
};
13. 罗马数字转整数
建立映射函数 对于每一个字符 计算它和它之前的 对于I, IV 扫描到V会查看前一个 即IV得到3 因为前一个I已经计算过为1。结合编码格式,因为VIV是不存在的。
class Solution {
public:
int romanToInt(string s) {
unordered_map<string, int> m = {{"I", 1}, {"IV", 3}, {"IX", 8}, {"V", 5}, {"X", 10}, {"XL", 30}, {"XC", 80}, {"L", 50}, {"C", 100}, {"CD", 300}, {"CM", 800}, {"D", 500}, {"M", 1000}};
int r = m[s.substr(0, 1)];
for(int i=1; i<s.size(); ++i){
string two = s.substr(i-1, 2);
string one = s.substr(i, 1);
r += m[two] ? m[two] : m[one];
}
return r;
}
};
14. 最长公共前缀
维护cnt指针 指向公共下标
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
string ans = "";
int len = strs.size();
if (len == 0) return "";
int cnt = 0;
while(1) {
for (int i = 0; i < len; i++) {
if (strs[i].length() == 0) return "";
if (cnt < strs[i].length() && cnt < strs[0].length() && strs[i][cnt] == strs[0][cnt]) {
continue;
} else {
return ans;
}
}
ans += strs[0][cnt++];
}
return ans;
}
};
15. 三数之和
排序 + 三指针
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> res;
int n = nums.size();
if (n < 3) return res;
sort(nums.begin(), nums.end());
for (int i = 0; i < n; i ++ ) {
//1. 开始预处理
if (nums[i] > 0) return res; //若第一个数大于0,后面怎么加都不会等于0了
if (i > 0 && nums[i] == nums[i - 1]) continue; //跳过重复数字
// i是第一个数 l 三元组中第二小 r 最大的
int l = i + 1, r = n - 1;
while (l < r) {
if (nums[i] + nums[l] + nums[r] == 0) {
res.push_back({nums[i], nums[l], nums[r]}); //加入一个正确方案
while (l < r && nums[l] == nums[l + 1]) l ++ ; //跳过重复数字
while (l < r && nums[r] == nums[r - 1]) r -- ;
l ++ ; //左指针前进
r -- ; //右指针后退
}
else if (nums[i] + nums[l] + nums[r] > 0) {
r -- ; //和大于0,要减少总和之值,即右指针后退 因为左指针不能再后退了
}
else {
l ++ ; //和小于0,要增加总和之值,即左指针前进, 因为右指针不能再前进了
}
}
}
return res;
}
};
17. 电话号码的字符组合
回溯法
class Solution {
public:
vector<string> letterCombinations(string digits) {
vector<string> combinations;
if (digits.empty()) {
return combinations;
}
unordered_map<char, string> phoneMap{
{'2', "abc"},
{'3', "def"},
{'4', "ghi"},
{'5', "jkl"},
{'6', "mno"},
{'7', "pqrs"},
{'8', "tuv"},
{'9', "wxyz"}
};
string combination;
backtrack(combinations,combination, phoneMap, digits, 0 );
return combinations;
}
void backtrack(vector<string>& combinations, string& combination,const unordered_map<char, string>& phoneMap, const string& digits, int index) {
if (index == digits.length()) { // 当前达到字符组合长度
combinations.push_back(combination);
} else {
char digit = digits[index];
const string& letters = phoneMap.at(digit); // 找到对应的letters
for (const char& letter: letters) {
combination.push_back(letter);
backtrack(combinations,combination,phoneMap, digits, index + 1);
combination.pop_back();
}
}
}
};
19. 删除链表的第N个结点
先遍历一遍得到整个链表的长度 然后从head走 len - n - 1次,到需要删除的结点的前一个结点,然后通过该结点删除目标结点,返回head
边界条件:当len - n == 0 即需要删除头结点 head = head -> next就可了
/**
* 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:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode *temp = head;
int len = 0;
while (temp) {
len++;
temp = temp -> next;
}
temp = head;
len = len - n;
if (len == 0) {
head = head -> next;
return head;
}
for (int i =0; i < len - 1; i++) {
temp = temp -> next;
}
temp -> next = temp -> next -> next;
return head;
}
};
20. 有效的括号
用栈来模拟 对于左括号压栈 右括号要检查栈状态
class Solution {
public:
bool isValid(string s) {
stack<char> stk;
for (auto c: s) {
if (c == ')' || c == '}' || c == ']') {
if (stk.size() == 0) return false;
char top = stk.top();
if (top == ')' || top == '}' || top == ']') return false;
switch (c) {
case ')':
if (top != '(') return false;
break;
case '}':
if (top != '{') return false;
break;
case ']':
if (top != '[') return false;
break;
}
stk.pop();
} else {
stk.push(c);
}
}
return stk.empty();
}
};
21.合并两个有序链表
递归 每次递归都意味着一次分割
/**
* 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:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
// 如果为空 意味着有一个到达了尽头 将这个剩下的接到前一个后面即可
if (l1 == NULL) {
return l2;
}
if (l2 == NULL) {
return l1;
}
// l1 结点小于l2结点
if (l1->val <= l2->val) {
l1->next = mergeTwoLists(l1->next, l2);
return l1;
}
// l1结点小于等于l2结点
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}
};
22. 括号生成
递归方法(类似深搜)
class Solution {
// 检查括号是否有效的一种方式
bool valid(const string& str) {
int balance = 0;
for (char c : str) {
if (c == '(') {
++balance;
} else {
--balance;
}
if (balance < 0) {
return false;
}
}
return balance == 0;
}
void generate_all(string& current, int n, vector<string>& result) {
// n是左右括号的个数为2 * n
if (n == current.size()) {
// 判断此次迭代的是否符合规则
if (valid(current)) {
result.push_back(current);
}
return;
}
// 此处两种方案 : 当前填'(' or 当前填 ')'
current += '(';
generate_all(current, n, result);
current.pop_back();
current += ')';
generate_all(current, n, result);
current.pop_back();
}
public:
vector<string> generateParenthesis(int n) {
vector<string> result;
string current;
generate_all(current, n * 2, result);
return result;
}
};
回溯方法
优化:跟踪判断当前括号序列是否有效 -- 根据左括号和右括号个数来判断
class Solution {
void backtrack(vector<string>& ans, string& cur, int open, int close, int n) {
if (cur.size() == n * 2) {
ans.push_back(cur);
return;
}
// 左括号小于n
if (open < n) {
cur.push_back('(');
backtrack(ans, cur, open + 1, close, n);
cur.pop_back();
}
// 右括号小于左括号
if (close < open) {
cur.push_back(')');
backtrack(ans, cur, open, close + 1, n);
cur.pop_back();
}
}
public:
vector<string> generateParenthesis(int n) {
vector<string> result;
string current;
backtrack(result, current, 0, 0, n);
return result;
}
};
23. 合并K个排序链表
K次合并 两个有序的链表
class Solution {
public:
ListNode* mergeTwoLists(ListNode *a, ListNode *b) {
if ((!a) || (!b)) return a ? a : b;
ListNode head, *tail = &head, *aPtr = a, *bPtr = b;
while (aPtr && bPtr) {
if (aPtr->val < bPtr->val) {
tail->next = aPtr; aPtr = aPtr->next;
} else {
tail->next = bPtr; bPtr = bPtr->next;
}
tail = tail->next;
}
tail->next = (aPtr ? aPtr : bPtr);
return head.next;
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
ListNode *ans = nullptr;
for (size_t i = 0; i < lists.size(); ++i) {
ans = mergeTwoLists(ans, lists[i]);
}
return ans;
}
};
24. 两两交换链表中的节点
递归
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if (head == nullptr || head->next == nullptr) {
return head;
}
ListNode* newHead = head->next; // newHead表示第二个节点
head->next = swapPairs(newHead->next);// 第一个节点指向下一个递归
newHead->next = head;// 将第二个节点的next 指向第一个
return newHead;
}
};
26. 删除有序数组中的重复项
双指针 一次循环
i指向当前下标 j指向循环
class Solution {
public int removeDuplicates(int[] nums) {
if (nums.length == 0) return 0;
int i = 0;
for (int j = 1; j < nums.length; j++) {
if (nums[j] != nums[i]) {
i++;
nums[i] = nums[j];
}
}
return i + 1;
}
}
31. 下一个排列
一个很秒的思路
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int i = nums.size() - 2, j = nums.size() - 1;
while(i >= 0 && nums[i] >= nums[i+1]) --i; //寻找比后面那个数小的nums[i]
if(i >= 0)
{
while(j >= 0 && nums[j] <= nums[i]) --j; //寻找比nums[i]大的第一个数
swap(nums[i], nums[j]);
}
sort(nums.begin() + i + 1, nums.end()); //如果不存在下一个排列,i为-1
}
};
198. 打家劫舍
简单dp
dp[i]表示偷前i家能得到的最大值
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i - 1])
class Solution {
public:
int rob(vector<int>& nums) {
int n = nums.size();
if (n == 0) return 0;
vector<int> dp(n + 1, 0); // dp[i]表示偷前i个房子的最大金额
dp[1] = nums[0];
for (int i = 2; i <= n; i++) {
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i - 1]);
}
return dp[n];
}
};
进一步对空间进行压缩
class Solution {
public:
int rob(vector<int>& nums) {
int n = nums.size();
if (n == 0) return 0;
//vector<int> dp(n + 1, 0); // dp[i]表示偷前i个房子的最大金额
//dp[1] = nums[0];
int cur = nums[0], pre = 0;
for (int i = 2; i <= n; i++) {
int temp = cur;
cur = max(cur, pre + nums[i - 1]);
pre = temp;
//dp[i] = max(dp[i - 1], dp[i - 2] + nums[i - 1]);
}
return cur;
}
};
常见面试题
自建链表
#include <iostream>
using namespace std;
struct node{
int data;
node *next;
node(int data,node *next=NULL){
this->data = data;
this->next = next;
}
};
node *createlist(const int num){
node *head = NULL;
for (int i = 0; i < num; i++) {
head = new node(i, head);
}
return head;
}
void displaylist(node *head){
cout<<"list node -> ";
while(head!=NULL){
cout<<head->data<<" ";
head = head->next;
}
cout<<endl;
}
int main(){
node *head = createlist(5);
displaylist(head);
return 0;
}
反转链表
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* prev = nullptr;
ListNode* curr = head;
while (curr) {
ListNode* next = curr->next;
curr->next = prev;
prev = curr;
curr = next;
}
return prev;
}
};
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if (!head || !head->next) {
return head;
}
ListNode* newHead = reverseList(head->next);
head->next->next = head;
head->next = nullptr;
return newHead;
}
};
环形链表
哈希表存放走过的记录
class Solution {
public:
bool hasCycle(ListNode *head) {
unordered_set<ListNode*> seen;
while (head != nullptr) {
if (seen.count(head)) {
return true;
}
seen.insert(head);
head = head->next;
}
return false;
}
};
快慢指针
class Solution {
public:
bool hasCycle(ListNode* head) {
if (head == nullptr || head->next == nullptr) {
return false;
}
ListNode* slow = head;
ListNode* fast = head->next;
while (slow != fast) {
if (fast == nullptr || fast->next == nullptr) {
return false;
}
slow = slow->next;
fast = fast->next->next;
}
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:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
// 如果为空 意味着有一个到达了尽头 将这个剩下的接到前一个后面即可
if (l1 == NULL) {
return l2;
}
if (l2 == NULL) {
return l1;
}
// l1 结点小于l2结点
if (l1->val <= l2->val) {
l1->next = mergeTwoLists(l1->next, l2);
return l1;
}
// l1结点小于等于l2结点
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}
};