Leet Code 刷题记录【2】

88. 合并两个有序数组

这道题的难点就在于如何避免反复挪动数字。
我很自然而然地就用空间换时间了,重新令了个空数组来存结果,在内存消耗上惨不忍睹。
实际上更巧妙的办法是倒着来。

一般我们会考虑从小到大去找、去存,实际上也可以从大到小去找、去存。这样就不会覆盖存储。
然后我写成了这个丑样子:

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        int j;
        if(n == 0)
            return ;
        if(m == 0)
        {
            for(j=0 ; j<n ; j++)
                nums1[j] = nums2[j];
            return ;
        }
        int cur1 = nums1[m-1],cur2;
        int i = m-1;
        for(j=n-1 ; j>=0 ; j--)
        {
            cur2 = nums2[j];
            while(i>=0 && cur1 > cur2)
            {
                nums1[i+j+1] = nums1[i];
                i--;
                if(i<0)
                    break;
                cur1 = nums1[i];
            }
            nums1[i+j+1] = cur2;
        }
    }
};

再围观一下大佬的题解:

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int len1 = m - 1;
        int len2 = n - 1;
        int len = m + n - 1;
        while(len1 >= 0 && len2 >= 0) {
            // 注意--符号在后面,表示先进行计算再减1,这种缩写缩短了代码
            nums1[len--] = nums1[len1] > nums2[len2] ? nums1[len1--] : nums2[len2--];
        }
        // 表示将nums2数组从下标0位置开始,拷贝到nums1数组中,从下标0位置开始,长度为len2+1
        System.arraycopy(nums2, 0, nums1, 0, len2 + 1);
    }
}

其实意思是一样的,只不过我这种更丑。
这里学到了两个新的函数:System.arraycopy() ; swap()

100. 相同的树

我的想法挺简单的,既然比较树是否相同,那遍历一遍不就好了?
于是我写了个深度优先遍历。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool isSameTree(TreeNode* p, TreeNode* q) {
        //处理有空树的情况
        if(p == NULL && q == NULL)
            return true;
        if((p == NULL && q!=NULL)||(p != NULL && q==NULL))
            return false;
        
        stack<TreeNode*> ss1;
        stack<TreeNode*> ss2;
        if(p->val != q->val)
            return false;
        //将树根压入栈
        ss1.push(p);
        ss2.push(q);
        p = p->left;
        q = q->left;

        //在栈不空的时候深度优先遍历树
        while(!ss1.empty() || !ss2.empty())
        {
            while(p == NULL || q == NULL)
            {
                if((p == NULL && q!=NULL)||(p != NULL && q==NULL))
                    return false;
                if((ss1.empty() && !ss2.empty()) || (!ss1.empty() && ss2.empty()))
                    return false;
                if(ss1.empty() && ss2.empty())
                    return true;
                p = ss1.top();
                ss1.pop();
                q = ss2.top();
                ss2.pop();
                p = p->right;
                q = q->right;
            }
            if(p->val != q->val)
                return false;
            ss1.push(p);
            ss2.push(q);
            p = p->left;
            q = q->left;
        }
        if(ss1.empty() && ss2.empty())
            return true;
        return false;

    }
};

显然算法课还是有点用的,起码我还记得深度优先用栈,广度优先用队列,这样写也是双百的……然后我发现看题解,这道题更简洁的写法是用递归。

class Solution {
public:
    bool isSameTree(TreeNode* p, TreeNode* q) {
        if(p == NULL || q == NULL)
        {
            if(p == NULL && q == NULL)
                return true;
            return false;
        }
        if(p->val == q->val)
            return (isSameTree(p->left,q->left)&&isSameTree(p->right,q->right));
        return false;
    }
};

这样写瞬间变好看了有木有。

101. 对称二叉树

有了100题做铺垫,这道题就很好做了。我们只需要将left子树与left子树比较 变成 left子树与right子树的比较。

class Solution {
public:
    bool check(TreeNode* p,TreeNode* q)
    {
        if(!p && !q)
            return true;
        if(!p || !q)
            return false;
        if(p->val == q->val)
            return (check(p->left,q->right)&&check(p->right,q->left));
        return false;
    }
    bool isSymmetric(TreeNode* root) {
        if(root == NULL)
            return true;
        return check(root->left,root->right);
    }
};

很愉快。
当然官方也给了迭代的算法,即广度遍历,只不过注意因为要检查镜像对称,所以压入的时候也是一左一右地往队列里压。

107. 二叉树的层次遍历 II

其实这道题很好写,但是不熟悉C++的一些语法,所以卡了蛮久。

第一,vector> res 这样格式的二维数组,可以通过push_back向里面添加元素。要么一行一行的添,要么一个一个地添:res.push_back({一个一维vector数组});,或者res[0].push_back(1);
这里可以参考一下这个:二维vector的性质、向二维vector添加元素的方法

第二,要理解广度优先遍历和深度优先遍历的区别。
除了广度优先遍历用队列,深度优先遍历用栈之外,还有一些微妙的区别。
广度优先是弹一个走,就压入它的孩子;而深度优先是压无可压,才弹出。

第三,C++的一个库,reverse,可以对容器等进行反转。
参考:C++ reverse函数的用法 - yuanch2019 - 博客园

第四,很巧妙的一点,当广度遍历完一层时,此时队列中保留的恰好是下一层的所有元素。

于是最后我们得到了这样一个代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> levelOrderBottom(TreeNode* root) {
        queue<TreeNode*> myq;
        vector<vector<int>> res;
        TreeNode* cur;
        if(root == NULL)
            return res;
        else
            myq.push(root);

        while(!myq.empty())
        {
            int length = myq.size();
            vector<int> curlevel;
            for(int i=0; i<length ; i++)
            {
                cur = myq.front();
                myq.pop();
                
                if(cur != NULL)
                {
                    curlevel.push_back(cur->val);
                    myq.push(cur->left);
                    myq.push(cur->right);
                }
            }

            if(curlevel.size() > 0)
                res.push_back(curlevel);
        }
        reverse(res.begin(),res.end());
        return res;
    }
};

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

这道题官方答案写得很清晰了。
法一:递归

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        int size = nums.size();
        if(size == 0)
            return NULL;
        TreeNode* cur = new TreeNode;
        cur->val = nums[size/2];
        vector<int>::iterator i1 = nums.begin(), i2 = nums.begin() + size/2;
        vector<int> leftr(i1,i2); 
        cur->left = sortedArrayToBST(leftr);
        vector<int>::iterator i3 = nums.end(), i4 = nums.begin() + size/2 + 1;
        vector<int> rightr(i4,i3); 
        cur->right = sortedArrayToBST(rightr);
        return cur;
    }
};

我这里直接把递归写在了原函数里,用了迭代器来截断数组。也可以像官方一样再声明一个含坐标的函数,方便使用。

110. 平衡二叉树

class Solution {
public:
    int height(TreeNode* root)
    {
        if(root == NULL)
            return -1;
        else
            return 1 + max(height(root->left),height(root->right));
    }
    bool isBalanced(TreeNode* root) {
        if(root == NULL)
            return true;
        else if(abs(height(root->left)-height(root->right))>1)
            return false;
        else
            return (isBalanced(root->left)&&isBalanced(root->right));
    }
};

我想到了要递归检查是否平衡,没想到还要递归检查左右子树的高度,真就老千层饼了。

111. 二叉树的最小深度

有上一题的铺垫,很容易就跳坑里了。
一定要搞清楚,什么是深度!什么是高度!这俩是不一样的。
而题目说明里也写清楚了,最小深度是从根节点到最近叶子节点的最短路径上的节点数量,这个节点是包含了根节点本身的。
当树为空,深度为0。其实这是为了返回一个int值人为设定的,我觉得理解为不存在更好。
如果一棵树只有树根,它的深度是1.一棵树的左子树为空,那么就要看它右侧的深度,我们可以理解为它左侧的深度不存在,而不是为0.

112. 路径总和

在被上一题坑过之后,不自觉地开始加上很多限制条件,最后被自己骚死。
当然,也需要好好理解题意:
当树为空,无论sum是多少,结果都是false,可以理解为它的值为Null。
如果只有树根,它的值不为Null。虽然没有叶子节点,但也是可以进行判断的。
比较方便的是,我们采用||来判断结果,所以假如左子树为空,也不影响结果的判断,只要右边存在就能找到。
111和112可以比对着多看看。

118.杨辉三角

我可能递归上瘾,第一反应就是递归:

class Solution {
public:
    vector<vector<int>> result;
    vector<int> eachrow(int numRows)
    {
        if(numRows == 1)
        {
            vector<int> cur;
            cur.push_back(1);
            result.push_back(cur);
            return cur;
        }
        vector<int> cur;
        vector<int> last = eachrow(numRows-1);
        cur.push_back(1);

        for(int i = 1; i<numRows-1 ; i++)
        {
            cur.push_back(last[i-1]+last[i]);
        }
        cur.push_back(1);
        result.push_back(cur);
        return cur;
    }

    vector<vector<int>> generate(int numRows) {

        vector<int> cur;
        if(numRows == 0)
        {
            return result;
        }
        eachrow(numRows);
        return result;
    }
};

递归的耗时不是很理想,我去看了一下大家的解法,用动态规划的比较多。
d[i][j] =
1 if j = 0 or j = i;
d[i-1][j-1] + d[i-1][j] else
就是这个思路,写出来大概长这样:

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> result(numRows);
        if(numRows == 0)
            return result;
        int i,j;
        for(i = 0; i<numRows ; i++)
        {
            result[i] = vector<int>(i+1,0);		//一定要记得声明数组大小
            result[i][0] = 1;
            result[i][i] = 1;
        }
        for(i = 2 ; i<numRows ; i++)
        {
            for(j=1 ; j<i ; j++)
            {
                result[i][j] = result[i-1][j-1] + result[i-1][j];
            }
        }
        return result;
    }
};

你可能感兴趣的:(刷题吭吭,c++,leetcode,算法)