力扣刷题笔记

记录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.寻找图中是否存在路径

并查集主要有三个功能。

  1. 寻找根节点,函数:find(int u),也就是判断这个节点的祖先节点是哪个
  2. 将两个节点接入到同一个集合,函数:join(int u, int v),将两个节点连在同一个根节点上
  3. 判断两个节点是否在同一个集合,函数: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),没有用到额外空间。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/859590.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

什么是多态?一文彻底搞懂!

什么是多态 面向对象程序设计有三要素&#xff1a;封装、继承&#xff08;或组合&#xff09;、多态&#xff0c;前两者较好理解&#xff0c;多态总让人困惑&#xff0c;不知道具体有什么作用&#xff0c;更不知道为什么要用多态。今天就来详细分析下什么是多态&#xff0c;以…

众所周知,配置即代码≠基础设置即代码

​前段时间翻到几条留言&#xff0c;问&#xff1a; “配置即代码和基础设施即代码一样吗&#xff1f;” “配置即代码是什么&#xff1f;怎么都是基础设施即代码&#xff1f;” 我们都是知道&#xff0c;DevOp的快速发展&#xff0c;让服务器管理与配置的时间大大减少&#x…

玩客云刷海纳思(记录下踩坑过程)

一、准备材料 1、硬件&#xff1a; 玩客云设备 至少1台&#xff0c;带电源线&#xff1b; USB公对公线&#xff0c;1条&#xff08;可网购或者自制&#xff0c;百度自制usb公对公就好了&#xff09;&#xff1b; 带usb2.0接口的电脑&#xff0c;1台&#xff0c;大部分电脑都…

基于 ROS 的 Terraform 托管服务轻松部署文本转语音系统 ChatTTS

介绍 ChatTTS是专门为对话场景设计的文本转语音模型&#xff0c;例如LLM助手对话任务。它支持英文和中文两种语言。最大的模型使用了10万小时以上的中英文数据进行训练。ChatTTS webUI & API 为 ChatTTS 提供了网页界面和API服务。 资源编排服务&#xff08;Resource Orc…

应用分发也叫APP分发

应用分发&#xff08;App Distribution&#xff09;或APP分发&#xff0c;通常指的是将移动应用程序&#xff08;如iOS、Android或其他平台的应用&#xff09;通过各种渠道提供给最终用户进行下载和安装的过程。这个过程涉及多个环节&#xff0c;包括应用开发、测试、发布、推广…

构建LangChain应用程序的示例代码:38、自主RAG的概念及其实现方法,使用LangChain和OpenAI工具从头开始构建一个结合检索和生成的系统

# 安装必要的库 ! pip install langchain_community tiktoken langchain-openai langchainhub chromadb langchain langgraph自主RAG (Self-RAG) 自主RAG是最近的一篇论文&#xff0c;介绍了一种用于主动RAG的有趣方法。 该框架训练单个任意的语言模型&#xff08;如LLaMA2-7…

Linux文本三剑客sed

sed和awk grep就是查找文本当中的内容&#xff0c;最强大的功能就是使用扩展正则表达式 sed sed是一种流编辑器&#xff0c;一次处理一行内容。 如果只是展示&#xff0c;会放在缓冲区&#xff08;模式空间&#xff09;&#xff0c;展示结束后&#xff0c;会从模式空间把结…

信息检索(52):From doc2query to docTTTTTquery

From doc2query to docTTTTTquery 摘要 发布时间&#xff08;2019&#xff09; 摘要 Nogueira 等人 [7] 使用简单的序列到序列转换器 [9] 进行文档扩展。我们用 T5 [8] 替换转换器&#xff0c;并观察到效率大幅提升。 doc2query [7] 是一种文档扩展形式&#xff0c;其理念是…

高清录屏软件哪个好用?分享这3款录屏软件

在我们的日常生活和工作中&#xff0c;视频录制是我们经常需要使用到的功能。不仅可以帮助我们录制网课内容方便后期复习&#xff0c;还可以录制视频会议&#xff0c;记录上级指示&#xff0c;甚至可以通过录制一些视频教程或游戏视频上传网络进行分享。 对电脑屏幕进行录制&a…

【GD32F303红枫派使用手册】第二十四节 DHT11温湿度传感器检测实验

24.1 实验内容 通过本实验主要学习以下内容&#xff1a; DHT11操作原理 单总线GPIO模拟操作原理 24.2 实验原理 HT11是一款已校准数字信号输出的温湿度一体化数字传感器。该产品具有品质卓越、超快响应、抗干扰能力强、性价比极高等优点信号&#xff0c;传输距离可达20米以…

Codeforces Round 954 (Div. 3) (A~F)(不会数学)

A - X Axis 暴力枚举一下所有可能 void solve() {int a , b , c;cin >> a >> b >> c;int ans 100;for(int i 0 ; i < 10 ; i ){ans min(ans , abs(i - a) abs(i - b) abs(i - c));} cout << ans << endl; } B - Matrix Stabiliz…

【数据分享】2006-2023《中国贸易外经统计年鉴》

公众号新功能 目前公众号新增以下等功能 1、处理GIS出图、Python制图、区位图、土地利用现状图、土地利用动态度和重心迁移图等等 2、核密度分析、网络od分析、地形分析、空间分析等等 3、地理加权回归、地理探测器、生态环境质量指数、地理加权回归模型影响因素分析、计算…

硬件开发笔记(二十):AD21导入外部下载的元器件原理图库、封装库和3D模型

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/139707771 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…

webpack【实用教程】

基础配置 配置的拆分和合并 通常 webpack 的配置文件会有3个 webpack.common.js 公共配置&#xff08;会被另外两个配置文件导入并合并&#xff09;webpack.dev.js 开发环境的配置webpack.prod.js 生产环境的配置 开发环境的本地服务 在 webpack.dev.js 中配置 devServer:…

使用鸿蒙HarmonyOs NEXT 开发b站的卡片效果 手把手教学

资源准备&#xff1a; 需要4张图片&#xff1a;分别是页面图&#xff0c;播放图标&#xff0c;评论图标&#xff0c;更多图标 1.实现效果显示&#xff1a; 2.教学视频&#xff1a; 使用鸿蒙HarmonyOs NEXT 开发b站卡片_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1…

Huffman树——AcWing 148. 合并果子

目录 Huffman树 定义 运用情况 注意事项 解题思路 AcWing 148. 合并果子 题目描述 运行代码 代码思路 其它代码 代码思路 Huffman树 定义 它是一种最优二叉树。通过构建带权路径长度最小的二叉树&#xff0c;经常用于数据压缩等领域。 运用情况 在数据压缩中&a…

【机器学习】半监督学习可以实现什么功能?

目录 一、什么是机器学习二、半监督学习算法介绍三、半监督学习算法的应用场景四、半监督学习可以实现什么功能&#xff1f; 一、什么是机器学习 机器学习是一种人工智能技术&#xff0c;它使计算机系统能够从数据中学习并做出预测或决策&#xff0c;而无需明确编程。它涉及到…

Java研学-RBAC权限控制(八)

九 登录登出 1 登录作用 判断员工是否有权限访问&#xff0c;首先得知道现在操作的人是谁&#xff0c;所以必须先实现登录功能 2 登录流程 ① 提供登录页面&#xff0c;可输入用户名与密码信息&#xff0c;并添加执行登录的按钮。&#xff08;登录页面不能被拦截&#xff09;…

意识清晰,对答如流,但手脚活动受限 是脊髓损伤?

在医学领域&#xff0c;有一种情况常常令人困惑&#xff1a;患者意识清醒&#xff0c;交流无碍&#xff0c;但手脚的活动却受到限制。这种情况可能源于多种原因&#xff0c;其中之一就是脊髓损伤。本文将对脊髓损伤进行科普&#xff0c;帮助大家更好地理解这一复杂的医学问题。…

Handler、Looper与Message Queue:深入解析Android中的异步消息处理机制

在Android开发中&#xff0c;Handler、Looper和Message Queue是处理异步消息和线程间通信的核心组件。它们共同构成了一个高效且灵活的消息处理机制&#xff0c;使得开发者能够轻松地在不同线程之间传递数据和执行任务。以下将从技术难点、面试官关注点、回答吸引力和代码举例四…