Skip to content

Commit

Permalink
[Post] LC-450해설 및 BST개념 정리 (#160)
Browse files Browse the repository at this point in the history
Co-authored-by: cloud <[email protected]>
  • Loading branch information
sanghunlee-711 and cloud authored Jan 31, 2024
1 parent a616990 commit e26eb35
Showing 1 changed file with 204 additions and 0 deletions.
204 changes: 204 additions & 0 deletions public/posts/post-algorithm/2024-01-31-LC-450.md
Original file line number Diff line number Diff line change
@@ -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/)

0 comments on commit e26eb35

Please sign in to comment.