首页 > 其他分享 >力扣刷题笔记

力扣刷题笔记

时间:2024-06-23 16:02:47浏览次数:3  
标签:right return nums int 笔记 力扣 vector 刷题 left

记录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); // 根节点的深度为 0
        return 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 左边的节点指向 node
            pre[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]为末尾的最大子数组和,判断当前数是否大于0
        if (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; // 题中字符串长度<=100
            for (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 -> string
                result.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; // 节点数量 <= 20000
    vector<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 到 1000
    vector<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->2
            node2->next = nede1; // 2->1
            node1->next = node3; // 1->3
            
            node0 = node1; // 下一轮交换,0是1
            node1 = 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->1
        
        return 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),没有用到额外空间。

标签:right,return,nums,int,笔记,力扣,vector,刷题,left
From: https://blog.csdn.net/qq_43818724/article/details/139901157

相关文章

  • unity麦扣x唐老狮3DRPG笔记分享,ProBuilder插件基本介绍(持续更新)
    声明:本文仅用于个人笔记及学习交流,禁止用作任何商业用途唐老师没有讲过这些插件,所以现在还没轮结合到唐老狮的课程的阶段在具体写代码以及介绍unity本体功能的时候唐老师的课程知识点会融入进来另外该插件功能过多,而用的较少所以很多功能就只做介绍,知道大概即可  首......
  • 力扣-122. 买卖股票的最佳时机 II
    1.题目题目地址(122.买卖股票的最佳时机II-力扣(LeetCode))https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/题目描述给你一个整数数组prices,其中 prices[i]表示某支股票第i天的价格。在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最......
  • Hadoop+Hive超全笔记 一站式搞定!!
    Hadoophadoop集群的组成hadoop常用端口HDFS常用shell命令HDFS的原理、机制块和副本edits和fsimage文件HDFS的三大机制HDFS数据上传、写入原理(写流程)【重点】HDFS数据读取(读流程)【重点】原数据存储流程【重点】安全模式归档机制(小文件)垃圾桶机制MapReduce底层原......
  • Python进阶学习笔记-基础篇
    打印原始字符串print(r"D:\three\two\one\now")D:\three\two\one\now复现随机数importrandomx=random.getstate()print(random.randint(1,10))print(random.randint(1,10))print(random.randint(1,10))random.setstate(x)print(random.randint(1,10))pr......
  • Python进阶学习笔记-函数篇
    函数的特殊参数#/前的参数只能是位置参数,*后面的只能是关键字参数,之间的不限参数类型deffunc(a,b,/,c,*,d,e):print(a,b,c,d,e)func(1,2,3,d=4,e=5)func(1,2,c=3,d=4,e=5)#a,b不能以关键字形式传参,d,e只能以关键字参数传参#可变参数*argsdef......
  • Python进阶学习笔记-面向对象篇
    组合classEngine:"""引擎类,提供基本的引擎功能"""def__init__(self,power):self.power=powerdefstart(self):print(f"引擎启动,功率:{self.power}")classCar:"""汽车类,使用引擎类的功能"&......
  • 硬件开发笔记(二十):AD21导入外部下载的元器件原理图库、封装库和3D模型
    若该文为原创文章,转载请注明原文出处本文章博客地址:https://hpzwl.blog.csdn.net/article/details/139707771长沙红胖子Qt(长沙创微智科)博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中…硬件相关开发......
  • 硬件开发笔记(二十一):外部搜索不到的元器件封装可尝试使用AD21软件的“ManufacturerPart
    若该文为原创文章,转载请注明原文出处本文章博客地址:https://hpzwl.blog.csdn.net/article/details/139869584长沙红胖子Qt(长沙创微智科)博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中…硬件相关开发......
  • AcWing算法基础课笔记——求组合数3
    求组合数Ⅲ20万组数据,1≤b≤a≤1......
  • FFmpeg开发笔记(三十二)利用RTMP协议构建电脑与手机的直播Demo
    不管是传统互联网还是移动互联网,实时数据传输都是刚需,比如以QQ、微信为代表的即时通信工具,能够实时传输文本和图片。其中一对一的图文通信叫做私聊,多对多的图文通信叫做群聊。除了常见的图文即时通信,还有实时音视频通信,比如一对一的音频通话、一对一的视频通话等等,此时可采用WebR......