diff --git a/tree-bst/98.md b/tree-bst/98.md index e69de29..21ee068 100644 --- a/tree-bst/98.md +++ b/tree-bst/98.md @@ -0,0 +1,330 @@ +- [98. Validate Binary Search Tree](https://leetcode.com/problems/validate-binary-search-tree/description/) +- 方針 + - Queue を用いる + - 初期値の `root.val` は `Long.MIN_VALUE ~ Long.MAX_VALUE` の間に収まっていればOK + - 同様に、左の子については、最大値が root.val になればよく、左の子の値判定は、`Long.MIN_VALUE ~ root.val` に収まっていればBSTとして適切 + - 同様に、右の子については、最小値が root.val になればよく、右の子の値判定は、`root.val ~ Long.MAX_VALUE` に収まっていればBSTとして適切 + - 上記を繰り返すだけではなく、一つ下のノードに落ちた時に左に落ちたのであれば、最大値を更新。右に落ちたのであれば最小値を更新して、許容される値の区間を更新し続ける必要がある + - 一般化すると、左に行く時は最大値を現在のノードの値で更新、右に行く時は現在のノードの値で最小値を更新すれば良い +- 実装時間: 20分 +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + public boolean isValidBST(TreeNode root) { + if (root == null) { + return false; + } + + Deque nodes = new ArrayDeque<>(); + nodes.addLast(new NodeWithRange(root, Long.MIN_VALUE, Long.MAX_VALUE)); + + while (!nodes.isEmpty()) { + NodeWithRange nodeWithRange = nodes.pollFirst(); + TreeNode node = nodeWithRange.node; + long min = nodeWithRange.min; + long max = nodeWithRange.max; + + if (node.val <= min || node.val >= max) { + return false; + } + + if (node.left != null) { + nodes.addLast(new NodeWithRange(node.left, min, node.val)); + } + + if (node.right != null) { + nodes.addLast(new NodeWithRange(node.right, node.val, max)); + } + } + + return true; + } + + private record NodeWithRange(TreeNode node, long min, long max){} +} +``` +- うまくいかず、40分程度格闘したコード +- 何がいけなかったのか? + - Queue に対するノードの追加と invalid の判定を同時に行おうとした点 +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + public boolean isValidBST(TreeNode root) { + if (root == null) { + return false; + } + + Deque nodes = new ArrayDeque<>(); + nodes.addLast(new NodeWithRange(root, Long.MIN_VALUE, Long.MAX_VALUE)); + while (!nodes.isEmpty()) { + NodeWithRange nodeWithRange = nodes.pollFirst(); + TreeNode node = nodeWithRange.node; + long min = nodeWithRange.min; + long max = nodeWithRange.max; + + if (node.left != null) { + if (node.left.val >= node.val || node.left.val <= min) { + return false; + } + nodes.addLast(new NodeWithRange(node.left, min, node.val)); + } + + if (node.right != null) { + if (node.right.val <= node.val || node.right.val >= max) { + return false; + } + nodes.addLast(new NodeWithRange(node.right, node.val, max)); + } + } + + return true; + } + + private record NodeWithRange(TreeNode node, long min, long max){} +} +``` + +- 時間を空けて解き直し、もっとシンプルにできる +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + public boolean isValidBST(TreeNode root) { + if (root == null) { + return false; + } + + Deque nodes = new ArrayDeque<>(); + nodes.addLast(new NodeWithRange(root, Long.MIN_VALUE, Long.MAX_VALUE)); + + while (!nodes.isEmpty()) { + NodeWithRange nodeWithRange = nodes.pollFirst(); + TreeNode node = nodeWithRange.node; + long min = nodeWithRange.min; + long max = nodeWithRange.max; + + if (node.val <= min || node.val >= max) { + return false; + } + + if (node.left != null) { + // 最大値更新 + nodes.addLast(new NodeWithRange(node.left, min, node.val)); + } + + if (node.right != null) { + // 最小値更新 + nodes.addLast(new NodeWithRange(node.right, node.val, max)); + } + } + + return true; + } + + private record NodeWithRange(TreeNode node, long min, long max){} +} +``` + +- 前回の課題をまとめる + - [103. Binary Tree Zigzag Level Order Traversal #27 line 145](https://github.com/hiroki-horiguchi-dev/leetcode/pull/27/changes)で自分に出した課題 + + ```terminaloutput + Iterable + └── Collection + ├── List + │ ├── ArrayList + │ └── LinkedList + ├── Set + │ ├── HashSet + │ └── TreeSet + └── Queue + ├── LinkedList (List も実装してる) + └── ArrayDeque + ``` + + - [Iterable](https://github.com/openjdk/jdk/blob/a13dd293a811a6ba829696e68cd2150de2cb2f17/src/java.base/share/classes/java/lang/Iterable.java) + - Iterable は、Javaの Collection 自体が実装しているので、Collectionを継承ないし実装しているクラス(List, Queue, Set, Map) は全て for を使うことができる + - CSZAP の チューリング完全あたりで出てきた内容っぽいので入門コンピュータ科学12章をつまみ読んだ + ```java + public interface Iterable { + Iterator iterator(); + + default void forEach(Consumer action) { + Objects.requireNonNull(action); + for (T t : this) { + action.accept(t); + } + } + default Spliterator spliterator() { + return Spliterators.spliteratorUnknownSize(iterator(), 0); + } + } + ``` + - [Collections](https://github.com/openjdk/jdk/blob/a13dd293a811a6ba829696e68cd2150de2cb2f17/src/java.base/share/classes/java/util/Collection.java) + ```java + package java.util; + + import java.util.function.IntFunction; + import java.util.function.Predicate; + import java.util.stream.Stream; + import java.util.stream.StreamSupport; + + public interface Collection extends Iterable { + + int size(); + + boolean isEmpty(); + + boolean contains(Object o); + + Iterator iterator(); + + Object[] toArray(); + + T[] toArray(T[] a); + + default T[] toArray(IntFunction generator) { + return toArray(generator.apply(0)); + } + + boolean add(E e); + + boolean remove(Object o); + + boolean containsAll(Collection c); + + boolean addAll(Collection c); + + boolean removeAll(Collection c); + + default boolean removeIf(Predicate filter) { + Objects.requireNonNull(filter); + boolean removed = false; + final Iterator each = iterator(); + while (each.hasNext()) { + if (filter.test(each.next())) { + each.remove(); + removed = true; + } + } + return removed; + } + + boolean retainAll(Collection c); + + void clear(); + + boolean equals(Object o); + + int hashCode(); + + @Override + default Spliterator spliterator() { + return Spliterators.spliterator(this, 0); + } + + default Stream stream() { + return StreamSupport.stream(spliterator(), false); + } + + default Stream parallelStream() { + return StreamSupport.stream(spliterator(), true); + } + } + ``` + - 抽象メソッド(実装必須) + - 情報取得: `size(), isEmpty(), contains()` + - 走査: `iterator()` + - 変換: `toArray(), toArray(T[])` + - 追加/削除: `add(), remove(), addAll(), removeAll(), retainAll(), clear()` + - 存在確認: `containsAll()` + - 比較: `equals(), hashCode()` + - 実装しているクラスのまとめ + ```terminaloutput + Collection [interface] + ├── SequencedCollection [interface] (Java 21〜) + │ ├── List [interface] + │ │ ├── ArrayList + │ │ ├── LinkedList (※Dequeも実装) + │ │ ├── Vector + │ │ │ └── Stack + │ │ └── CopyOnWriteArrayList + │ ├── SequencedSet [interface] (Java 21〜 / Setを継承) + │ │ ├── LinkedHashSet (※HashSetの子クラス) + │ │ └── TreeSet (※NavigableSet経由で継承) + │ └── Deque [interface] (※Queueも継承) + │ ├── ArrayDeque + │ ├── LinkedList (※Listも実装) + │ ├── ConcurrentLinkedDeque + │ └── BlockingDeque [interface] + │ └── LinkedBlockingDeque + │ + ├── Set [interface] + │ ├── HashSet + │ │ └── LinkedHashSet (※SequencedSetも実装) + │ ├── EnumSet + │ ├── TreeSet (※NavigableSet経由) + │ └── CopyOnWriteArraySet + │ + ├── Queue [interface] + │ ├── PriorityQueue + │ ├── ConcurrentLinkedQueue + │ ├── Deque [interface] (※上記SequencedCollectionの枠内へ移動) + │ └── BlockingQueue [interface] + │ ├── ArrayBlockingQueue + │ ├── LinkedBlockingQueue + │ ├── PriorityBlockingQueue + │ ├── SynchronousQueue + │ └── LinkedTransferQueue + │ + └── AbstractCollection [abstract] + ├── AbstractList [abstract] ──> ArrayList, Vector などの親 + │ └── AbstractSequentialList [abstract] ──> LinkedList の親 + ├── AbstractSet [abstract] ──> HashSet, TreeSet などの親 + └── AbstractQueue [abstract] ──> PriorityQueue などの親 + ``` + - Map は実装していないので注意。Map を entrySet で回せているように見えるのは Set が Iterator を実装しているからで Map が Iterator を実装しているからではない + - 面倒くさくなってきたので一旦ここで push する + - あとは気が向けば戻ってきて続きをやる + \ No newline at end of file