Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adt/tree #2

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open

Adt/tree #2

wants to merge 6 commits into from

Conversation

blingblin-g
Copy link
Contributor

@blingblin-g blingblin-g commented Apr 16, 2022

구현한 것

  • Binary Search Tree

Why?

  • 트리 중에서 제일 만만해보였다. (보이는 것과 현실은 많이 다르다.)

How to run

python3이상, tree 디렉토리에서

python3 BST.py

How?

  • 일단 python이 가장 익숙하고 쉬워서 python으로 구현했다.
  • '뇌를 자극하는 알고리즘'에서 C로 구현된 이진 탐색 트리 코드를 참고했다.
  • 파일 하나에 BinaryTreeNode, BST를 구현했고, main에서 이를 검증하는 코드를 작성했다.
  • BinaryTreeNode : 한 노드에 필요한 정보를 저장한 클래스. 거의 구조체에 가깝다.
  • BST : 이진 탐색 트리에 필요한 요소와 메서드를 가진 클래스
    • insert(self, node, child) -> None : 노드 삽입
    • search(self, node, data) -> None : 노드 탐색
    • search_min_node(self, node) : 최소값을 가진 노드 탐색
    • remove(self, node, parent, target_data) : 특정 노드 삭제
    • print(self, node) : 노드 출력
    • to_list(self, node, result) : 트리를 list로 변환

느낀 점

  • 트리 삭제는 생각보다 어렵다.
  • 처음엔 재귀가 까다로울 것 같아서 반복문으로 구현했는데, 재귀를 쓰면 의외로 더 깔끔해지고 편한 경우가 있는 것 같다.
  • 좀 어려웠는데 재밌다.

개선할 부분

  • pytest를 적용하면 좋을 것 같다^^ 테스트 코드 작성의 귀찮음을 극복하면 좋을 것 같다 😄
  • 수정하는 부분을 만들려다가 말았는데, 이유는 특정 노드에 특정 값을 넣었을 때 정렬이 깨질 수 있기 때문이다.
    • 정렬이 깨지지 않는 범위 내에서 수정을 허용하는 함수를 만들어야 할까? 아니면 애초에 금지하는 것이 좋을까?
    • 일단 이렇게 아래와 같이 강제로 넣을 수는 있다.
      to_update = bs_tree.find_node(root, 21)
      print(to_update.data)
      to_update.data = 99
      result = bs_tree.to_list(root, [])
      print(result)
      python BST.py
      [9, 10, 11, 19, 20, 99, 29, 30, 31, 39, 41, 50, 59, 60, 61, 69, 70, 71, 79, 80, 81, 89, 90, 91]
  • 메서드에 필요한 파라미터가 별로 직관적이지 않고 너무 더럽다고 느껴진다.
    • 애초에 root를 안넣으면 어떻게 되는건데..?
      -> 해당 노드를 기준으로 잡습니다! 뭔가 구려..! 항상 root를 사용자가 알고 있어야 하잖아? 라는 생각이 들었지만 그정도쯤은 알아둬도 상관없지 않나 싶기도 하고 (애초에 멤버 변수로 갖고 있어서 언제든 쓸 수 있긴 함).. 그치만 왠지 거부감이 조금 들었어요. 낯설어서 그런걸까?
  • 노드 삭제는 아직 복잡하게 느껴진다.
  • 예쁘게 출력하고 싶어서(실제 트리처럼) 깊이를 구하려고 했는데, 이게 더 어려울 것 같았다.
    • 깊이를 알아내려면 어떻게 하면 좋을까?

results

python3 BST.py

 * * * initial tree * * * 

[9, 10, 11, 19, 20, 21, 29, 30, 31, 39, 40, 41, 50, 59, 60, 61, 69, 70, 71, 79, 80, 81, 89, 90, 91]
True
removed is 41

 * * * after remove tree * * * 

[9, 10, 11, 19, 20, 21, 29, 30, 31, 39, 41, 50, 59, 60, 61, 69, 70, 71, 79, 80, 81, 89, 90, 91]
True

 * * * print partial * * * 

[ 9 ]
[ 10 ]
[ 11 ]

Comment on lines +82 to +85
if node.left and node.right:
min_node = self.find_min_node(node.right)
removed = self.remove(node, None, min_node.data)
node.data = min_node.data
Copy link
Contributor Author

@blingblin-g blingblin-g Apr 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

문제의 코드가 이 부분이었는데, 83line에서 removed가 알고리즘 책 다른 페이지에서는 min_node로 되어 있었어요. 그래서 오타구나 싶었는데 결과는 min_node를 넣든 removed를 넣든 동일 하다는 충격적인 사실..! 👀

@blingblin-g blingblin-g self-assigned this Apr 16, 2022
@blingblin-g blingblin-g added the ADT 자료구조 이론 label Apr 16, 2022
@blingblin-g blingblin-g requested review from rockpell, Ariyn and xxyoungkim and removed request for rockpell and Ariyn April 16, 2022 09:26
Comment on lines +7 to +8
self.left:BinaryTreeNode = left
self.right:BinaryTreeNode = right

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:BinaryTreeNode는 어떤 의미인가요?
:any가 붙어있으면 어떤 자료형이든 들어갈 수 있다는 뜻인가요?
그러면 :BinaryTreeNode는 해당 클래스의 요소만 들어갈 수 있다는 뜻이 아닐까 조심스레 추측해봤는데 제 추측이 어떤가요 컨펌 부탁드립니다😂

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맞습니다! BinaryTreeNode는 이진트리에 들어갈 노드를 클래스로 제가 만든거에요. :는 타입 힌트를 주는 거고 :BinaryTreeNode는 이진트리 노드 타입이라고 힌트를 주는 겁니다. 완벽한 추리왕! 👍

self.depth = 1
else:
self.root = BinaryTreeNode(None)
self.len = 0

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

len은 무슨 의미인가요? 문자열 길이 구할 때 사용하는 함수의 이름이라고 알고 있는데.. 비슷한 의미일까요? 노드의 개수를 카운트한다거나?!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

앗 요건 length의 줄임말입니다. (len 함수도 length의 줄임말입니다.)
보통 컴퓨터 언어에서 count -> cnt, index -> idx, length -> len 이런것들은 관습적으로 줄여쓰는 경우가 있습니다.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

줄임말을 열심히 익혀야겠어요 뭔가 줄임말을 써야 멋진 개발자 느낌이 나는 것 같아요🤣감사합니다!!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사실 이런건 줄이는 것 보다는 풀어 쓰는게 더 좋다고 하더라고요ㅎㅎ 하지만 저는 귀찮기 때문에 줄였습니다 하하!



def search(self, node, data) -> None:
# root node를 안넣으면 어떻게 되지?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

루트노드가 없으면 트리 자체가 못 만들어지지 않나여? 루트노드를 안 넣을 수도 있나요? 약간 dll에 head가 없는 상태인 그런 느낌일까요? 이런 것도 물어봐도 되나염..?😂

Copy link
Contributor Author

@blingblin-g blingblin-g Apr 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

당연하죠! 좋은 질문 감사합니다~ 저도 궁금해서 실행해봤는데, root 노드가 아니라 다른 노드를 넣으면 그 노드를 root로 인식하고 그걸 기준으로 탐색을 하더라구요. 아예 없는 건 말이 안되는 것 같고, 해당 트리의 진짜 루트가 아니더라도 뭘 기준으로 찾을 건지 여부를 결정해서 넣으면 되는 것 같았어요.

Copy link

@Ariyn Ariyn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 전체적으로 잘 만들어진 것 같습니다.
search나 insert에 재귀를 사용한게 좋았어요.

remove를 조금만 다듬어도 좋을 것 같아요. 뇌자알의 로직이 잘 이해 안되게 되어 있네요 😢

@@ -0,0 +1,67 @@
from xmlrpc.client import Boolean
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

기본 타입인 bool 대신 Boolean을 사용하신 이유가 있을까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

얘는 뭐지?? 발견해주셔서 감사해요ㅋㅋㅋㅋㅋㅋㅋ

def __init__(self, data:any, left=None, right=None) -> None:
self.data:any = data
self.left:Node = left
self.right:Node = right
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

조금 오래되어서 저도 헷갈리지만, 보통 function parameter에 type hint를 사용하는 것으로 기억하는데요
https://peps.python.org/pep-0484/#user-defined-generic-types

혹시 self의 필드값에 타입 힌트를 쓰면 이점이 있을까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그러게요, 파라미터에 주는 게 더 좋을 것 같네요! 감사합니다~

self.current = self.root

while True:
if data > self.current.data:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혹시 self.root = None인 상태에서 진행하면 문제가 생기지는 않을까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기서 빵 터져버릴 것 같네요 🤣

self.depth = 1
else:
self.root = BinaryTreeNode(None)
self.len = 0
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

앗 요건 length의 줄임말입니다. (len 함수도 length의 줄임말입니다.)
보통 컴퓨터 언어에서 count -> cnt, index -> idx, length -> len 이런것들은 관습적으로 줄여쓰는 경우가 있습니다.

return None
self.insert(node.left, child)

self.len += 1
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

재귀로 insert 함수가 호출되면서 len이 엄청나게 많이 증가하지 않을까 생각되는데요, 확인부탁드립니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맞아요. 감사합니다. 하나만 추가하는건데 len만 엄청나게 많아질 것 같네요. 근데 정작 len을 쓰는 곳이 없었어서 발견을 못했나봐요ㅋㅋㅋ

if node is None:
return

self.to_list(node.left, result)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기서 result가 reference로 전달되나요? result를 사용해서 to_list를 호출하지만, 반환된 값을 사용하지는 않는 것 같아서요.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기서 반환된 값을 쓰지는 않지만, 같은 result에 append를 계속 한 걸 보면 reference로 전달된다고 생각합니다!

Copy link
Member

@rockpell rockpell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Member

@rockpell rockpell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리뷰 철회 테스트

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ADT 자료구조 이론 리뷰완료
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants