From e26eb35cfb10d0f557d64a5280e4e74291cc35dc Mon Sep 17 00:00:00 2001 From: sanghun lee <57477415+sanghunlee-711@users.noreply.github.com> Date: Wed, 31 Jan 2024 22:18:51 +0900 Subject: [PATCH] =?UTF-8?q?[Post]=20LC-450=ED=95=B4=EC=84=A4=20=EB=B0=8F?= =?UTF-8?q?=20BST=EA=B0=9C=EB=85=90=20=EC=A0=95=EB=A6=AC=20(#160)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: cloud --- .../posts/post-algorithm/2024-01-31-LC-450.md | 204 ++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 public/posts/post-algorithm/2024-01-31-LC-450.md diff --git a/public/posts/post-algorithm/2024-01-31-LC-450.md b/public/posts/post-algorithm/2024-01-31-LC-450.md new file mode 100644 index 0000000..bca326d --- /dev/null +++ b/public/posts/post-algorithm/2024-01-31-LC-450.md @@ -0,0 +1,204 @@ +--- +slug: 2024-01-31-LC-450 +title: LC#450 (Delete Node in a BST) +summary: LC#450 (Delete Node in a BST) +author: Sanghun lee +date: 2024-01-31 11:33:00 +0800 +categories: [LeetCode, BST, Successor, Predecessor, Inorder] +folder: [post-algorithm] +tags: [Algorithm] +math: true +mermaid: true +image: + src: https://upload.wikimedia.org/wikipedia/commons/thumb/0/0a/LeetCode_Logo_black_with_text.svg/640px-LeetCode_Logo_black_with_text.svg.png + width: 850 + height: 585 +--- + +# 문제 + +Given a root node reference of a BST and a key, delete the node with the given key in the BST. Return the root node reference (possibly updated) of the BST. + +Basically, the deletion can be divided into two stages: + +Search for a node to remove. +If the node is found, delete the node. + +```md +Input: root = [5,3,6,2,4,null,7], key = 3 +Output: [5,4,6,2,null,null,7] + +Explanation: Given key to delete is 3. So we find the node with value 3 and delete it. +One valid answer is [5,4,6,2,null,null,7], shown in the above BST. + +Please notice that another valid answer is [5,2,6,null,4,null,7] and it's also accepted. +``` + +Constraints: + +The number of nodes in the tree is in the range [0, 104]. + +-105 <= Node.val <= 105 + +Each node has a unique value. + +root is a valid binary search tree. + +-105 <= key <= 105 + +# 풀이 + +Solving카테고리가 LeetHub라는 익스텐션을 사용하게 되며 다른 별도의 레포에 문제가 solve상태가 되면 올라가게 만든 뒤로 조금 방치해놓은 카테고리가 된 것 같다. + +블로그에 알고리즘 글을 쓰는 것은 오랜만이지만.. 나름 시간 될때 한번씩은 알고리즘을 계속 풀고 있었다. + +오늘은 처음으로 보게 된 문제이고 정리할 개념도 있는 것 같아 글을 작성해본다. + +처음에는 간단하게 노드의 값을 null로 해결해버리면 되지 않을까 하였으나 역시나 삭제를 할때 위치를 바꾸고 정렬을 해주는 것이 당연히 필요한 문제이다. +BST 구조를 유지하는게 조건이니 당연한 것이었다. + +아래는 해설을 보며 처음 접해본 successor, predecessor에 관한 개념의 정리와 이 개념으로 문제를 해결한 방식이다. + +`Inorder traversal of BST is an array sorted in ascending order.` + +BST에서 중위 순회를 하게 되면 구조의 특성으로 인해 오름차순으로 정렬을 할 수 있다. + +```javascript +//inorder : left -> node -> right +const inorder = function (root, arr) { + if (!root) return arr; + inorder(root.left, arr); + arr.push(root.val); + inorder(root.right, arr); + return arr; +}; +``` + +여기에서 Successor(후임자), Predecessor(선행자)라는 개념을 활용할 수 있게 된다. + +Successor: 선택한 노드의 오른쪽 서브트리중 가장 작은 값을 가지는 노드를 의미한다. 중위순회를 한 상태에서 보게 된다면 successor는 해당 노드의 바로 다음 값이다. + +Predecessor: 선택한 노드의 값보다 작으면서 가장 큰 값을 가지는 노드를 의미한다(↔ Successor). 중위순회를 한 상태에서 보게 된다면 Predecessor는 해당 노드의 바로 전 값이다.(값이 같다면 본인이 될 수도 있지만 논외로하자) + +따라서 이러한 개념을 가지고 난 뒤, 해당 BST구조에서 해당되는 형태의 노드를 가져올 수 있는 간단한 수도코드를 살펴보자. + +```jsx +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +// one step right and then left till you can +const successor = function (root) { + root = root.right; + while (root.left !== null) root = root.left; + return root; +}; +``` + +```jsx +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +// one step left and then right till you can +const predecessor = function (root) { + root = root.left; + while (root.right !== null) root = root.right; + return root; +}; +``` + +여기까지 선행 지식을 습득하였으면 본 문제를 해결할 실마리가 생기는 것 같다. + +BST의 구조를 유지하면서 key값을 지울 때 successor, predecessor를 잘 활용한다면 노드를 바꿔치고 삭제하기가 수월해지기 때문이다. + +문제의 요구사항은 입력받은 이진탐색 트리와 key를 통해 key에 해당하는 노드를 지운 BST를 반환해달라는 것이고, 이 때 위의 개념들을 활용할 수 있게 된다. + +문제해결 시 key가 위치할 세가지 케이스를 나눠볼 수 있게 된다. + +1. key가 leaf인 경우 + + 1. node = null로 선언하여 간단하게 해결 + +2. leaf가 아니고 오른쪽 subtree에 있을 경우 → Successor를 찾아서 현재의 노드 값을 Sucessor값으로 변경한 후 실제 Successor노드를 날려버리는 방법 + +3. leaf가 아니고 왼쪽 subtree에 있을 경우 → Predecessor를 찾아 현재의 노드 값을 Predecessor로 변경한 후 실제 Predecessor노드를 날려버리는 방법 return + +아래는 위 케이스를 토대로 작성된 해결법이다. + +```jsx +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @param {number} key + * @return {TreeNode} + BFS +left is smaller right is bigger +if key is leaf node -> ? +if key is parent node -> ? +how to remove node -> 1. set val null ? 2. find node -> sort every node again without find node + */ + +const successor = function (root) { + root = root.right; + while (root.left) root = root.left; + return root.val; +}; + +const predecessor = function (root) { + root = root.left; + while (root.right) root = root.right; + return root.val; +}; + +var deleteNode = function (root, key) { + if (!root) return null; + // 오른쪽 서브트리에서 삭제되는 경우 + if (key > root.val) root.right = deleteNode(root.right, key); + // 왼쪽 서브트리에서 삭제되는 경우 + else if (key < root.val) root.left = deleteNode(root.left, key); + // 현재 노드를 삭제해야하는 경우 + else { + // 리프인 경우 + if (!root.left && !root.right) root = null; + // right subtree에서 삭제해야하는 경우 + else if (root.right) { + // successor.val로 값 대체 + root.val = successor(root); + // 재귀로 root.right의 값 제거 + root.right = deleteNode(root.right, root.val); + } + // left subtree에서 삭제해야하는 경우 + else { + root.val = predecessor(root); + root.left = deleteNode(root.left, root.val); + } + } + return root; +}; +``` + +# 3. 결론 + +언젠가 간단하게 자료구조를 공부할 때, BST는 중위순회하면 이렇게 된다더라 정도의 기억만 남은상태에서 바라본 문제라 어떠한 실마리도 생각하지 못하였다. + +하지만 새로운 접근법을 알게 되었으니 기분.. 좋게 마무리 하는 문제이다. 복습이 꼭 필요한 문제다. + +## 참고 + +- [LeetCode - 450.Delete Node in a BST](https://leetcode.com/submissions/detail/1161990903/)