Tree, BST, Trie

Sujinยท2022๋…„ 12์›” 3์ผ
0

LeetCode

๋ชฉ๋ก ๋ณด๊ธฐ
7/24
post-thumbnail

๐Ÿ’ก Maximum Depth of Binary Tree

Given the root of a binary tree, return its maximum depth.

A binary tree's maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.

Example 1:

Input: root = [3,9,20,null,null,15,7]
Output: 3

Example 2:

Input: root = [1,null,2]
Output: 2

Solution

Recursion (DFS)

DFS: traversing depth first

์žฌ๊ท€์ ์œผ๋กœ ์ƒ๊ฐํ•˜๋ฉด ๊ฐ„๋‹จํ•˜๋‹ค

์ผ๋‹จ ํ˜„์žฌ root๊ฐ€ null ํฌ์ธํ„ฐ๋ผ๋ฉด length๋Š” 0
์•„๋‹ˆ๋ผ๋ฉด leftchild ์™€ rightchild์ค‘์— ๋” ๊นŠ์€ depth๋ฅผ ๊ฐ€์ง„์• ๋ฅผ ์ฐพ์•„์„œ +1์„ ํ•ด์ค€๋‹ค

class Solution {
public:
    int maxDepth(TreeNode* root) {
        if (root == NULL) return 0;
        return max(maxDepth(root->left), maxDepth(root->right)) + 1;
    }
};

Iteration (BFS)

BFS: traversing level by level

number of level = number of depth
๊ทธ๋ž˜์„œ,, queue๋ฅผ ์ด์šฉํ•ด์„œ ๊ทธ๋ƒฅ level์ˆ˜๋ฅผ ์„ธ๋ฉด ๋œ๋‹ค~!

class Solution {
public:
    int maxDepth(TreeNode* root) {
        if (root == NULL) {
            return 0;
        }
        queue<TreeNode*> q;
        q.push(root);
        int result = 0;
        while (!q.empty()) {
            int count = q.size();
            for (int i = 0; i < count; i++) {
                TreeNode* node = q.front();
                q.pop();
                if (node->left != NULL) {
                    q.push(node->left);
                }
                if (node->right != NULL) {
                    q.push(node->right);
                }
            }
            result++;
        }
        return result;
    }
};

๐Ÿ’ก Minimum Depth of Binary Tree

์œ„์™€ ๊ฐ™์€ ๋ฌธ์ œ์ด์ง€๋งŒ ์ด๋ฒˆ์—” minimum depth ๋ฅผ ์ฐพ์•„๋ณด์ž

์œ„์˜ ์ฝ”๋“œ๋ฅผ ์กฐ๊ธˆ๋งŒ ๋ฐ”๊พธ๋ฉด ๋˜๊ฒ ๋‹ค

Solution

Recursion (DFS)

์ฃผ์˜ํ•ด์•ผํ•  ๋ถ€๋ถ„์€ leafnode์˜ depth๋ฅผ ์„ธ์ง€ ์•Š๋„๋ก ํ•˜๋Š” ๊ฒƒ ์ด๋‹ค
leafnode์— minDepth๋ฅผ ๋Œ๋ฆฐ๋‹ค๋ฉด 0์ด ๋‚˜์˜ค๊ธฐ ๋•Œ๋ฌธ์— ์šฐ๋ฆฌ์˜ min ๊ฐ’์ด ์˜ํ–ฅ์„ ๋ฐ›๋Š”๋‹ค

์ž์‹์ด ํ•˜๋‚˜๋งŒ ์žˆ๋‹ค๋ฉด:
null์ด ์•„๋‹Œ ์ž์‹์—๊ฒŒ๋งŒ recursion์„ ๋Œ๋ ค์•ผํ•œ๋‹ค

(completely unbalanced tree๋ฅผ ์ƒ๊ฐํ•ด๋ณด์ž)

class Solution {
public:
    int minDepth(TreeNode* root) {
        if (root == NULL) return 0; //์ง„์งœ root๋งŒ ์—ฌ๊ธฐ condition์— ํ•ด๋‹น
        if (!root->left && !root->right) return 1; //leafnode

        // ์ž์‹์ด ์กด์žฌํ•œ๋‹ค๋Š” ๊ฒฝ์šฐ์—๋งŒ ๊ทธ์ž์‹์— ๋Œ€ํ•œ minDepth๋ฅผ ์‹คํ–‰ํ•œ๋‹ค
        // ์ž์‹์ด ์กด์žฌํ•˜์ง€ ์•Š๋‹ค๋ฉด depthend์ด๊ธฐ ๋•Œ๋ฌธ์— ์šฐ๋ฆฌ์˜ min ๊ณ„์‚ฐ์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋„๋ก INT_MAX๋กœ ์ง€์ •ํ•œ๋‹ค
        int left = root->left ? minDepth(root->left) : INT_MAX;
        int right = root->right ? minDepth(root->right) : INT_MAX;

        return min(left, right) + 1;
    }
};

Iterative (BFS)

ํ•ต์‹ฌ์€: leafnode๋ฅผ ๋งŒ๋‚˜๋Š” ์ˆœ๊ฐ„์ด min depth์ด๋‹ค!

class Solution {
public:
    int minDepth(TreeNode* root) {
        if (root == NULL) {
            return 0;
        }
        queue<TreeNode*> q;
        q.push(root);
        int result = 1;
        while (!q.empty()) {
            int count = q.size();
            for (int i = 0; i < count; i++) {
                TreeNode* node = q.front();
                q.pop();
                // if the node is a leafnode, min depth is here
                if (!node->left && !node->right) return result;
                // else push children into the queue and calculate the next level
                if (node->left) q.push(node->left);
                if (node->right) q.push(node->right);
            }
            result++;
        }
        return result;
    }
};

๐Ÿ’ก Merge Two Binary Trees

You are given two binary trees root1 and root2.

Imagine that when you put one of them to cover the other, some nodes of the two trees are overlapped while the others are not. You need to merge the two trees into a new binary tree. The merge rule is that if two nodes overlap, then sum node values up as the new value of the merged node. Otherwise, the NOT null node will be used as the node of the new tree.

Return the merged tree.

Note: The merging process must start from the root nodes of both trees.

Example 1:

Input: root1 = [1,3,2,5], root2 = [2,1,3,null,4,null,7]
Output: [3,4,5,5,4,null,7]

Example 2:

Input: root1 = [1], root2 = [1,2]
Output: [2,2]

Solution

์˜ค ์‰ฝ๋„ค!
ํ•œ๋ฒˆ์— ํ’€์—‡๋‹น

class Solution {
public:
    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        TreeNode *merged; 

        if (root1 && root2) {
            // overlapping nodes
            int val = root1->val + root2->val;
            merged = new TreeNode(val);
            merged->left = mergeTrees(root1->left, root2->left);
            merged->right= mergeTrees(root1->right, root2->right);

        } else {
            // if not overlapping nodes
            if (root1) {
                merged = root1;
            } else {
                merged = root2;
            }
        }

        return merged;
    }
};

๐Ÿ’ก Convert Sorted Array to Binary Search Tree

Given an integer array nums where the elements are sorted in ascending order, convert it to a height-balanced binary search tree.

Example 1:

Input: nums = [-10,-3,0,5,9]
Output: [0,-3,9,-10,null,5]
Explanation: [0,-10,5,null,-3,null,9] is also accepted:

Example 2:

Input: nums = [1,3]
Output: [3,1]
Explanation: [1,null,3] and [3,1] are both height-balanced BSTs.

Solution

๊ฐ€์šด๋ฐ๋ฅผ ๋‚˜๋ˆ ์„œ ๊ทธ ๊ฐ€์šด๋ฐ๋ฅผ ๊ธฐ์ค€์œผ๋กœ root๋ผ๊ณ  ํ•˜๊ณ  ์–‘์˜†์„ ๊ฐ๊ฐ์˜ left, right children์—๊ฒŒ ์ค€๋‹ค!

์•„๋‹ˆ ๋˜๊ฒŒ ๊ฐ„๋‹จํ•œ ๋ฌธ์  ๋ฐ vector slicing ํ•˜๋Š”๊ฑฐ ๋ญ ํ•˜๋Š” ๊ฒƒ ๋งˆ๋‹ค ์˜ค๋ฅ˜๋œจ๋ƒ ๊ฐœ๋นข์น˜๋„ค

class Solution {
public:
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        if (nums.size() == 0) return NULL;

        int mid = nums.size() / 2 ;
        TreeNode *node = new TreeNode(nums[mid]);

        vector<int> left(nums.begin(), nums.begin() + mid);
        vector<int> right(nums.begin() + mid + 1, nums.end());

        node->left = sortedArrayToBST(left); 
        node->right = sortedArrayToBST(right); 

        return node;
    }
};

ํœด ๋“œ๋””์–ด ๋๋„ค!!

๐Ÿ’ก Path Sum

Given the root of a binary tree and an integer targetSum, return true if the tree has a root-to-leaf path such that adding up all the values along the path equals targetSum.

Example 1:

Input: root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
Output: true
Explanation: The root-to-leaf path with the target sum is shown.

Example 2:

Input: root = [1,2,3], targetSum = 5
Output: false
Explanation: There two root-to-leaf paths in the tree:
(1 --> 2): The sum is 3.
(1 --> 3): The sum is 4.
There is no root-to-leaf path with sum = 5.

Example 3:

Input: root = [], targetSum = 0
Output: false
Explanation: Since the tree is empty, there are no root-to-leaf paths.

Solution

class Solution {
public:
    bool hasPathSum(TreeNode* root, int targetSum) {
        if (root == NULL) return false;

        int val = root->val;
        if (targetSum - val == 0 && !root->left && !root->right) return true;
        return hasPathSum(root->left, targetSum - val) || hasPathSum(root->right, targetSum - val);
    }
};

๐Ÿ’ก Binary Tree Level Order Traversal

Given the root of a binary tree, return the level order traversal of its nodes' values. (i.e., from left to right, level by level).

Example 1:

Input: root = [3,9,20,null,null,15,7]
Output: [[3],[9,20],[15,7]]

Example 2:

Input: root = [1]
Output: [[1]]

Example 3:

Input: root = []
Output: []

Solution

outputํ˜•ํƒœ๋ฅผ ๋ณด๋ฉด ~ ์ด๊ฑฐ๋Š” ๋”ฑ๋ด๋„ recursion ์•„๋‹ˆ๊ณ  iteration BFS๋„ค!

Queue๋ฅผ ์“ฐ์ž!!

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> result;

        if (root == NULL) return result;

        queue<TreeNode*> q;
        q.push(root);

        while (!q.empty()) {
            vector<int> cur_level;
            int n = q.size();

            // for each level
            for (int i = 0; i < n; i++) {
                TreeNode *node = q.front();
                q.pop();

                cur_level.push_back(node->val);

                if (node->left) q.push(node->left);
                if (node->right) q.push(node->right);
            }

            result.push_back(cur_level);
        }

        return result;
    }
};

๐Ÿ’ก Binary Tree Zigzag Level Order Traversal

Given the root of a binary tree, return the zigzag level order traversal of its nodes' values. (i.e., from left to right, then right to left for the next level and alternate between).

Example 1:

Input: root = [3,9,20,null,null,15,7]
Output: [[3],[20,9],[15,7]]

Example 2:

Input: root = [1]
Output: [[1]]

Example 3:

Input: root = []
Output: []

Solution

๋ฐ”๋กœ ์ด์ „ ๋ฌธ์ œ๋ฅผ ๊ฐ”๋‹ค ์จ์„œ ์‹ค์ œ ๋‹ต์„ ๋งŒ๋“ค์–ด ์ค„๋•Œ right โ†’ left ํ•ด์ค˜์•ผ ํ•˜๋Š” level์—์„œ๋Š” ์ˆซ์ž๋ฅผ ๋‹ต vector์—์„œ ์•ž์—๋‹ค๊ฐ€ ์‚ฝ์ž…ํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ํ–ˆ๋‹ค

class Solution {
public:
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
        vector<vector<int>> result;

        if (root == NULL) return result;

        queue<TreeNode*> q;
        q.push(root);
        int level = 1;

        while (!q.empty()) {
            vector<int> cur_level;
            int n = q.size();

            // for each level
            for (int i = 0; i < n; i++) {
                TreeNode *node = q.front();
                q.pop();

                if (level % 2 == 0) {
                    // right->left ์ผ๋•Œ๋Š” vector์˜ ์•ž์—๋‹ค๊ฐ€ ์‚ฝ์ž…ํ•ด์ค€๋‹ค
                    cur_level.insert(cur_level.begin(), node->val);
                } else {
                    // left->right ์ผ๋•Œ๋Š” ์ˆœ์„œ๋Œ€๋กœ ๋“ค์–ด๊ฐ€๋„๋ก ์›๋ž˜์ฒ˜๋Ÿผ push ํ•ด์ค€๋‹ค
                    cur_level.push_back(node->val);
                }

                if (node->left) q.push(node->left);
                if (node->right) q.push(node->right);
            }

            result.push_back(cur_level);
            level++;
        }

        return result;
    }
};

๐Ÿ’ก Validate Binary Search Tree

Given the root of a binary tree, determine if it is a valid binary search tree (BST).

A valid BST is defined as follows:

The left subtree of a node contains only nodes with keys less than the node's key.
The right subtree of a node contains only nodes with keys greater than the node's key.
Both the left and right subtrees must also be binary search trees.

Example 1:

Input: root = [2,1,3]
Output: true

Example 2:

Input: root = [5,1,4,null,null,3,6]
Output: false
Explanation: The root node's value is 5 but its right child's value is 4.

Solution

ํ•ด๋‹น subtree๊ฐ€ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋Š” min๊ฐ’๊ณผ max๊ฐ’์„ parameter์— ์œ ์ง€ํ•ด์„œ ๋น„๊ตํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค

left child์˜ subtree์˜ ๊ฐ’์€ ํ˜„์žฌ root๊ฐ’ ๋ณด๋‹ค ์ปค์„œ๋Š” ์•ˆ๋œ๋‹ค
right child์˜ subtree์˜ ๊ฐ’์€ ํ˜„์žฌ root๊ฐ’๋ณด๋‹ค ์ž‘์•„์„œ๋Š” ์•ˆ๋œ๋‹ค

class Solution {
public:
    bool isValid(TreeNode* root, TreeNode* min, TreeNode* max) {
        if(root == NULL) return true;
        if(min && root->val <= min->val) return false;
        if(max && root->val >= max->val) return false;
        
        return isValid(root->left, min, root) && isValid(root->right, root, max);
    }
    
    bool isValidBST(TreeNode* root) {
        return isValid(root, NULL, NULL);
    }
};

๐Ÿ’ก Construct Binary Tree from Preorder and Inorder Traversal

Given two integer arrays preorder and inorder, construct and return the binary tree.

Example 1:

Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]

Example 2:

Input: preorder = [-1], inorder = [-1]
Output: [-1]

Solution

preorder,, inorder,, ์ด๊ฑฐ ๋ญ”๊ฐ€ cs341์ธ๊ฐ€์—์„œ ๋‹ค ํ–ˆ๋˜๊ฑฐ ๊ฐ™์€๋ฐ ์ง„์งœ ๋ ˆ์•Œ ๊ธฐ์–ต 1๋„ ์•ˆ๋‚œ๋‹ค ,, ๋ฏธ์ณค

์ผ๋‹จ ์ด๊ฑฐ๋ผ๋„ ๋ณด๋ฉด์„œ ๋ณต๊ธฐ๋ฅผ ํ•ด๋ณด์ž,,,

preorder traversal์˜ ํŠน์ง•์€:

  • ์ œ์ผ๋จผ์ € root๊ฐ€ ๋‚˜์˜ค๊ณ 
  • ๋ชจ๋“  left child๋ฅผ ๋จผ์ €๋ณด๊ณ 
  • ๊ทธ๋‹ค์Œ์— right child ๋ฅผ ๋ณด๊ฒŒ ๋œ๋‹ค

์œ„์˜ ์˜ˆ์‹œ์—์„œ๋Š” [1 2 3 4 5]๊ฐ€ ๋  ๊ฒƒ์ด๋‹ค
โ€ผ๏ธ ์ด ๊ฐœ๋…์ด ์ฝ”๋“œ์˜ index์— ์ ์šฉ๋œ๋‹ค

inorder traversal์˜ ํŠน์ง•์€:

root element๋ฅผ ์ฐพ์œผ๋ฉด ๊ทธ ๊ธฐ์ค€์œผ๋กœ

  • ์™ผ์ชฝ์€ ๊ทธ root์˜ left subtree์ด๊ณ 
  • ์˜ค๋ฅธ์ชฝ์€ right subtree์ด๋‹ค
class Solution { 
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int index = 0;
        return build(preorder, inorder, index, 0, inorder.size() - 1);
    }
private:
    TreeNode* build(vector<int>& preorder, vector<int>& inorder, int& index, int i, int j) {
        // **
        // * ์ผ๋‹จ index๋ฅผ ์ฃผ์†Œ ๊ฐ’์œผ๋กœ ๋ฐ›์•„ ๋งˆ์น˜ global variable ์ธ๊ฑฐ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•˜์ž
        // * *
        
		// i์™€ j๋Š” inorder array์˜ index๊ฐ’์ด๋‹ค 
        // ํ˜„์žฌ node๊ฐ€ root๊ฐ€ ๋˜๋Š” ๊ธฐ์ค€์œผ๋กœ ๋ดค์„ ๋•Œ inorder array[i:j+1]๊ฐ€ ํ•ด๋‹น๋œ๋‹ค
        if (i > j) { // OOB
            return NULL;
        }
        
        TreeNode* root = new TreeNode(preorder[index]);
        
        int split = 0;
        for (int i = 0; i < inorder.size(); i++) {
            if (preorder[index] == inorder[i]) {
                split = i;
                break;
            }
        }

        // **
        // * preorder์˜ ํŠน์ง•์„ ์ƒ๊ฐํ•ด๋ณด์ž
        // * ์™ผ์ชฝ์• ๋“ค ์ดค๋ฅด๋ฅต ๋จผ์ €ํ•˜๊ณ  ๊ทธ๋‹ด์— ์˜ค๋ฅธ์ชฝ์• ๋“ค์ด ์‹œ์ž‘๋œ๋‹ค
        // * ๋”ฐ๋ผ์„œ ์ด index๋ฅผ ์ƒ์„ฑ๋œ function stack๋งˆ๋‹ค ํ•œ์นธ์”ฉ ์˜ฌ๋ ค์ฃผ๊ณ 
        // * index๋Š” ์ฃผ์†Œ๊ฐ’์„ ๋ฐ›์•„์™€์„œ ์ด์ „๊ฐ’์ด ์œ ์ง€๊ฐ€ ๋˜๋ฏ€๋กœ
        // * ์ด๋Ÿฐ๋ฐฉ์‹์ด ํ†ตํ•˜๋Š” ๊ฒƒ์ด๋‹ค
        // ** 
        index++;
        // left build๊ฐ€ ๋ชจ๋‘ ๋๋‚˜์„œ์•ผ right build๊ฐ€ ์‹œ์ž‘๋œ๋‹ค
        // index๋ฅผ ์ด๋ ‡๊ฒŒ ๊ฐ„ํŽธํ•˜๊ฒŒ ์ค„ ์ˆ˜ ์žˆ๋Š”๊ฒƒ์€ preorder์˜ ํŠน์ง• ๋•๋ถ„
        root->left = build(preorder, inorder, index, i, split - 1); 
        root->right = build(preorder, inorder, index, split + 1, j);
        
        return root;
    }
};

๐Ÿ’ก Binary Tree Maximum Path Sum

A path in a binary tree is a sequence of nodes where each pair of adjacent nodes in the sequence has an edge connecting them. A node can only appear in the sequence at most once. Note that the path does not need to pass through the root.

The path sum of a path is the sum of the node's values in the path.

Given the root of a binary tree, return the maximum path sum of any non-empty path.

Example 1:

Input: root = [1,2,3]
Output: 6
Explanation: The optimal path is 2 -> 1 -> 3 with a path sum of 2 + 1 + 3 = 6.

Example 2:

Input: root = [-10,9,20,null,null,15,7]
Output: 42
Explanation: The optimal path is 15 -> 20 -> 7 with a path sum of 15 + 20 + 7 = 42.

Solution

์ด๊ฑด ์ฆ๋งฌ๋ฃจ ์–ด๋ ต๋‹น ,, ํ•˜์ง€๋งŒ ๋ญ”๊ฐ€ ์ธํ„ฐ๋ทฐ์— ๋‚˜์˜ฌ๊ฒƒ ๊ฐ™์€ ์งˆ๋ฌธ์ด์•ผ,,

class Solution {
public:
    int maxPathSum(TreeNode* root) {
        int maxPath = INT_MIN;
        // maxPath is not updated when the path from root to leaf w/o split
        //  has the maxPath 
        // and the return value for this line will return such path
	    int m = dfs(root, maxPath); 
        return max(m, maxPath); 
    }
private:
    int dfs(TreeNode *root, int &maxPath) {
        if (root == NULL) return 0;

        // this will compute the subtrees
        //  will not consider split in the subtree
        //  left and right each represent the maxPath from bottom to root->left/root->right without any splits
        int left = max(dfs(root->left, maxPath), 0);
        int right = max(dfs(root->right, maxPath), 0);

        // we will eventually consider the split case in here
        int cur_path = root->val + left + right; 
        maxPath = max(cur_path, maxPath);

        // return "the maxPath from bottom to root without any splits"
        return root->val + max(left, right);
    }
};

๐Ÿ’ก Implement Trie (Prefix Tree)

class TrieNode { // alphabet nodes
public:
    TrieNode* children[26];
    bool isWord; // to mark that this is a word
    
    TrieNode() {
        for (int i = 0; i < 26; i++) {
            children[i] = NULL;
        }
        isWord = false;
    }
};

class Trie { // this is the rootnode (wrapper)
private:
    TrieNode *root;

public:
    Trie() {
        root = new TrieNode();
    }
    
    void insert(string word) {
        // start from the root
        TrieNode *curr = root;
        int n = word.size();

        for (int i = 0; i < n; i++) {

            int idx = word[i] - 'a';
            
            if (curr->children[idx] == NULL) {
                // create new node when neccessary (wasn't created before)
                curr->children[idx] = new TrieNode();
            }
            curr = curr->children[idx];
        }
        
        // mark that this is a word
        curr->isWord = true;
    }
    
    bool search(string word) {

        TrieNode *curr = root;
        int n = word.size();

        for (int i = 0; i < n; i++) {

            int idx = word[i] - 'a';

            if (curr->children[idx] == NULL) {
                // when there isn't a word 
                return false;
            } 
            curr = curr->children[idx];
        }

        // if this is a word
        return curr->isWord;
    }
    
    bool startsWith(string prefix) {
        TrieNode *curr = root;
        int n = prefix.size();
        for (int i = 0; i < n; i++) {
            int idx = prefix[i] - 'a';
            if (curr->children[idx] == NULL) {
                return false;
            }
            curr = curr->children[idx];
        }
        // similar as search() but it doesn't have to be a word, 
        // just need to BE there without facing any NULL 
        return true;
    }
};

/**
 * Your Trie object will be instantiated and called as such:
 * Trie* obj = new Trie();
 * obj->insert(word);
 * bool param_2 = obj->search(word);
 * bool param_3 = obj->startsWith(prefix);
 */
profile
๋ฉ‹์ฐ ๋‚˜ ,,(๊ฐ€ ๋˜๊ณ ํ”ˆ)

0๊ฐœ์˜ ๋Œ“๊ธ€