算法刷题:深入分析一下我和大佬的差距

文章目录

加一:

给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。

最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头。

示例 1:

输入:digits = [1,2,3]
输出:[1,2,4]
解释:输入数组表示数字 123。

其实我知道从头往后遍历的方式,不过那时候还是选择了两次翻转,因为好写。

那我们来看一下大佬们的暴力写法吧,确实是在复杂度上会好很多的。

vector<int> plusOne(vector<int> &digits)
{
    int carry = 1;  //进位
    int n = digits.size();
    for (int i = n - 1; i >= 0; i--)
    {
        digits[i] += carry;
        carry=0;       
        if (digits[i] == 10)
        {
            digits[i] %= 10;//进位后当前位置零
            carry = 1;
            if (i == 0 && carry == 1)   //99...9的情况
                digits.insert(digits.begin(), 1);
        }
        else        //无进位则直接返回
            return digits;
    }
    return digits;
}

对称二叉树

前面也有些题目和我的解法不一样,但是我寻思着我们复杂度都一样,就不放了。

给定一个二叉树,检查它是否是镜像对称的。

例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

	1
   / \
  2   2
 / \ / \
3  4 4  3

但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:

    1
   / \
  2   2
   \   \
   3    3

进阶:

你可以运用递归和迭代两种方法解决这个问题吗?

作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/top-interview-questions-easy/xn7ihv/
来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

我们可以实现这样一个递归函数,通过「同步移动」两个指针的方法来遍历这棵树,p 指针和 q 指针一开始都指向这棵树的根,随后 p 右移时,q 左移,p 左移时,q 右移。每次检查当前 p 和 q 节点的值是否相等,如果相等再判断左右子树是否对称。

bool check(TreeNode *p, TreeNode *q) {
        if (!p && !q) return true;
        if (!p || !q) return false;
        return p->val == q->val && check(p->left, q->right) && check(p->right, q->left);
    }

    bool isSymmetric(TreeNode* root) {
        return check(root, root);
    }


作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/symmetric-tree/solution/dui-cheng-er-cha-shu-by-leetcode-solution/
来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

假设树上一共 n 个节点。

时间复杂度:这里遍历了这棵树,渐进时间复杂度为 O(n) 。
空间复杂度:这里的空间复杂度和递归使用的栈空间有关,这里递归层数不超过 n,故渐进空间复杂度为 O(n) 。

合并两个有序数组

给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。

初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。你可以假设 nums1 的空间大小等于 m + n,这样它就有足够的空间保存来自 nums2 的元素。

示例 1:

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]

示例 2:

输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]

提示:

nums1.length == m + n
nums2.length == n
0 <= m, n <= 200
1 <= m + n <= 200
-10^9 <= nums1[i], nums2[i] <= 10^9

方法二中,之所以要使用临时变量,是因为如果直接合并到数组 nums1中,nums1​ 中的元素可能会在取出之前被覆盖。那么如何直接避免覆盖 nums1 中的元素呢?观察可知,nums1 的后半部分是空的,可以直接覆盖而不会影响结果。因此可以指针设置为从后向前遍历,每次取两者之中的较大者放进 nums1\textit{nums}_1nums1​ 的最后面。

严格来说,在此遍历过程中的任意一个时刻,nums1​ 数组中有 m−p个元素被放入 nums1的后半部,nums2​ 数组中有 n−p2−1 个元素被放入 nums1​ 的后半部,而在指针 p1的后面,nums1​ 数组有 m+n−p1−1 个位置。由于

m+n−p1−1≥m−p1−1+n−p2−1

等价于

p2≥−1

永远成立,因此 p1 后面的位置永远足够容纳被插入的元素,不会产生 p1的元素被覆盖的情况。

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/merge-sorted-array/solution/he-bing-liang-ge-you-xu-shu-zu-by-leetco-rrb0/
来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        int p1 = m - 1, p2 = n - 1;
        int tail = m + n - 1;
        int cur;
        while (p1 >= 0 || p2 >= 0) {
            if (p1 == -1) {
                cur = nums2[p2--];
            } else if (p2 == -1) {
                cur = nums1[p1--];
            } else if (nums1[p1] > nums2[p2]) {
                cur = nums1[p1--];
            } else {
                cur = nums2[p2--];
            }
            nums1[tail--] = cur;
        }
    }

时间复杂度:O(m+n)。
指针移动单调递减,最多移动 m+n 次,因此时间复杂度为 O(m+n)。

空间复杂度:O(1)。
直接对数组 nums1 原地修改,不需要额外空间。


上一篇:瓶子君前端算法集训营第一期开营啦,免费哟


下一篇:leetcode算法第四题