leetcode(78-144)

78-Subsets「回溯」

Subsets

子集问题,元素没有重复值。直接套回溯模板:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> res;
        vector<int> tmp;
//        sort(nums.begin(), nums.end());
        backtrack(res, 0, tmp, nums);
        return res;
    }

    void backtrack(vector<vector<int>> & res, int start, vector<int>& tmp, vector<int>& nums)
    {
        res.push_back(tmp);
        for(int i=start; i<nums.size(); i++)
        {
            tmp.push_back(nums[i]);
            backtrack(res, i+1, tmp, nums);
            tmp.pop_back();
        }
    }
};

80-Subsets II「回溯」

Subsets II

同上题相似,但是给定数组中有重复值,需要注意条件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        vector<vector<int>> res;
        vector<int> path;
        sort(nums.begin(), nums.end());
        backtrack(res, path, nums, 0);
        return res;

    }

    void backtrack(vector<vector<int>> & res, vector<int> & path, vector<int> &nums, int start)
    {
        res.push_back(path);
        for(int i=start;i<nums.size();i++)
        {
            if(i==start||nums[i]!=nums[i-1])//注意如果122这种,如果对2已经回溯过了,那么下一个2应该跳过,不然结果是一样的!
            {
                path.push_back(nums[i]);
                backtrack(res, path, nums, i+1);
                path.pop_back();
            }
        }
    }
};

79-Word Search「DFS」

Word Search

路径搜索,寻找匹配字符。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
public:
    bool exist(vector<vector<char>>& board, string word) {
        for (int i = 0;i<board.size();i++)
            for(int j = 0; j<board[0].size();j++)
                if(dfs(board, i, j, word))
                    return true;
        return false;

    }

    bool dfs(vector<vector<char>>& board, int i, int j, string word)
    {
        if(!word.size())
            return true;
        if(i<0||i>=board.size()||j<0||j>=board[0].size()||word[0]!=board[i][j])
            return false;
        char store = board[i][j];
        word = word.substr(1);
        board[i][j] = '*';
        bool ret = dfs(board, i+1, j, word)||dfs(board, i-1, j, word)||dfs(board, i, j+1, word)||dfs(board, i, j-1, word);
        board[i][j] = store;
        return ret;
    }
};

94-Binary Tree Inorder Traversal「二叉树」

Binary Tree Inorder Traversal

中序遍历二叉树,两种方法,一种是常规方法,一种是Morris Traversal。第二种不借助额外的空间,属于利用树中原有的空指针。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class Solution {
public:
    vector<int> inorderTraversal(TreeNode *root) {
        vector<int> res;
        inorder(root, res);
        return res;
    }

    void inorder(TreeNode *root, vector<int> & res)
    {
        if(root!= nullptr)
        {
            inorder(root->left, res);
            res.push_back(root->val);
            inorder(root->right, res);
        }
    }
};

第二种方法,算法可以描述为:

  1. 如果当前节点的左孩子为空,则输出当前节点并将其右孩子作为当前节点。

  2. 如果当前节点的左孩子不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点。

    1. 如果前驱节点的右孩子为空,将它的右孩子设置为当前节点。当前节点更新为当前节点的左孩子。
    2. 如果前驱节点的右孩子为当前节点,将它的右孩子重新设为空(恢复树的形状)。输出当前节点。当前节点更新为当前节点的右孩子。
  3. 重复以上1、2直到当前节点为空。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Solution1 {
public:
    vector<int> inorderTraversal(TreeNode *root) {
        vector<int> res;
        inorderMorrisTraversal(root, res);
        return res;
    }

    void inorderMorrisTraversal(TreeNode *root, vector<int> & res)
    {
        TreeNode * pre = nullptr;
        TreeNode* cur = root;
        while (cur != nullptr)
        {
            if(cur->left == nullptr)
            {//如果当前节点的左孩子为空,则输出当前节点并将其右孩子作为当前节点。 1
                res.push_back(cur->val);
                cur = cur->right;
            } else
            {//左孩子不为空
                // 寻找前驱结点
                pre = cur->left;
                while (pre->right!= nullptr && pre->right != cur)
                {
                    pre = pre->right;
                }
                //前驱结点右孩子为空,则用这个前驱结点的右指针指向当前结点  2.1
                if(pre->right == nullptr)
                {
                    pre->right = cur;
                    cur = cur->left;
                } else
                {//前驱结点右孩子指向当前结点,则恢复成空指针   2.2
                    pre->right = nullptr;
                    res.push_back(cur->val);
                    cur = cur->right;
                }
            }
        }
    }
};

也可以改为先序遍历,只需要改变输出位置即可。只放在2.1处

参考

96-Unique Binary Search Trees「DP」

Unique Binary Search Trees

给定一个序列,求BST树的种类数。

假定G[n]表示序列1…n的BST树的种类数,而F[i, n]表示以i为根结点的BST树的种类数。

显然有: $$ F(i, n) = G(i-1) \times G(n-i) \quad 1\le i\le n
G(n) = F(1, n) + F(2, n) + \cdots + F(n, n) $$ 代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Solution {
public:
    int numTrees(int n) {
        int G[n+1];
        fill(G, G+n+1, 0); //数据初始化
        G[0] = 1;
        G[1] = 1;
        for(int i=2; i<=n; i++)
            for(int j = 1; j<=i; j++)
                G[i] += G[j-1] * G[i-j];
        return G[n];
    }
};

98-Validate Binary Search Tree「中序遍历」

Validate Binary Search Tree

判断是不是BST树,根据中序遍历序列递增可以判断。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
struct TreeNode{
    int val;
    TreeNode *left;
    TreeNode *right;
    explicit TreeNode(int x): val(x), left(nullptr), right(nullptr) {}
};


class Solution {
public:
    bool isValidBST(TreeNode* root) {
        stack<TreeNode*> s;
        TreeNode* pre= nullptr;
        while(root != nullptr || !s.empty())
        {
            while (root!= nullptr){
                s.push(root);
                root = root->left;
            }
            root = s.top();
            s.pop();

            if(pre!= nullptr && root->val <= pre->val)
                return false;
            pre = root;

            root = root->right;
        }
        return true;
    }
};

101-Symmetric Tree「二叉树」

Symmetric Tree

判断二叉树是否对称。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
struct TreeNode{
    int val;
    TreeNode *left;
    TreeNode *right;
    explicit TreeNode(int x): val(x), left(nullptr), right(nullptr) {}
};


class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        if(root== nullptr)
            return true;
        return valid(root->left, root->right);

    }

    bool valid(TreeNode* L, TreeNode* R)
    {
        if(L== nullptr||R== nullptr)
            return L==R;
        if(L->val != R->val)
            return false;
        return valid(L->right, R->left) && valid(L->left, R->right);
    }
};

还可以使用队列,进行层序遍历进行判断。

102-Binary Tree Level Order Traversal「队列」

Binary Tree Level Order Traversal

层序遍历二叉树。注意队列只放非空结点,这样节省空间和时间。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<TreeNode*> Q;
        vector<vector<int>> res;
        if(root == nullptr)
            return res;
        Q.push(root);
        int num;
        while (!Q.empty())
        {
            vector<int> tmp;
            num = Q.size();
            while (num > 0)
            {
                root = Q.front();
                Q.pop();

                if(root->left != nullptr)
                    Q.push(root->left);
                if(root->right != nullptr)
                    Q.push(root->right);
                tmp.push_back(root->val);
                num--;
            }
            res.push_back(tmp);
        }
        return res;
    }
};

104-Maximum Depth of Binary Tree「DFS/BFS」

Maximum Depth of Binary Tree

求二叉树的最大深度。

1
2
3
4
5
6
7
8
class Solution {
public:
    int maxDepth(TreeNode* root) {
        if(root == nullptr)
            return 0;
        return max(maxDepth(root->left), maxDepth(root->right)) + 1;
    }
};

使用BFS就是用队列层序遍历。

105-Construct Binary Tree from Preorder and Inorder Traversal「二叉树遍历」

Construct Binary Tree from Preorder and Inorder Traversal

经典题目

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {

        return build(preorder, inorder, 0, preorder.size()-1, 0, inorder.size()-1);
    }

    TreeNode* build(vector<int>& preorder, vector<int>& inorder, int pres, int pree, int ins, int ine)
    {
        if(pres > pree)
            return nullptr;
        auto* root = new TreeNode(preorder[pres]);
        int pos; // 根在中序遍历中的位置
        for(int i=ins; i<=ine; i++)
        {
            if(inorder[i] == root->val)
            {
                pos = i;
                break;
            }
        }
        root->left = build(preorder, inorder, pres+1, pres + (pos - ins), ins, pos-1);
        root->right = build(preorder, inorder, pres + (pos - ins)+ 1, pree, pos+1, ine);
        return root;
    }
};

114-Flatten Binary Tree to Linked List「二叉树遍历」

Flatten Binary Tree to Linked List

将一个二叉树压成链表。

这里采用一种右-左-根的遍历方式。这样树的遍历结果是[6->5->4->3->2->1],也就是链表从后往前生成。

只需要在处理“根部”的时候,将右孩子指向链表的当前结点,左孩子为空即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Solution {
private:
    TreeNode* pre = nullptr; //链表的前驱
public:
    void flatten(TreeNode* root) {
        if(root!= nullptr)
        {
            flatten(root->right);
            flatten(root->left);
            root->right = pre;
            root->left = nullptr;
            pre = root;
        }
    }
};

相反,采用根左右这种相反的顺序不可行,因为处理完根之后,左右孩子指向变了,需要加辅助变量,复杂很多。