LeetCode Hot100 回顾(三)

链表

141.环形链表

快慢指针一直走, 如果走到某一时刻快慢指针指向内容相等了, 说明有环。

142.环形链表II

找到环并找到入环的第一个节点, 朴素做法可以用一个set来记录走过的节点, 这样在验证环的时候也可以找到第一个入环的节点; 高级做法还是利用快慢指针来做, 假设链表到环入口的节点个数为 x x x个, 环上节点有 y y y个:
快指针走过的节点数为 f f f, 慢指针走过的节点数为 s s s, 则有
f = 2 s f = 2s f=2s
由于快指针比慢指针多走了 n n n圈, 则有
f = s + n y f = s + ny f=s+ny
两式相减得
f = 2 n y f = 2ny f=2ny

s = n y s = ny s=ny

所以现在两个指针走的节点总数为环上节点数目的整数倍, 由于从链表的头到环入口节点的位置可以走 x + n y x+ny x+ny得到, 所以当前的慢指针只需要再走 x x x个节点就可以到达入口节点, 此时再有一个指针从链表的头开始走, 每次走一个节点, 慢指针也一次走一个节点, 当这两个指针相遇的时候, 指针指向的节点就是链表的入口节点了。

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        if (!head) return nullptr;
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast && fast->next) {
            fast = fast->next->next;
            slow = slow->next;
            if (fast == slow) break;
        }
        if (!fast || !fast->next) return nullptr;
        fast = head;
        while(fast != slow) {
            fast = fast->next;
            slow = slow->next;
        }
        return fast;
    }
};

21.合并两个有序链表

参考归并排序的合并部分。

2.两数相加

最大的数可以有100位的9, unsigned long long也存不下, 这道题需要用链表模拟加法, 题目还非常贴心,给的是逆序的数字, 只需要用两个指针分别指向两个链表, 然后依次对每一位相加, 使用一个标志位来标识进位信息即可; 需要特殊考虑两个数字位数不同的情况。

19.删除链表的倒数第N个结点

去年面试被问了好几次这个问题, 也是用双指针来解, 一个指针指向第一个节点, 另一个指针指向第N个节点, 然后两个指针同时向后移动, 靠后的节点指向最后一个节点的时候, 前一个节点正好指向倒数第N个节点。题目要求删除, 所以在移动靠前的节点时存一下前驱节点即可。

24.两两交换链表中的节点

这个题和前面的翻转链表一样, 也可以通过递归来解决

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        if (!head || !head->next) return head;
        ListNode* nHead = swapPairs(head->next->next);  // (1)
        ListNode* t = head->next;
        head->next->next = head;
        head->next = nHead;
        return t;
    }
};

每次递归只考虑当前这两个节点, 假设在(1)之后这两个节点之后的链表完成了两两翻转, 我们只需要调换当前这两个节点, 然后正确连接后面的链表, 返回正确的头节点即可。

25.K个一组翻转链表

递归做法和前一个题目的思路一样, 只不过翻转K个节点这个操作比较繁琐, 也可以抽取出来成为一个函数, 这个函数可以在前面翻转链表的代码上稍微改动一下就可以用了, 其实这个题就是24. 和 206. 的结合。

138. 随机链表的复制

先不考虑指针, 新建出链表, 然后把两个链表的对应节点做一个映射关系,然后再遍历旧链表把对应的新链表的节点的指针赋值即可

148. 排序链表

5 e 4 5e^4 5e4的数据范围, 至少需要 O ( n n ) O(n\sqrt{n}) O(nn )的算法, 推荐使用归并排序, 稳定的 O ( n log ⁡ n ) O(n\log{n}) O(nlogn)的时间复杂度, 同时在链表上还没有额外的空间消耗, 完美的算法

class Solution {
public:
    ListNode* sortList(ListNode* head) {  // 对链表归并排序
        if (!head || !head->next) return head;
        ListNode* slow = head, *fast = head;
        ListNode* pre = slow;
        while(fast && fast->next) {
            pre = slow;
            slow = slow->next;
            fast = fast->next->next;
        }
        pre->next = nullptr;
        head = sortList(head);
        slow = sortList(slow);
        return mergeList(head, slow);
    }
    ListNode* mergeList(ListNode* l1, ListNode* l2) {
        if (l1 == nullptr) return l2;
        if (l2 == nullptr) return l1;
        ListNode* newList;
        if (l1->val > l2->val) {
            newList = l2;
            l2 = l2->next;
        } else {
            newList = l1;
            l1 = l1->next;
        }
        ListNode* p = newList;
        while(l2 && l1) {
            if(l1->val > l2->val) {
                p->next = l2;
                l2 = l2->next;
            } else {
                p->next = l1;
                l1 = l1->next;
            }
            p = p->next;
        }
        while(l1) {
            p->next = l1;
            l1 = l1->next;
            p = p->next;
        }
        while(l2) {
            p->next = l2;
            l2 = l2->next;
            p = p->next;
        }
        p->next = nullptr;
        
        return newList;
    }
};

23.合并K个升序链表

把链表数组放到优先级队列里面, 自定义优先级队列的排序方式, 然后从优先级队列里面取节点即可。

146. LRU缓存

把数据放到链表里面, 每次新使用过的节点把它抽出来放到第一个, 再使用一个unordered_map来加速对链表中节点的访问, 当缓存数据个数超过容量时, 删除链表的最后一个节点。

二叉树

94.二叉树的中序遍历

朴实无华的中序遍历, 没有弯弯绕和坑

104.二叉树的最大深度

当前树的最大深度为 max ⁡ ( max ⁡ ( 左子树的最大深度 ) + 1 , max ⁡ ( 右子树的最大深度 ) + 1 ) \max({\max({左子树的最大深度})+1, \max({右子树的最大深度})+1}) max(max(左子树的最大深度)+1,max(右子树的最大深度)+1)
空节点的深度为0, 简单明了的递归关系

226.翻转二叉树

遍历二叉树, 将二叉树的每个节点的左右孩子交换一下就行了

101.对称二叉树

用两个指针分别遍历这颗树的左右子树, 遍历过程对称, 同时在遍历过程中查看节点的结构和数值是否相同, 不相同返回false

class Solution {
    bool dfs(TreeNode* p1, TreeNode* p2) {
        if (!p1 && !p2) return true;
        if ((p1 && !p2) || (p2 && !p1)) return false;
        if (p1->val != p2->val) return false;
        bool flag1 = dfs(p1->left, p2->right);
        bool flag2 = dfs(p1->right, p2->left);
        return flag1 && flag2;
    }
public:
    bool isSymmetric(TreeNode* root) {
        // 深度优先搜索
        return dfs(root->left, root->right);
    }
};

543.二叉树的直径

先遍历一遍二叉树, 求出每个节点的高度, 利用节点的高度可以快速算出该节点到叶子节点的距离; 然后再遍历一遍二叉树, 即可找到答案

class Solution {
    unordered_map<TreeNode*, int> mp;
    int maxHeight(TreeNode* root) {
        if (!root) return 0;
        if (mp.find(root) != mp.end()) return mp[root];  // 记忆化
        return mp[root] = max(maxHeight(root->left), maxHeight(root->right)) + 1;
    }
    int dfs(TreeNode* root) {
        if (!root) return 0;
        int leftHeight = maxHeight(root->left);
        int rightHeight = maxHeight(root->right);
        if (leftHeight) --leftHeight;
        if (rightHeight) --rightHeight;
        if (root->left) ++leftHeight;
        if (root->right) ++rightHeight;

        int leftres = dfs(root->left);
        int rightres = dfs(root->right);
        return max(leftres, max(rightres, leftHeight+rightHeight));
    }
public:
    int diameterOfBinaryTree(TreeNode* root) {  // 任意两点之间的最长路径的长度
        mp.clear();
        // 求出每个节点到跟节点的距离
        return dfs(root);
    }
};

102.二叉树的层序遍历

这个不是简单的遍历完二叉树即可, 还需要区分每一层, 在原本利用队列进行层序遍历的代码基础上修改一下, 每次while循环处理一整层的节点; 其实也可以使用先序遍历得到想要的答案

class Solution {
    vector<vector<int>> res;
    void preOrder(TreeNode* root, int depth) {
        if(!root) return;
        if (res.size() <= depth) {
            res.push_back({});
        }
        preOrder(root->left, depth+1);
        preOrder(root->right, depth+1);
        res[depth].emplace_back(root->val);
    }
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        res.clear();
        preOrder(root, 0);
        return res;
    }
};

108.将有序数组转换为二叉搜索树

有序数组转换为高度平衡的二叉搜索树, 尽可能保证每一颗子树左右的节点数目相等即可, 构建出的树即可满足条件

class Solution {
    TreeNode* buildTree(vector<int>& nums, int left, int right) {
        if (left > right) return nullptr;
        int mid = (left + right) >> 1;
        TreeNode* root = new TreeNode(nums[mid]);
        root->right = buildTree(nums, mid+1, right);
        root->left = buildTree(nums, left, mid-1);
        return root;
    }
public:
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        // 每次选根选择最中间的
        TreeNode* res = buildTree(nums, 0, nums.size()-1);
        return res;
    }
};

98.验证二叉搜索树

向下递归的同时限制当前子树的数值的上下界, 不在规定的范围内返回false

class Solution {
    bool isValid(TreeNode* root, long lower, long upper) {
        if (!root) return true;
        if (root->val <= lower || root->val >= upper) return false;
        return isValid(root->left, lower, root->val) && isValid(root->right, root->val, upper);
    }
public:
    bool isValidBST(TreeNode* root) {  // 验证是否为二叉搜索树
        return isValid(root, LONG_MIN, LONG_MAX);  // 
    }
};

230.二叉搜索树中第K小的元素

先序遍历, 如果某个子树的左子树正好有k-1个元素, 那么当前根就为第k小的元素; 如果当前根的左子树无法正好凑成k-1, 那么就需要在右子树中循环合适的元素

class Solution {
    unordered_map<TreeNode*, int> mp;  // 记录某棵树有多少个节点
    int getCnt(TreeNode* root) {
        if (mp.find(root) != mp.end()) return mp[root];
        if (!root) return mp[root] = 0;
        return mp[root] = getCnt(root->left) + getCnt(root->right) + 1;
    }
public:
    int kthSmallest(TreeNode* root, int k) {
        // 某个节点左子树正好有k-1个数字, 根节点就是
        int cnt = getCnt(root->left);
        if (cnt == k-1) return root->val;
        else if (cnt > k-1) return kthSmallest(root->left, k);
        else return kthSmallest(root->right, k-cnt-1);
    }
};

199.二叉树的右视图

按照 根 -> 右 -> 左的顺序遍历二叉树, 在每个深度遇到的第一个节点即为该深度最右侧的节点, 遍历完二叉树即可得到答案。

114. 二叉树展开为链表

在新建的链表上添加一个尾指针, 遍历二叉树, 向尾指针上添加节点即可。

105.从前序与中序遍历序列构造二叉树

先序遍历是 根->左->右, 中序遍历是左->根->右

通过先序遍历序列即可将中序遍历序列划分, 通过中序遍历的左右节点数又可以将先序遍历序列划分, 递归构建二叉树即可。

437.路径总和III

两个DFS, 一个用来遍历二叉树上所有节点, 一个用来找以当前节点开始的最大的路径, 遍历过程中更新答案即可。

236.二叉树的最近公共祖先

两种方法, 第一种是从下往上找, 可以参考我之前的博客, 第二种可以自顶向下, 如果在某一棵树上, 给定的两个节点分别位于这颗树的左子树和右子树, 那么这颗树的跟节点就是给定两个节点的最近公共祖先。

你可能感兴趣的:(leetcode,算法,职场和发展)