力扣刷题笔记
记录5-6月力扣刷题,持续刷题中~
2024.05
15.三数之和
双指针或者哈希表,注意去重的操作要考虑仔细
class Solution {
public:vector<vector<int>> threeSum(vector<int>& nums) {vector<vector<int>> result;sort(nums.begin(), nums.end());// 找出a + b + c = 0// a = nums[i], b = nums[left], c = nums[right]for (int i = 0; i < nums.size(); i++) {// 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了if (nums[i] > 0) {return result;}// 错误去重a方法,将会漏掉-1,-1,2 这种情况/*if (nums[i] == nums[i + 1]) {continue;}*/// 正确去重a方法if (i > 0 && nums[i] == nums[i - 1]) {continue;}int left = i + 1;int right = nums.size() - 1;while (right > left) {// 去重复逻辑如果放在这里,0,0,0 的情况,可能直接导致 right<=left 了,从而漏掉了 0,0,0 这种三元组/*while (right > left && nums[right] == nums[right - 1]) right--;while (right > left && nums[left] == nums[left + 1]) left++;*/if (nums[i] + nums[left] + nums[right] > 0) right--;else if (nums[i] + nums[left] + nums[right] < 0) left++;else {result.push_back(vector<int>{nums[i], nums[left], nums[right]});// 去重逻辑应该放在找到一个三元组之后,对b 和 c去重while (right > left && nums[right] == nums[right - 1]) right--;while (right > left && nums[left] == nums[left + 1]) left++;// 找到答案时,双指针同时收缩right--;left++;}}}return result;}
};
时间复杂度:O(n^2)
空间复杂度:O(1)
18.四数之和
双指针法,三数之和扩展(多一层for循环)
class Solution {
public:vector<vector<int>> fourSum(vector<int>& nums, int target) {vector<vector<int>> result;sort(nums.begin(), nums.end());for (int k = 0; k < nums.size(); k++) {// 剪枝处理if (nums[k] > target && nums[k] >= 0) {break; // 这里使用break,统一通过最后的return返回}// 对nums[k]去重if (k > 0 && nums[k] == nums[k - 1]) {continue;}for (int i = k + 1; i < nums.size(); i++) {// 2级剪枝处理if (nums[k] + nums[i] > target && nums[k] + nums[i] >= 0) {break;}// 对nums[i]去重if (i > k + 1 && nums[i] == nums[i - 1]) {continue;}int left = i + 1;int right = nums.size() - 1;while (right > left) {// nums[k] + nums[i] + nums[left] + nums[right] > target 会溢出if ((long) nums[k] + nums[i] + nums[left] + nums[right] > target) {right--;// nums[k] + nums[i] + nums[left] + nums[right] < target 会溢出} else if ((long) nums[k] + nums[i] + nums[left] + nums[right] < target) {left++;} else {result.push_back(vector<int>{nums[k], nums[i], nums[left], nums[right]});// 对nums[left]和nums[right]去重while (right > left && nums[right] == nums[right - 1]) right--;while (right > left && nums[left] == nums[left + 1]) left++;// 找到答案时,双指针同时收缩right--;left++;}}}}return result;}
};
时间复杂度:O(n^3)
空间复杂度:O(1)
503.下一个更大的元素II
单调栈的想法,相比较739.每日温度,需要多处理一步循环数组问题
class Solution {
public:vector<int> nextGreaterElements(vector<int>& nums) {vector<int> result(nums.size(), -1);if (nums.size() == 0) return result;stack<int> st;for (int i = 0; i < nums.size() * 2; i++) {// 模拟遍历两边nums,注意一下都是用i % nums.size()来操作while (!st.empty() && nums[i % nums.size()] > nums[st.top()]) {result[st.top()] = nums[i % nums.size()];st.pop();}st.push(i % nums.size());}return result;}
};
102.二叉树的层序遍历
使用队列进行层序遍历
class Solution {
public:vector<vector<int>> levelOrder(TreeNode* root) {queue<TreeNode*> que;if (root != NULL) que.push(root);vector<vector<int>> result;while (!que.empty()) {int size = que.size();vector<int> vec;// 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的for (int i = 0; i < size; i++) {TreeNode* node = que.front();que.pop();vec.push_back(node->val);if (node->left) que.push(node->left);if (node->right) que.push(node->right);}result.push_back(vec);}return result;}
};
使用递归法
class Solution {
public:void order(TreeNode* cur, vector<vector<int>>& result, int depth){if (cur == nullptr) return;if (result.size() == depth) result.push_back(vector<int>());result[depth].push_back(cur->val);order(cur->left, result, depth + 1);order(cur->right, result, depth + 1);}vector<vector<int>> levelOrder(TreeNode* root) {vector<vector<int>> result;int depth = 0;order(root, result, depth);return result;}
};
以上可以作为模板,对于107.二叉树的层序遍历II,199.二叉树的右视图,637.二叉树的层平均值等问题可以相似地解决
117.填充每个结点的下一个右侧结点指针II
// DFS
class Solution {vector<Node *> pre;
public:Node *connect(Node *root) {dfs(root, 0); // 根节点的深度为 0return root;}void dfs(Node *node, int depth) {if (node == nullptr) {return;}if (depth == pre.size()) { // node 是这一层最左边的节点pre.push_back(node);} else { // pre[depth] 是 node 左边的节点pre[depth]->next = node; // node 左边的节点指向 nodepre[depth] = node;}dfs(node->left, depth + 1);dfs(node->right, depth + 1);}
};// BFS
class Solution {
public:Node *connect(Node *root) {if (root == nullptr) {return nullptr;}vector<Node*> q = {root};while (!q.empty()) {vector<Node*> nxt;for (int i = 0; i < q.size(); i++) {Node *node = q[i];if (i) { // 连接同一层的两个相邻节点q[i - 1]->next = node;}if (node->left) {nxt.push_back(node->left);}if (node->right) {nxt.push_back(node->right);}}q = move(nxt);}return root;}
};// BFS + 链表
class Solution {
public:Node *connect(Node *root) {Node *dummy = new Node();Node *cur = root;while (cur) {dummy->next = nullptr;Node *nxt = dummy; // 下一层的链表while (cur) { // 遍历当前层的链表if (cur->left) {nxt->next = cur->left; // 下一层的相邻节点连起来nxt = cur->left;}if (cur->right) {nxt->next = cur->right; // 下一层的相邻节点连起来nxt = cur->right;}cur = cur->next; // 当前层链表的下一个节点}cur = dummy->next; // 下一层链表的头节点}delete dummy;return root;}
};
77.组合问题
class Solution {
private:vector<vector<int>> res;vector<int> path;void backtracking(int n, int k, int startIndex) {if (path.size() == k) {res.push_back(path);return;}for (int i = startIndex; i <= n; i++) {path.push_back(i);backtracking(n, k, i + 1); // 从当前数的下一个数开始path.pop_back();}}public:vector<vector<int>> combine(int n, int k) {// k个数若是k层for循环暴力遍历,则不好写出,故用回溯backtracking(n, k, 1); // 包含从1到n的数return res;}
};
class Solution {
public:vector<vector<int>> combine(int n, int k) {vector<vector<int>> ans;vector<int> path;function<void(int)> dfs = [&](int i) {int d = k - path.size(); // 还要选 d 个数if (d == 0) {ans.emplace_back(path);return;}for (int j = i; j >= d; --j) {path.push_back(j);dfs(j - 1);path.pop_back();}};dfs(n);return ans;}
};
复杂度分析:
- 时间复杂度:分析回溯问题时间复杂度,有个通用的公式:路径长度 x 搜索树的叶子数,对于本题,它等于O(k*C(n,k))
- 空间复杂度:O(k)
53.最大子数组和
动态规划求解,也有贪心的思想,局部最优推导全局最优,在遇到加上负值让结果不如下一个值的时候,重新选择开头值
class Solution {
public:int maxSubArray(vector<int>& nums) {//动态规划,子问题定义为以nums[i]为末尾的最大子数组和,判断当前数是否大于0if (nums.size() == 0) return 0;vector<int> dp(nums.size(), 0); // dp[i]表示包括i之前的最大连续子序列和dp[0] = nums[0];int result = dp[0];for (int i = 1; i < nums.size(); i++) {dp[i] = max(dp[i - 1] + nums[i], nums[i]); // 遇到更大的值或者说原来值为负,重新记录值if (dp[i] > result) result = dp[i]; // result 保存dp[i]的最大值}return result;}
};
941.有效的山脉数组
数组越界,单调问题,考虑少了,提交了4次。。。
class Solution {
public:bool validMountainArray(vector<int>& arr) {if (arr.size() < 3) return false;int left = 0, right = arr.size() - 1;while (left < arr.size() - 1 && arr[left] < arr[left + 1]) left++; // 注意数组越界问题while (right > 0 && arr[right] < arr[right - 1]) right--;if (left == right && left != 0 && right != arr.size() - 1) return true; // 单调递增和单调递减不算return false;}
};
1002.查找常用字符
哈希表,通过构造数组来模拟
class Solution {
public:vector<string> commonChars(vector<string>& A) {vector<string> result;if (A.size() == 0) return result;int hash[26] = {0}; // 用来统计所有字符串里字符出现的最小频率for (int i = 0; i < A[0].size(); i++) { // 用第一个字符串给hash初始化hash[A[0][i] - 'a']++;}int hashOtherStr[26] = {0}; // 统计除第一个字符串外字符的出现频率for (int i = 1; i < A.size(); i++) {memset(hashOtherStr, 0, 101; // 题中字符串长度<=100for (int j = 0; j < A[i].size(); j++) {hashOtherStr[A[i][j] - 'a']++;}// 更新hash,保证hash里统计26个字符在所有字符串里出现的最小次数for (int k = 0; k < 26; k++) {hash[k] = min(hash[k], hashOtherStr[k]);}}// 将hash统计的字符次数,转成输出形式for (int i = 0; i < 26; i++) {while (hash[i] != 0) { // 注意这里是while,多个重复的字符string s(1, i + 'a'); // char -> stringresult.push_back(s);hash[i]--;}}return result;}
};
class Solution {
private:
vector<vector<string>> result;// n 为输入的棋盘大小
// row 是当前递归到***的第几行了
void backtracking(int n, int row, vector<string>& chessboard) {if (row == n) {result.push_back(chessboard);return;}for (int col = 0; col < n; col++) {if (isValid(row, col, chessboard, n)) { // 验证合法就可以放chessboard[row][col] = 'Q'; // 放置皇后backtracking(n, row + 1, chessboard);chessboard[row][col] = '.'; // 回溯,撤销皇后}}
}
bool isValid(int row, int col, vector<string>& chessboard, int n) {int count = 0;// 检查列for (int i = 0; i < row; i++) { // 这是一个剪枝if (chessboard[i][col] == 'Q') {return false;}}// 检查 45度角是否有皇后for (int i = row - 1, j = col - 1; i >=0 && j >= 0; i--, j--) {if (chessboard[i][j] == 'Q') {return false;}}// 检查 135度角是否有皇后for(int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {if (chessboard[i][j] == 'Q') {return false;}}return true;
}
public:vector<vector<string>> solveNQueens(int n) {// 声明所用变量再使用vector<string> chessboard(n, string(n, '.'));backtracking(n, 0, chessboard);return result;}
};
52.N皇后
class Solution {private:int res = 0;// vector<vector<string>> way;void backtracking(int n, int row, vector<string>& chessboard) {if (row == n) {// way.push_back(chessboard);res++;return;}for (int col = 0; col < n; col++) {if (isValid(n, row, col, chessboard)) {chessboard[row][col] = 'Q';backtracking(n, row + 1, chessboard);chessboard[row][col] = '.';} }}bool isValid(int n, int row, int col, vector<string>& chessboard) {for (int i = 0; i < row; i++) { // 检查列if (chessboard[i][col] == 'Q') return false;}for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) { // 检查副对角线if (chessboard[i][j] == 'Q') return false; }for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) { // 检查主对角线if (chessboard[i][j] == 'Q') return false;}return true;}public:int totalNQueens(int n) {vector<string> chessboard(n, string(n, '.'));backtracking(n, 0, chessboard);return res;}
};
-
时间复杂度:O(n!),n为皇后的数量
-
空间复杂度:O(n)
673.最长递增子序列的个数
class Solution {
public:int findNumberOfLIS(vector<int>& nums) {if (nums.size() <= 1) return nums.size();vector<int> dp(nums.size(), 1);vector<int> count(nums.size(), 1);int maxCount = 0;for (int i = 1; i < nums.size(); i++) {for (int j = 0; j < i; j++) {if (nums[i] > nums[j]) {if (dp[j] + 1 > dp[i]) {dp[i] = dp[j] + 1;count[i] = count[j];} else if (dp[j] + 1 == dp[i]) {count[i] += count[j];}}if (dp[i] > maxCount) maxCount = dp[i];}}int result = 0;for (int i = 0; i < nums.size(); i++) {if (maxCount == dp[i]) result += count[i];}return result;}
};
- 时间复杂度:O(n^2)
- 空间复杂度:O(n)
1971.寻找图中是否存在路径
并查集主要有三个功能。
- 寻找根节点,函数:find(int u),也就是判断这个节点的祖先节点是哪个
- 将两个节点接入到同一个集合,函数:join(int u, int v),将两个节点连在同一个根节点上
- 判断两个节点是否在同一个集合,函数:isSame(int u, int v),就是判断两个节点是不是同一个根节点
这道题目是并查集基础题目,题目中各个点是双向图链接,那么判断 一个顶点到另一个顶点有没有有效路径其实就是看这两个顶点是否在同一个集合里。
class Solution {
private:int n = 200001; // 节点数量 <= 20000vector<int> father = vector<int> (n, 0); // C++里的一种数组结构// 并查集初始化void init() {for (int i = 0; i < n; ++i) { father[i] = i;}}// 并查集里寻根的过程int find(int u) {return u == father[u] ? u : father[u] = find(father[u]);}// 判断 u 和 v是否找到同一个根bool isSame(int u, int v) {u = find(u);v = find(v);return u == v;}// 将v->u 这条边加入并查集void join(int u, int v) {u = find(u); // 寻找u的根v = find(v); // 寻找v的根if (u == v) return ; // 如果发现根相同,则说明在一个集合,不用两个节点相连直接返回father[v] = u;}
public:bool validPath(int n, vector<vector<int>>& edges, int source, int destination) {init();for (int i = 0; i < edges.size(); i++) {join(edges[i][0], edges[i][1]);}return isSame(source, destination);}
};
684.冗余连接
树可以看作是一个连通且无环的无向图
class Solution {
private:int n = 1001; // 节点数量3 到 1000vector<int> father = vector<int> (n, 0); // C++里的一种数组结构// 并查集初始化void init() {for (int i = 0; i < n; ++i) {father[i] = i;}}// 并查集里寻根的过程int find(int u) {return u == father[u] ? u : father[u] = find(father[u]);}// 判断 u 和 v是否找到同一个根bool isSame(int u, int v) {u = find(u);v = find(v);return u == v;}// 将v->u 这条边加入并查集void join(int u, int v) {u = find(u); // 寻找u的根v = find(v); // 寻找v的根if (u == v) return ; // 如果发现根相同,则说明在一个集合,不用两个节点相连直接返回father[v] = u;
}
public:vector<int> findRedundantConnection(vector<vector<int>>& edges) {init();for (int i = 0; i < edges.size(); i++) {if (isSame(edges[i][0], edges[i][1])) return edges[i];else join(edges[i][0], edges[i][1]);}return {};}
};
2024.06
2928.给小朋友们分糖果I
容斥原理,列出所有符合的情况再减去不符合的情况
class Solution {
public:int c2(int n) {return n > 1 ? n * (n - 1) / 2 : 0;}int distributeCandies(int n, int limit) { // n个糖果分3堆,在n+2(n+3-1)个空位中放挡板int res = 0;res = c2(n + 2) - 3 * c2(n + 2 - (limit + 1)) + 3 * c2(n + 2 - 2 * (limit + 1)) - c2(n + 2 - 3 * (limit + 1));return res;}// int distributeCandies(int n, int limit) { // 嵌套for循环,i,j和n-i-j都需要小于等于limit// int ans = 0;// for (int i = 0; i <= limit; i++) {// for (int j = 0; j <= limit; j++) {// if (i + j > n) break; // 不符情况// if (n - i - j <= limit) ans++;// }// }// return ans;// }
};
复杂度分析
-
时间复杂度:O(1)
-
空间复杂度:O(1)
669.修剪二叉搜索树
递归三部曲:
- 确定递归函数的参数以及返回值
- 确定终止条件
- 确定单层递归的逻辑
class Solution {
public:TreeNode* trimBST(TreeNode* root, int low, int high) {if (root == nullptr ) return nullptr;if (root->val < low) {TreeNode* right = trimBST(root->right, low, high); // 寻找符合区间[low, high]的节点return right;}if (root->val > high) {TreeNode* left = trimBST(root->left, low, high); // 寻找符合区间[low, high]的节点return left;}root->left = trimBST(root->left, low, high); // root->left接入符合条件的左孩子root->right = trimBST(root->right, low, high); // root->right接入符合条件的右孩子return root;}
};
复杂度分析:
- 时间复杂度:O(n),其中n为二叉树的结点数目
- 空间复杂度:O(n),递归栈最坏情况下需要O(n)的空间
15.三数之和
问题是考虑的不全,题意是三元组中元素可以重复,三元组不可重复
class Solution {
public:vector<vector<int>> threeSum(vector<int>& nums) {vector<vector<int>> result;sort(nums.begin(), nums.end());// 找出a + b + c = 0// a = nums[i], b = nums[left], c = nums[right]for (int i = 0; i < nums.size(); i++) {// 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了if (nums[i] > 0) {return result;}// 错误去重a方法,将会漏掉-1,-1,2 这种情况/*if (nums[i] == nums[i + 1]) {continue;}*/// 正确去重a方法if (i > 0 && nums[i] == nums[i - 1]) {continue;}int left = i + 1;int right = nums.size() - 1;while (right > left) {// 去重复逻辑如果放在这里,0,0,0 的情况,可能直接导致 right<=left 了,从而漏掉了 0,0,0 这种三元组(0,0,0,0,0)/*while (right > left && nums[right] == nums[right - 1]) right--;while (right > left && nums[left] == nums[left + 1]) left++;*/if (nums[i] + nums[left] + nums[right] > 0) right--;else if (nums[i] + nums[left] + nums[right] < 0) left++;else {result.push_back(vector<int>{nums[i], nums[left], nums[right]});// 去重逻辑应该放在找到一个三元组之后,对b 和 c去重while (right > left && nums[right] == nums[right - 1]) right--;while (right > left && nums[left] == nums[left + 1]) left++;// 找到答案时,双指针同时收缩right--;left++;}}}return result;}
};
- 时间复杂度:排序算法nlogn,for循环和双指针复杂度n^2,所以时间复杂度O(n)
- 空间复杂度:O(1),仅仅用到若干变量
312.戳气球
// 记忆化搜索方法
class Solution {
public:vector<vector<int>> rec;vector<int> val;public:int solve(int left, int right) {if (left >= right - 1) { // 递归结束条件return 0;}if (rec[left][right] != -1) {return rec[left][right];}for (int i = left + 1; i < right; i++) {int sum = val[left] * val[i] * val[right];sum += solve(left, i) + solve(i, right);rec[left][right] = max(rec[left][right], sum);}return rec[left][right];}int maxCoins(vector<int>& nums) {int n = nums.size();val.resize(n + 2);for (int i = 1; i <= n; i++) {val[i] = nums[i - 1];}val[0] = val[n + 1] = 1;rec.resize(n + 2, vector<int>(n + 2, -1));return solve(0, n + 1);}
};// 动态规划方法
class Solution {
public:int maxCoins(vector<int>& nums) {// 先将num[-1]和nums[n] = 1 添加到数组int n = nums.size() + 2;nums.insert(nums.begin(), 1);nums.push_back(1);//dp[0][n - 1]为最后要求的答案,假设最后戳破的气球为第i个,状态转移方程://dp[start][end] = dp[start][i] + dp[i][end] + nums[start] * nums[i] * nums[end] (start < i < end)//从状态转移方程看出,每次需要后面遍历的结果,即dp[start][i]和dp[i][end],不需要管start前面的值vector<vector<int>> dp(n, vector<int>(n, 0));for (int start = n - 1; start >= 0; start--) {for (int end = start; end < n; end++) {if (end - start < 2) continue; // 没有气球可以戳for (int i = start + 1; i < end; i++) {dp[start][end] = max(dp[start][end], dp[start][i] + dp[i][end] + nums[start] * nums[i] * nums[end]);}}} return dp[0][n - 1];}
};
复杂度分析:
- 时间复杂度:O(n3),其中n是气球数量,状态数为n2,状态转移复杂度为O(n3)
- 空间复杂度:O(n2),n为气球数量
24.两两交换链表中的结点
class Solution {
public:ListNode *swapPairs(ListNode* head) {auto dummy = new ListNode(0, head);auto node0 = dummy;auto node1 = head;while (node1 && node1->next) {auto node2 = node1->next;auto node4 = node2->next;node0->next = node2; // 0->2node2->next = nede1; // 2->1node1->next = node3; // 1->3node0 = node1; // 下一轮交换,0是1node1 = node3; // 下一轮交换,1是3}return dummy->next; // 返回新链表的头结点}
};
类似于上述方法,使用递归
class Solution {
public:ListNode *swapPairs(ListNode* head) {// 递归结束条件if (head == nullptr || head->next == nullptr) return head;auto node1 = head;auto node2 = node1->next;auto node3 = node2->next;node1->next = swapPairs(node3); // 指向递归返回的头结点node2->next = node1; // 2->1return node2; // 返回交换后新的头结点}
};
复杂度分析
- 时间复杂度:O(n),其中n为链表的长度;
- 空间复杂度:O(n),递归需O(n)的栈空间。
151.反转字符串中的单词
双指针,主要处理多余空格和反转字符串
class Solution {
public:string reverseWords(string s) {int m = s.size() - 1;string res;while (s[m] == ' ' && m > 0) m--;int n = m; // 移除末尾空格后,字符串长度while (m >= 0) {while (m >= 0 && s[m] != ' ') m--;res += s.substr(m + 1, n - m) + ' '; // 获取单词并加上空格while (m >= 0 && s[m] == ' ') m--; // 获取下一个单词末尾位置n = m;}return res.substr(0, res.size() - 1); // 忽略最后一位的空格}
};
代码随想录参考代码
class Solution {
public:void reverse(string& s, int start, int end) { // 看下344.翻转字符串for (int i = start, j = end; i < j; i++, j--) {swap(s[i], s[j]);}}void removeExtraSpaces(string& s) { // 去除所有空格并在相邻单词之间添加空格, 快慢指针。int slow = 0; // 看下27.移除元素for (int i = 0; i < s.size(); ++i) {if (s[i] != ' ') { // 遇到非空格就处理,即删除所有空格。if (slow != 0) s[slow++] = ' '; // 手动控制空格,给单词之间添加空格。slow != 0说明不是第一个单词,需要在单词前添加空格。while (i < s.size() && s[i] != ' ') { // 补上该单词,遇到空格说明单词结束。s[slow++] = s[i++];}}}s.resize(slow); // slow的大小即为去除多余空格后的大小。}string reverseWords(string s) {removeExtraSpaces(s); // 去除多余空格,保证单词之间之只有一个空格,且字符串首尾没空格。reverse(s, 0, s.size() - 1);int start = 0; // removeExtraSpaces后保证第一个单词的开始下标一定是0。for (int i = 0; i <= s.size(); ++i) {if (i == s.size() || s[i] == ' ') { //到达空格或者串尾,说明一个单词结束。进行翻转。reverse(s, start, i - 1); //翻转,注意是左闭右闭 []的翻转。start = i + 1; //更新下一个单词的开始下标start}}return s;}
};
时间复杂度:O(n)
空间复杂度:O(n)
28.找出字符串中第一个匹配项的下标
暴力遍历,直接寻找是否有匹配项
class Solution {
public:int strStr(string haystack, string needle) {// 字符串匹配项// return haystack.find(needle);int m = haystack.size();int n = needle.size();for (int i = 0; i + n <= m; i++) { // 注意这里<=,因为可能两个字符串都是一个字符bool flag = true;for (int j = 0; j < n; j++) {if (haystack[i + j] != needle[j]) {flag = false;break;}}if (flag) return i; // 一次遍历完是否都匹配}return -1;}
};
KMP算法
最长前后缀:最长的不包含最后一个字符的所有以第一个字符开头的连续子串,后缀是不包含第一个字符的所有以最后一个字符结尾的连续子串
class Solution {
public:void getNext(int* next, const string& s) {int j = -1;next[0] = j;for (int i = 1; i < s.size(); i++) { // i指向文本串,j+1指向模式串while (j >= 0 && s[i] != s[j + 1]) { // 不相等向前回退j = next[j];}if (s[i] == s[j + 1]) { // 找到相同的前后缀j++;}next[i] = j; // 将j的长度赋给next[i]}}int strStr(string haystack, string needle) {if (needle.size() == 0) {return 0;}vector<int> next(needle.size());getNext(&next[0], needle);int j = -1;for (int i = 0; i < haystack.size(); i++) {while (j >= 0 && haystack[i] != needle[j + 1]) {j = next[j];}if (haystack[i] == needle[j + 1]) {j++;}if (j == (needle.size() - 1)) {return (i - needle.size() + 1);}}return -1;}
}
- 时间复杂度:O(n + m)
- 空间复杂度:O(m),只需要保存字符串needle的前缀表
257.二叉树的所有路径
// 深度优先搜索方法
class Solution {
public:void construct_paths(TreeNode* root, string path, vector<string>& paths) {if (root != nullptr) {path += to_string(root->val);if (root->left == nullptr && root->right == nullptr) { // 当前节点是叶子节点paths.push_back(path); // 把路径加入到答案中} else {path += "->"; // 当前节点不是叶子节点,继续递归遍历construct_paths(root->left, path, paths);construct_paths(root->right, path, paths);}}}vector<string> binaryTreePaths(TreeNode* root) {vector<string> paths;construct_paths(root, "", paths);return paths;}
};// 广度优先搜索方法
class Solution {
public:vector<string> binaryTreePaths(TreeNode* root) {vector<string> paths;if (root == nullptr) {return paths;}queue<TreeNode*> node_queue;queue<string> path_queue;node_queue.push(root);path_queue.push(to_string(root->val));while (!node_queue.empty()) {TreeNode* node = node_queue.front(); string path = path_queue.front();node_queue.pop();path_queue.pop();if (node->left == nullptr && node->right == nullptr) {paths.push_back(path);} else {if (node->left != nullptr) {node_queue.push(node->left);path_queue.push(path + "->" + to_string(node->left->val));}if (node->right != nullptr) {node_queue.push(node->right);path_queue.push(path + "->" + to_string(node->right->val));}}}return paths;}
};
- 时间复杂度:O(n2)
- 空间复杂度:O(n2)
700.二叉搜索树中的搜索
写个简单题
// 递归法
class Solution {
public:TreeNode* searchBST(TreeNode* root, int val) {if (root == nullptr || root->val == val) return root;if (root->val > val) return searchBST(root->left, val); // 二叉搜索树的性质,有序性if (root->val < val) return searchBST(root->right, val);return nullptr; // 没找到}
}// 迭代法
class Solution {
public:TreeNode* searchBST(TreeNode* root, int val) {while (root != nullptr) {if (root->val > val) root = root->left;else if (root->val < val) root = root->right;else return root; // 搜索到结点}return nullptr;}
}
迭代法时间复杂度
- 时间复杂度:O(n),其中n是二叉搜索树的节点数。最坏情况下二叉搜索树是一条链,且要找的元素比链末尾的元素值还要小(大),这种情况下我们需要迭代n次。
- 空间复杂度:O(1),没有用到额外空间。
相关文章:
力扣刷题笔记
记录5-6月力扣刷题,持续刷题中~ 2024.05 15.三数之和 双指针或者哈希表,注意去重的操作要考虑仔细 class Solution { public:vector<vector<int>> threeSum(vector<int>& nums) {vector<vector<int>> result;sort…...
【JS逆向百例】某点数据逆向分析,多方法详解
前言 最近收到粉丝的私信,其在逆向某个站点时遇到了些问题,在查阅资料未果后,来询问K哥,K哥一向会尽力满足粉丝的需求。网上大多数分析该站点的教程已经不再适用,本文K哥将提供 3 种解决方案,对于 webpack…...
windows系统docker镜像导出
docker镜像导入导出(windows)_windowdocker下载镜像导出-CSDN博客https://blog.csdn.net/qq_22211217/article/details/93936363...
selenium前期准备
驱动地址: a. chromedriver:https://googlechromelabs.github.io/chrome-for-testing/ b. https://registry.npmmirror.com/binary.html?pathchromedriver/ selenium原理:Selenium 是一个用于自动化测试 Web 应用程序的工具集 a. 浏览器驱动࿰…...
[Python人工智能] 四十六.PyTorch入门 (1)环境搭建、神经网络普及和Torch基础知识
从本专栏开始,作者正式研究Python深度学习、神经网络及人工智能相关知识。前文讲解合如何利用keras和tensorflow构建基于注意力机制的CNN-BiLSTM-ATT-CRF模型,并实现中文实体识别研究。这篇文章将介绍PyTorch入门知识。前面我们的Python人工智能主要以TensorFlow和Keras为主,…...
示例:推荐一个应用Adorner做的通知和提示消息对话框
一、目的:在开发过程中,增加一些提示消息可以很好的提高用户体验,下面介绍一个用于增加提示消息的库 二、效果如下 可以看到右侧顶端弹出提示消息,消息间隔3s自动退出 三、环境 VS2022 Net7 四、使用方式 安装nuget包ÿ…...
nvdiadocker相关配置S3Gaussian
https://download.csdn.net/download/sinat_21699465/89458214 dockerfile文件参考: https://download.csdn.net/download/sinat_21699465/89458214 prework: 显卡驱动决定了cuda版本支持的上限。例如nvdia535驱动最高支持cuda12.2所以显卡驱动版本选…...
【科技前沿】电子设计新贵SmartEDA:为何它引领行业风潮?
在当今这个电子科技日新月异的时代,电子设计工具如同设计师的魔法棒,不断推动着产品创新的速度。而近期,一款名为SmartEDA的电子国产设计仿真软件异军突起,成为了行业内的新宠。那么,SmartEDA究竟有何过人之处…...
免费悬浮翻译器哪个好?测评5款悬浮翻译器
在享受休闲时光时,我们通常都希望不被打扰,对吧? 然而,有时打扰我们的并非是外界的干扰,而是在观看外语视频时,无法理解视频内容的烦躁感。 不过,今天本文将为大家揭开几款屏幕悬浮翻译软件的…...
压缩文件解压方法总结
在日常工作和生活中,压缩文件已经成为我们传输和存储大文件的常见方式。压缩文件可以将多个文件或文件夹打包成一个文件,并通过压缩算法减小文件的体积,从而节省存储空间和传输时间。收到压缩文件后,我们需要将其解压才能查看和使…...
探索Elastic Search:强大的开源搜索引擎,详解及使用
🎬 鸽芷咕:个人主页 🔥 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活! 引入 全文搜索属于最常见的需求,开源的 Elasticsearch (以下简称 Elastic)是目前全文搜索引…...
vue中的代码分割
随着Web应用的日益复杂化,用户对页面加载速度的期望越来越高。在这种背景下,前端性能优化成为了开发者们必须面对的挑战。Vue.js,作为现代前端开发的首选框架之一,其轻量级和灵活性为构建高性能的Web应用提供了可能。然而…...
java课程设计GUI学生信息管理系统
目录 系统内容.. 3 用户界面模块... 4 数据存储模块... 4 信息管理模块... 4 管理模块.. 4 主要模块的算法描述... 4 –简要的语言描述... 4 运行及调试分析(测试数据及测试结果).. 5 课程设计总结... 7 参考文献(至少三个…...
一网通办怎么办?一网统管怎么管?
一网通办怎么办?一网统管怎么管? 下面资源来源于网络,如有侵权请联络删除! **一网通办与一网统管的建设背景、建设情况及建设意义** 一、建设背景随着信息技术的飞速发展,传统的政务服务方式已难以满足人民群众日益增长的需求。各部门信息系统独立运行,导致信息孤岛现象…...
Kubernetes Dashboard
Minikube 环境搭建 Kubernetes 的基本架构 Kubernetes 声明式语言 YAML YAML操作Kubernetes核心对象 CentOs搭建Kubernetes集群 Kubernetes进阶对象Deployment、DaemonSet、Service Kubernetes进阶对象Ingress、Ingress Class、Ingress Controller Kubernetes集群部署项目实践 …...
NSSCTF-Web题目15
目录 [HNCTF 2022 WEEK2]ez_SSTI 1、题目 2、知识点 3、思路 [SWPUCTF 2022 新生赛]Ez_upload 1、题目 2、知识点 3、思路 [HNCTF 2022 WEEK2]ez_SSTI 1、题目 2、知识点 SSTI、Jinja2 参考链接:1. SSTI(模板注入)漏洞(…...
每天认识:轮询和中断
轮询(Polling)和中断(Interrupt)是两种不同的事件处理机制,通常用于操作系统、硬件设备或软件程序中,以响应外部事件或内部状态变化。下面分别解释这两个概念: 轮询(Polling&#x…...
SpringBoot中使用MQTT实现消息的订阅和发布
SpringBoot中使用MQTT实现消息的订阅和发布 背景 java框架SpringBoot通过mQTT通信 控制物联网设备 还是直接上代码 第一步依赖: <!--mqtt相关依赖--><dependency><groupId>org.springframework.integration</groupId><artifactId>s…...
等保测评练习10
等级保护初级测评师试题10 姓名: 成绩: 判断题(10110分) 1.等级保护2.0三级系统测评合格最低分为60分() 70分且不能有高风险 2.当远程管理云计算平台中设备是…...
VBA学习(16):工作表事件示例:输入数据后锁定单元格
在工作表单元格中输入数据后,该单元格就被锁定,不能再编辑。 打开VBE,在工程资源管理器中双击该工作表名称打开其代码模块,在其中输入下面的代码: 假设整个工作表的LockedFalse Private Sub Worksheet_Change(ByVal …...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...
