1+ // https://contest.yandex.ru/contest/24810/run-report/160624033/
2+
3+ // <template>
4+ class Node {
5+ private int value ;
6+ private Node left ;
7+ private Node right ;
8+
9+ Node (Node left , Node right , int value ) {
10+ this .left = left ;
11+ this .right = right ;
12+ this .value = value ;
13+ }
14+
15+ public int getValue () {
16+ return value ;
17+ }
18+
19+ public Node getRight () {
20+ return right ;
21+ }
22+
23+ public void setRight (Node right ) {
24+ this .right = right ;
25+ }
26+
27+ public Node getLeft () {
28+ return left ;
29+ }
30+
31+ public void setLeft (Node left ) {
32+ this .left = left ;
33+ }
34+
35+ public void setValue (int value ) {
36+ this .value = value ;
37+ }
38+ }
39+ // <template>
40+
41+ public class Solution {
42+
43+ /*
44+ * Принцип работы алгоритма:
45+ * Используем свойство бинарного дерева поиска.
46+ * Если удаляемый ключ меньше значения в текущей вершине,
47+ * продолжаем поиск в левом поддереве.
48+ * Если больше — в правом.
49+ * Когда вершина с нужным ключом найдена, возможны три случая:
50+ *
51+ * 1) У вершины нет левого ребёнка.
52+ * Тогда на её место должно встать правое поддерево.
53+ *
54+ * 2) У вершины нет правого ребёнка.
55+ * Тогда на её место должно встать левое поддерево.
56+ *
57+ * 3) У вершины есть оба ребёнка.
58+ * Тогда берём максимальный элемент из левого поддерева
59+ * (это предшественник текущей вершины в порядке BST),
60+ * записываем его значение в текущую вершину,
61+ * а затем удаляем этот элемент из левого поддерева.
62+ *
63+ * Почему алгоритм корректен:
64+ * 1) По свойству BST искомый ключ может находиться только
65+ * в одном из двух поддеревьев, поэтому рекурсивный спуск
66+ * идёт ровно по нужному пути.
67+ *
68+ * 2) Если у удаляемой вершины не более одного ребёнка,
69+ * замена вершины её единственным поддеревом
70+ * сохраняет порядок ключей в дереве.
71+ *
72+ * 3) Если у вершины два ребёнка, максимум левого поддерева:
73+ * - меньше значения любой вершины из правого поддерева;
74+ * - не меньше всех остальных значений из левого поддерева.
75+ * Поэтому после замены текущего ключа на этот максимум
76+ * свойство BST сохраняется.
77+ *
78+ * 4) После этого мы удаляем из левого поддерева вершину,
79+ * значение которой перенесли вверх. Так как ключи уникальны,
80+ * удаляется ровно одна нужная вершина.
81+ *
82+ * Временная сложность: O(h), где h — высота дерева,
83+ * потому что мы спускаемся только по одному пути вниз.
84+ *
85+ * Пространственная сложность: O(h) из-за стека рекурсии.
86+ */
87+
88+ public static Node remove (Node root , int key ) {
89+ if (root == null ) {
90+ return null ;
91+ }
92+
93+ if (key < root .getValue ()) {
94+ root .setLeft (remove (root .getLeft (), key ));
95+ return root ;
96+ }
97+
98+ if (key > root .getValue ()) {
99+ root .setRight (remove (root .getRight (), key ));
100+ return root ;
101+ }
102+
103+ if (root .getLeft () == null ) {
104+ return root .getRight ();
105+ }
106+
107+ if (root .getRight () == null ) {
108+ return root .getLeft ();
109+ }
110+
111+ Node predecessor = findMax (root .getLeft ());
112+ root .setValue (predecessor .getValue ());
113+ root .setLeft (remove (root .getLeft (), predecessor .getValue ()));
114+ return root ;
115+ }
116+
117+ private static Node findMax (Node node ) {
118+ while (node .getRight () != null ) {
119+ node = node .getRight ();
120+ }
121+ return node ;
122+ }
123+
124+ private static void test () {
125+ Node node1 = new Node (null , null , 2 );
126+ Node node2 = new Node (node1 , null , 3 );
127+ Node node3 = new Node (null , node2 , 1 );
128+ Node node4 = new Node (null , null , 6 );
129+ Node node5 = new Node (node4 , null , 8 );
130+ Node node6 = new Node (node5 , null , 10 );
131+ Node node7 = new Node (node3 , node6 , 5 );
132+
133+ Node newHead = remove (node7 , 10 );
134+ assert newHead .getValue () == 5 ;
135+ assert newHead .getRight () == node5 ;
136+ assert newHead .getRight ().getValue () == 8 ;
137+
138+ Node root = new Node (
139+ new Node (new Node (null , null , 2 ), new Node (null , null , 4 ), 3 ),
140+ new Node (new Node (null , null , 7 ), new Node (null , null , 9 ), 8 ),
141+ 5
142+ );
143+ Node result = remove (root , 5 );
144+ assert result .getValue () == 4 ;
145+ assert result .getLeft ().getValue () == 3 ;
146+ assert result .getRight ().getValue () == 8 ;
147+
148+ Node single = new Node (null , null , 1 );
149+ assert remove (single , 1 ) == null ;
150+
151+ Node unchanged = new Node (null , null , 1 );
152+ assert remove (unchanged , 2 ) == unchanged ;
153+ }
154+ }
0 commit comments