부부의 코딩 성장 일기

LeetCode 98(Validate Binary Search Tree, Python) 본문

Algorithm/LeetCode

LeetCode 98(Validate Binary Search Tree, Python)

제로_콜라 2024. 2. 19. 19:00

1. 문제 링크

 

Validate Binary Search Tree - LeetCode

Can you solve this real interview question? 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 le

leetcode.com

2. 문제 설명

  • 주어진 tree가 이진 탐색 트리(BST, Binary Search Tree)인지 확인하여 True, False 반환하는 문제
  • 이진 탐색 트리란 아래 조건을 만족하는 Tree이다.
    • 왼쪽 서브트리의 모든 노드들은 현재 노드보다 작은 값
    • 오른쪽 서브트리의 모든 노드들은 현재 노드보다 큰 값
    • 왼쪽과 오른쪽 서브트리도 각각 이진 탐색 트리여야 함.
  • True인 예시

 2

/ \

1 3

  • False인 예시(3이 5보다 크지 않다.)

 5

/ \

1 4

   / \

  3   6

3. 처음 풀이

  • 왼쪽 서브 트리의 값이 모두 현재 노드 보다 작아야하므로 왼쪽 서브 트리의 값 중 최댓값이 현재 값보다 작아야한다. 마찬가지로 오른쪽 서브 트리의 최솟값은 현재 노드 보다 커야한다.
  • 따라서 주어진 노드를 루트로 하는 서브트리에서 최댓값, 최솟값을 찾는 함수를 만든 뒤, 재귀적으로 검사하는 방법을 생각하였다.
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def isValidBST(self, root: Optional[TreeNode]) -> bool:
        
				# 노드 없으면 유효
        if not root:
            return True

        # 하나 짜리도 유효
        if not root.left and not root.right:
            return True
        
				# 주어진 노드를 루트로 하는 서브트리에서 최대값을 찾는 함수        
        def findMax(root):
            if not root.left and not root.right:
                return root.val
            elif root.left and root.right:
                return max(root.val, findMax(root.left), findMax(root.right))
            elif root.right:
                return max(root.val, findMax(root.right))
            elif root.left:
                return max(root.val, findMax(root.left))
				
				# 주어진 노드를 루트로 하는 서브트리에서 최소값을 찾는 함수
        def findMin(root):
            if not root.left and not root.right:
                return root.val
            elif root.left and root.right:
                return min(root.val, findMin(root.left), findMin(root.right))
            elif root.left:
                return min(root.val, findMin(root.left))
            elif root.right:
                return min(root.val, findMin(root.right))

				# 왼쪽 서브트리의 최대값이 현재 노드보다 크면 False
        if root.left:
            if root.val <= findMax(root.left):
                return False

				# 오른쪽 서브트리의 최소값이 현재 노드보다 작으면 False
        if root.right:
            if root.val >= findMin(root.right):
                return False
        
				# 왼쪽 서브트리와 오른쪽 서브트리에 대해 재귀적으로 검사
        return self.isValidBST(root.left) and self.isValidBST(root.right)

4. 다른 풀이

  • 중위순회시 오름차순으로 값이 나와야함.
  • 중위순회하며 stack에 넣고 pop()하며 오름차순인지 확인하는 풀이.
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Solution:
    def isValidBST(self, root: Optional[TreeNode]) -> bool:
        # 중위 순회를 위한 스택
        stack = []
        # 이전에 방문한 노드의 값을 저장하는 변수
        prev = None
        
        # 중위 순회 시작
        while root or stack:
            # 현재 노드부터 가장 왼쪽 노드까지 스택에 추가
            while root:
                stack.append(root)
                root = root.left
            
            # 스택에서 노드를 꺼내어 현재 노드로 설정
            root = stack.pop()
            
            # 이전에 방문한 노드의 값과 비교하여 유효성 검사
            if prev is not None and root.val <= prev:
                return False
            
            # 현재 노드의 값을 이전에 방문한 노드로 설정
            prev = root.val
            
            # 오른쪽 서브트리로 이동
            root = root.right
        
        # 중위 순회 완료 후, 유효한 이진 탐색 트리임을 반환
        return True

 


  • 재귀적으로 접근하되 최댓값, 최솟값을 변수로 넣어 좀 더 효율적인 코드
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Solution:
    def isValidBST(self, root: Optional[TreeNode]) -> bool:
        # 재귀적으로 이진 탐색 트리를 검사하는 함수
        def helper(node, lower=float('-inf'), upper=float('inf')):
            # 노드가 없으면 유효한 이진 탐색 트리임
            if not node:
                return True
            
            # 현재 노드의 값이 범위를 벗어나면 유효하지 않음
            if not lower < node.val < upper:
                return False
            
            # 왼쪽 서브트리와 오른쪽 서브트리에 대해 재귀적으로 검사
            return (helper(node.left, lower, node.val) and
                    helper(node.right, node.val, upper))

        # 최초에는 무한대 범위로 호출
        return helper(root)

5. 배운 점

  • 중위 순회의 순서에 이런 의미가 있었다.
  • 최댓값, 최솟값을 매번 새로 찾지 말고 한 단계 씩 내려갈 때 가질 수 있는 값의 범위의 양쪽 경계를 업데이트 해주면 된다.