当前位置: 首页 > news >正文

力扣刷题笔记

记录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),没有用到额外空间。

相关文章:

  • Redis连接池配置:深入探索JedisPoolConfig
  • create-react-app创建的项目中设置webpack配置
  • 【科技前沿】电子设计新贵SmartEDA:为何它引领行业风潮?
  • 物联网 IoT 收录
  • 等保测评练习10
  • 程序员系统入门大模型的路径和资源,看这篇就够了
  • 道路救援小程序源码
  • 把sql拿到数据库中执行,和程序返回的值不一样??????
  • 深度学习工具jupyter的new没有环境选项以及遇到的EnvironmentLocationNotFound:Not such a environment
  • Nginx实现动静分离
  • 赋能AI未来,景联文科技推出高质量亿级教育题库、多轮对话以及心理大模型数据
  • 信息检索(53):Document Expansion by Query Prediction
  • Spring框架学习笔记(本地印象笔记搬运)(整理中)
  • TensorRT-LLM加速框架的基本使用
  • 数据库原理与安全复习笔记(未完待续)
  • ECMAScript6(0):ES6简明参考手册
  • Fastjson的基本使用方法大全
  • Java 网络编程(2):UDP 的使用
  • Node 版本管理
  • PHP的Ev教程三(Periodic watcher)
  • Python十分钟制作属于你自己的个性logo
  • vue学习系列(二)vue-cli
  • 从0到1:PostCSS 插件开发最佳实践
  • 搭建gitbook 和 访问权限认证
  • 多线程事务回滚
  • 京东美团研发面经
  • 中文输入法与React文本输入框的问题与解决方案
  • ​Linux Ubuntu环境下使用docker构建spark运行环境(超级详细)
  • (Demo分享)利用原生JavaScript-随机数-实现做一个烟花案例
  • (done) 两个矩阵 “相似” 是什么意思?
  • (pt可视化)利用torch的make_grid进行张量可视化
  • (vue)el-checkbox 实现展示区分 label 和 value(展示值与选中获取值需不同)
  • (八)c52学习之旅-中断实验
  • (附源码)计算机毕业设计ssm本地美食推荐平台
  • (论文阅读笔记)Network planning with deep reinforcement learning
  • (免费领源码)python#django#mysql校园校园宿舍管理系统84831-计算机毕业设计项目选题推荐
  • (七)Knockout 创建自定义绑定
  • ..回顾17,展望18
  • .bat批处理(七):PC端从手机内复制文件到本地
  • .NET 5种线程安全集合
  • .net Application的目录
  • .Net 路由处理厉害了
  • .net连接oracle数据库
  • ??如何把JavaScript脚本中的参数传到java代码段中
  • [ IO.File ] FileSystemWatcher
  • [AI]文心一言出圈的同时,NLP处理下的ChatGPT-4.5最新资讯
  • [Angular] 笔记 16:模板驱动表单 - 选择框与选项
  • [Angular] 笔记 8:list/detail 页面以及@Input
  • [autojs]逍遥模拟器和vscode对接
  • [C/C++]数据结构 循环队列
  • [CareerCup][Google Interview] 实现一个具有get_min的Queue
  • [CISCN 2019华东南]Web11
  • [G-CS-MR.PS02] 機巧之形2: Ruler Circle
  • [GN] 设计模式——面向对象设计原则概述
  • [hdu 2826] The troubles of lmy [简单计算几何 - 相似]