第十一章 其它题目

本文介绍了使用DFS和BFS解决钥匙和房间、所有可能路径及单词接龙问题,以及并查集在冗余连接问题中的应用。还涵盖了模拟问题,如机器人返回原点、下一个排列和岛屿周长的计算。此外,讨论了位运算、哈希表在字符串问题中的应用,以及二叉树和链表操作,包括求根节点到叶节点数字之和、将二叉搜索树变平衡、回文链表和重排链表的算法。最后,文章提到了数组操作,如找出小于当前数字的数字、有效的山脉数组等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、DFS 与 BFS

1.1 钥匙和房间

Leetcode 841

class Solution {
public:
    bool canVisitAllRooms(vector<vector<int>>& rooms) {
        vector<bool> visited(rooms.size(), false);
        dfs(rooms, 0, visited);
        for (int i : visited)
            if (!i) return false;
        return true;
    }

    void dfs(const vector<vector<int>>& rooms, int key, vector<bool>& visited) {
        if (visited[key]) return;
        visited[key] = true;
        vector<int> keys = rooms[key];
        for (int key : keys) 
            dfs(rooms, key, visited);
    }
};

1.2 所有可能的路径

Leetcode 797

class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;

    vector<vector<int>> allPathsSourceTarget(vector<vector<int>>& graph) {
        path.push_back(0);
        dfs(graph, 0);
        return res;
    }

    void dfs(vector<vector<int>>& graph, int x) { // x表示当前遍历的节点
        if (x == graph.size() - 1) { // 最后遍历的节点是n-1
            res.push_back(path);
            return;
        }
        for (int i = 0; i < graph[x].size(); i ++ ) {
            path.push_back(graph[x][i]);
            dfs(graph, graph[x][i]);
            path.pop_back();
        }
    }
};

1.3 单词接龙

Leetcode 127

class Solution {
public:
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        unordered_set<string> wordSet(wordList.begin(), wordList.end());
        if (wordSet.find(endWord) == wordSet.end()) return 0;
        unordered_map<string, int> visited; // word,查询到该word的路经长度
        visited.insert({beginWord, 1});
        queue<string> que; que.push(beginWord);
        while (!que.empty()) {
            string word = que.front(); que.pop();
            int path = visited[word];
            for (int i = 0; i < word.size(); i ++ ) {
                string newWord = word;
                for (int j = 0; j < 26; j ++ ) {
                    newWord[i] = j + 'a';
                    if (newWord == endWord) return path + 1;
                    if (wordSet.find(newWord) != wordSet.end() && visited.find(newWord) == visited.end()) {
                        visited.insert({newWord, path + 1});
                        que.push(newWord);
                    }
                }
            }
        }
        return 0;
    }
};

二、并查集

2.1 冗余连接

Leetcode 684

class Solution {
public:
    int n = 1005;
    int father[1005];

    int find(int x) {
        return x == father[x] ? x : father[x] = find(father[x]);
    }

    vector<int> findRedundantConnection(vector<vector<int>>& edges) {
        for (int i = 0; i < n; i ++ ) father[i] = i;
        for (int i = 0; i < edges.size(); i ++ ) {
            int pa = find(edges[i][0]), pb = find(edges[i][1]);
            if (pa == pb) return edges[i];
            else father[pb] = pa;
        }
        return {};
    }
};

2.2 冗余连接 Ⅱ

Leetcode 685

这个题目是有向图,复杂了一些,参考题解

class Solution {
public:
    static const int N = 1010;
    int father[N];
    int n; // 边的数目

    int find(int u) {
        return u == father[u] ? u : father[u] = find(father[u]);
    }

    // 在有向图中找到删除的那条边,使其变成树
    vector<int> getRemoveEdge(const vector<vector<int>>& edges) {
        for (int i = 0; i <= n; i ++ ) father[i] = i;
        for (int i = 0; i < n; i ++ ) {
            int pa = find(edges[i][0]), pb = find(edges[i][1]);
            if (pa == pb) return edges[i];
            father[pb] = pa;
        }
        return {};
    }

    // 删除一条边之后是不是树
    bool isTreeAfterRemoveEdge(const vector<vector<int>>& edges, int deleteEdge) {
        for (int i = 0; i <= n; i ++ ) father[i] = i;
        for (int i = 0; i < n; i ++ ) {
            if (i == deleteEdge) continue;
            int pa = find(edges[i][0]), pb = find(edges[i][1]);
            if (pa == pb) return false;
            father[pb] = pa;
        }
        return true;
    }

    vector<int> findRedundantDirectedConnection(vector<vector<int>>& edges) {
        int inDegree[N] = {0}; // 节点入度
        n = edges.size();
        for (int i = 0; i < n; i ++ )
            inDegree[edges[i][1]] ++ ;
        vector<int> vec; // 记录入度为2的边
        // 找入度为2的节点所对应的边,注意要倒序,因为优先返回最后出现在二维数组中的答案
        for (int i = n - 1; i >= 0; i -- )
            if (inDegree[edges[i][1]] == 2) vec.push_back(i);
        // 处理图中情况1 和 情况2
        // 如果有入度为2的节点,那么一定是两条边里删一个,看删哪个可以构成树
        if (vec.size() > 0) {
            if (isTreeAfterRemoveEdge(edges, vec[0])) return edges[vec[0]];
            else return edges[vec[1]];
        }
        // 处理图中情况3
        // 明确没有入度为2的情况,那么一定有有向环,找到构成环的边返回就可以了
        return getRemoveEdge(edges);
    }
};

三、模拟

3.1 机器人能否返回原点

Leetcode 657

class Solution {
public:
    bool judgeCircle(string moves) {
        int x = 0, y = 0;
        for (int i = 0; i < moves.size(); i ++ ) {
            if (moves[i] == 'U') y ++ ;
            if (moves[i] == 'D') y -- ;
            if (moves[i] == 'L') x -- ;
            if (moves[i] == 'R') x ++ ;
        }
        return !x && !y;
    }
};

3.2 下一个排列

Leetcode 31

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        for (int i = nums.size() - 1; i >= 0; i -- ) 
            for (int j = nums.size() - 1; j > i; j -- )
                if (nums[j] > nums[i]) {
                    swap(nums[i], nums[j]);
                    reverse(nums.begin() + i + 1, nums.end());
                    return;
                }
        // 到这里了说明整个数组都是倒序了,反转一下便可
        reverse(nums.begin(), nums.end());
    }
};

3.3 岛屿的周长

Leetcode 463

方法一:遍历每一个空格,遇到岛屿,计算其上下左右的情况,遇到水域或者出界的情况,就可以计算边了。

class Solution {
public:
    int islandPerimeter(vector<vector<int>>& grid) {
        int direction[4][2] = {0, 1, 1, 0, -1, 0, 0, -1};
        int res = 0;
        for (int i = 0; i < grid.size(); i ++ ) 
            for (int j = 0; j < grid[0].size(); j ++ ) 
                if (grid[i][j])
                    for (int k = 0; k < 4; k ++ ) {
                        int x = i + direction[k][0];
                        int y = j + direction[k][1];
                        if (x < 0 || x >= grid.size() || y < 0 || y >= grid[0].size() || !grid[x][y])
                            res ++ ;
                    }
        return res;
    }
};

方法二:计算出总的岛屿数量,因为有一对相邻两个陆地,边的总数就减 2,那么在计算出相邻岛屿的数量就可以了。

代码中未统计下边和右边的相邻矩阵是为了防止重复计算。

class Solution {
public:
    int islandPerimeter(vector<vector<int>>& grid) {
        int sum = 0, cover = 0; // 总数量,相邻数量
        for (int i = 0; i < grid.size(); i ++ ) 
            for (int j = 0; j < grid[0].size(); j ++ )
                if (grid[i][j]) {
                    sum ++ ;
                    if (i - 1 >= 0 && grid[i - 1][j]) cover ++ ; // 统计上边
                    if (j - 1 >= 0 && grid[i][j - 1]) cover ++ ; // 统计左边
                }
        return sum * 4 - cover * 2;
    }
};

四、位运算

Leetcode 1356

计算二进制中 1 的个数:

// 方法一:
int bitCount(int n) {
    int count = 0; // 计数器
    while (n > 0) {
        if((n & 1) == 1)  count++;  // 当前位是1,count++
        n >>= 1 ; // n向右移位
    }
    return count;
}

// 方法二
int bitCount(int n) {
    int count = 0;
    while (n) {
        n &= (n - 1); // 清除最低位的1
        count++;
    }
    return count;
}
class Solution {
public:
    vector<int> sortByBits(vector<int>& arr) {
        sort(arr.begin(), arr.end(), cmp);
        return arr;
    }

    static int bitCount(int n) {
        int count = 0;
        while (n) 
            n &= (n - 1), count ++ ;
        return count;
    }

    static bool cmp(int a, int b) {
        int countA = bitCount(a), countB = bitCount(b);
        if (countA == countB) return a < b;
        return countA < countB;
    }
};

五、哈希表

5.1 同构字符串

Leetcode 205

class Solution {
public:
    bool isIsomorphic(string s, string t) {
        unordered_map<char, char> mp1, mp2;
        for (int i = 0, j = 0; i < s.size(); i ++ , j ++ ) {
            if (mp1.find(s[i]) == mp1.end()) mp1[s[i]] = t[j];
            if (mp2.find(t[j]) == mp2.end()) mp2[t[j]] = s[i];
            if (mp1[s[i]] != t[j] || mp2[t[j]] != s[i]) return false;
        }
        return true;
    }
};

5.2 查找共用字符

Leetcode 1002

class Solution {
public:
    vector<string> commonChars(vector<string>& words) {
        vector<string> res;
        if (!words.size()) return res;
        int hash[30] = {0}; // 统计所有字符串里字符出现的最小频率
        for (char c: words[0]) hash[c - 'a'] ++ ; // 用第一个字符初始化
        int hashOtherStr[30]; // 统计除第一个字符串以外所有字符出现的频率
        for (int i = 1; i < words.size(); i ++ ) {
            memset(hashOtherStr, 0, 30 * sizeof(int));
            for (char c: words[i]) hashOtherStr[c - 'a'] ++ ;
            for (int j = 0; j < 26; j ++ )
                hash[j] = min(hash[j], hashOtherStr[j]);
        }
        for (int i = 0; i < 26; i ++ ) 
            while (hash[i]) { // 注意这里是while,多个重复的字符
                string s(1, i + 'a');
                res.push_back(s);
                hash[i] -- ;
            }
        return res;
    }
};

5.3 长按键入

Leetcode 925

class Solution {
public:
    bool isLongPressedName(string name, string typed) {
        int i = 0, j = 0;
        while (i < name.size() && j < typed.size()) 
            if (name[i] == typed[j]) i ++ , j ++ ;
            else {
                if (!j) return false; // 如果是第一个位置不匹配
                while (j < typed.size() && typed[j] == typed[j - 1]) j ++ ; // 跳过重复项
                if (name[i] == typed[j]) i ++ , j ++ ;
                else return false;
            }
        if (i < name.size()) return false; // name没有匹配完
        while (j < typed.size()) // type没有匹配完
            if (typed[j] == typed[j - 1]) j ++ ;
            else return false;
        return true;
    }
};

六、二叉树

6.1 求根节点到叶节点数字之和

Leetcode 129

版本一:

class Solution {
public:
    int dfs(TreeNode *root, int preSum) {
        if (!root) return 0;
        int sum = preSum * 10 + root->val;
        if (!root->left && !root->right) return sum;
        return dfs(root->left, sum) + dfs(root->right, sum);
    }

    int sumNumbers(TreeNode* root) {
        return dfs(root, 0);
    }
};

版本二:

class Solution {
public:
    int res;
    vector<int> path;

    int sumNumbers(TreeNode* root) {
        path.clear();
        res = 0;
        if (!root) return res;
        path.push_back(root->val);
        traversal(root);
        return res;
    }

    void traversal(TreeNode* cur) {
        if (!cur->left && !cur->right) {
            res += vectorToInt(path);
            return;
        }
        if (cur->left) {
            path.push_back(cur->left->val);
            traversal(cur->left);
            path.pop_back();
        }
        if (cur->right) {
            path.push_back(cur->right->val);
            traversal(cur->right);
            path.pop_back();
        }
        return;
    }

    int vectorToInt(vector<int> &vec) {
        int sum = 0;
        for (int x: vec) sum = sum * 10 + x;
        return sum;
    }
};

6.2 将二叉搜索树变平衡

Leetcode 1382

class Solution {
public:
    vector<int> vec;

    TreeNode* balanceBST(TreeNode* root) {
        traversal(root);
        return getTree(vec, 0, vec.size() - 1);
    }

    // 有序数组转换为平衡二叉树
    TreeNode* getTree(vector<int>& nums, int left, int right) {
        if (left > right) return nullptr;
        int mid = left + ((right - left) >> 1);
        TreeNode* root = new TreeNode(nums[mid]);
        root->left = getTree(nums, left, mid - 1);
        root->right = getTree(nums, mid + 1, right);
        return root;
    }

    // 有序树转换为有序数组
    void traversal(TreeNode* cur) {
        if (!cur) return;
        traversal(cur->left);
        vec.push_back(cur->val);
        traversal(cur->right);
    }
};

七、链表

7.1 回文链表

Leetcode 234

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        ListNode* cur = head;
        int len = 0;
        while (cur) len ++ , cur = cur->next;
        vector<int> vec(len, 0); // 给定vector初始长度,避免vector每次添加节点重新开辟新空间
        cur = head;
        int index = 0;
        while (cur) vec[index ++ ] = cur->val, cur = cur->next;
        for (int i = 0, j = len - 1; i < j; i ++ , j -- )
            if (vec[i] != vec[j]) return false;
        return true; 
    }
};

7.2 重排链表

Leetcode 143

方法一:数组模拟

class Solution {
public:
    void reorderList(ListNode* head) {
        vector<ListNode*> vec;
        ListNode* cur = head;
        if (!cur) return;
        while (cur) vec.push_back(cur), cur = cur->next;
        cur = head;
        int i = 1, j = vec.size() - 1, count = 0; // count 用于计数
        while (i <= j) {
            if (count % 2 == 0)
                cur->next = vec[j -- ];
            else cur->next = vec[i ++ ];
            cur = cur->next, count ++ ;
        }
        cur->next = nullptr;
    }
};

方法二:双向队列模拟

class Solution {
public:
    void reorderList(ListNode* head) {
        deque<ListNode*> que;
        if (!head) return;
        ListNode* cur = head;
        while (cur->next) que.push_back(cur->next), cur = cur->next; // 头结点不入队
        cur = head;
        int count = 0; // 计数
        ListNode* node;
        while (!que.empty()) {
            if (count % 2 == 0) node = que.back(), que.pop_back();
            else node = que.front(), que.pop_front();
            count ++ ;
            cur->next = node;
            cur = cur->next;
        } 
        cur->next = nullptr;
    }
};

方法三:将链表分割成两个链表,然后把第二个链表反转,最后再拼接

class Solution {
public:
    void reorderList(ListNode* head) {
        if (!head) return;
        ListNode* fast = head, *slow = head;
        while (fast && fast->next && fast->next->next)
            fast = fast->next->next, slow = slow->next;
        ListNode *head1 = head, *head2 = slow->next;
        slow->next = nullptr;
        head2 = reverseList(head2);
        ListNode *cur1 = head1, *cur2 = head2, *cur = head;
        cur1 = cur1->next;
        int count = 0;
        while (cur1 && cur2) {
            if (count % 2 == 0) cur->next = cur2, cur2 = cur2->next;
            else cur->next = cur1, cur1 = cur1->next;
            count ++ ;
            cur = cur->next;
        }
        if (cur2) cur->next = cur2;
        if (cur1) cur->next = cur1;
    }

    ListNode* reverseList(ListNode* head) {
        ListNode *tmp, *cur = head, *pre = nullptr;
        while (cur) {
            tmp = cur->next;
            cur->next = pre;
            pre = cur, cur = tmp;
        }
        return pre;
    }
};

八、数组

8.1 有多少小于当前数字的数字

Leetcode 1365

排序 + 哈希 + 从后往前遍历(方便处理数值相同的情况)

class Solution {
public:
    vector<int> smallerNumbersThanCurrent(vector<int>& nums) {
        vector<int> vec = nums;
        sort(vec.begin(), vec.end());
        int hash[110];
        for (int i = vec.size() - 1; i >= 0; i -- )
            hash[vec[i]] = i;
        for (int i = 0; i < nums.size(); i ++ ) 
            vec[i] = hash[nums[i]];
        return vec;
    }
};

8.2 有效的山脉数组

Leetcode 941

双指针

class Solution {
public:
    bool validMountainArray(vector<int>& arr) {
        if (arr.size() < 3) return false;
        int l = 0, r = arr.size() - 1;
        while (l < arr.size() - 1 && arr[l] < arr[l + 1]) l ++ ;
        while (r > 0 && arr[r] < arr[r - 1]) r -- ;
        if (l == r && l && r != arr.size() - 1) return true; // 如果l和r都在起始位置,则不是山峰
        return false;
    }   
};

8.3 独一无二的出现次数

Leetcode 1207

class Solution {
public:
    bool uniqueOccurrences(vector<int>& arr) {
        int count[2010] = {0}; // 统计数字出现的频率
        for (int i = 0; i < arr.size(); i ++ )
            count[arr[i] + 1000] ++ ;
        bool fre[1010] = {false}; // 查看相同频率是否重复出现
        for (int i = 0; i <= 2000; i ++ )
            if (count[i]) {
                if (!fre[count[i]]) fre[count[i]] = true;
                else return false;
            }
        return true;
    }
};

8.4 旋转数组

Leetcode 189

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        k = k % nums.size(); // 处理 k > nums.size() 的情况
        reverse(nums.begin(), nums.end());
        reverse(nums.begin(), nums.begin() + k);
        reverse(nums.begin() + k, nums.end());
    }
};

8.5 寻找数组的中心下标

Leetcode 724

class Solution {
public:
    int pivotIndex(vector<int>& nums) {
        int sum = 0, lSum = 0, rSum = 0;
        for (int x : nums) sum += x;
        for (int i = 0; i < nums.size(); i ++ ) {
            lSum += nums[i], rSum = sum - lSum + nums[i];
            if (lSum == rSum) return i;
        }
        return -1;
    }
};

8.6 按奇偶排序数组 II

Leetcode 922

class Solution {
public:
    vector<int> sortArrayByParityII(vector<int>& nums) {
        int oddIndex = 1;
        for (int i = 0; i < nums.size(); i += 2)  
            if (nums[i] % 2) { // 在偶数位置上遇到的奇数
                while (nums[oddIndex] % 2) oddIndex += 2; // 在奇数位找一个偶数
                swap(nums[i], nums[oddIndex]);
            }
        return nums;
    }
};

.7

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值