Skip to content

Commit 64705a2

Browse files
author
Ubuntu
committed
rename README.md to README_CHS.md
1 parent 67de954 commit 64705a2

File tree

2 files changed

+129
-0
lines changed

2 files changed

+129
-0
lines changed
File renamed without changes.

tree/binaryTree/README_CHS.md

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# 关于BST的非递归非栈的前中后序遍历
2+
## 网上很多都不是真正的非递归非栈遍历
3+
----------------------------
4+
完整的代码见[binarySearchTree.go](https://github.com/shady831213/algorithms/blob/master/tree/binaryTree/binarySearchTree.go)
5+
test见[binarySearchTree_test.go](https://github.com/shady831213/algorithms/blob/master/tree/binaryTree/binarySearchTree_test.go)
6+
test 命令:
7+
```
8+
go test
9+
```
10+
### 中序遍历
11+
最简单,就是从最小节点循环找后继节点
12+
```go
13+
func (t *BstIterative) InOrderWalk(node interface{}, callback func(interface{}) (bool)) (bool) {
14+
n := node.(*BstElement)
15+
for curNode := t.Min(n).(*BstElement); curNode != nil; {
16+
stop := callback(curNode)
17+
if stop {
18+
return true
19+
}
20+
curNode = t.Successor(curNode).(*BstElement)
21+
}
22+
return false
23+
}
24+
```
25+
-----
26+
### 后续遍历
27+
起始节点一定是最左边最底层的叶子节点,分两种情况:
28+
- 最小节点
29+
- 最小节点有右孩子,则沿着右边找到最小节点,如果最小节点还有右孩子,一直找下去
30+
确定起始节点后,如果节点是左孩子,则按找起始节点的方法,找到其父亲右面的最左边最底层的叶子节点;如果是右孩子,则返回父亲节点。如此一直循环
31+
```go
32+
func (t *BstIterative) PostOrderWalk(node interface{}, callback func(interface{}) (bool)) (bool) {
33+
n := node.(*BstElement)
34+
35+
leftistNode := func(curNode *BstElement) (nextNode *BstElement) {
36+
nextNode = curNode
37+
for nextNode.right != nil {
38+
nextNode = t.Min(nextNode.right).(*BstElement)
39+
}
40+
return
41+
}
42+
43+
for curNode := leftistNode(t.Min(n).(*BstElement)); curNode != n; {
44+
stop := callback(curNode)
45+
if stop {
46+
return true
47+
}
48+
parentNode := curNode.parent
49+
if curNode == parentNode.left {
50+
curNode = leftistNode(parentNode)
51+
} else {
52+
curNode = parentNode
53+
}
54+
55+
}
56+
return callback(n)
57+
}
58+
```
59+
60+
### 前序遍历
61+
最麻烦。。。
62+
首先其实节点一定是根节点
63+
整个遍历过程分两个方向,向下和向上
64+
一开始向下遍历,如果有左孩子就一直往左走,如果没有左孩子有右孩子就往右,总之是有孩子节点就一直往下,左面优先级高,一直走到叶子节点,方向转成向上
65+
整个向上的过程分成两种情况,不分叶子节点还是非叶子节点:
66+
- 如果节点是父节点的左节点,就向上遍历直到:
67+
- 向上的方向改变,即一直是左节点,左节点。。。突然一个节点是父节点的右节点,这时候停
68+
- 或者发现有一个节点有右孩子
69+
- 如果节点是父节点的右节点,把父节点的右孩子置为空,找到父节点的后继节点,然后恢复父节点的右孩子,这个后继节点要么是根节点,要么是一个左节点
70+
向上转为向下的情况有两种:
71+
- 节点为根节点
72+
- 节点有右孩子
73+
此时,方向转为向下,并指向其右孩子,如果右孩子为空,即循环停止。
74+
75+
```go
76+
func (t *BstIterative) PreOrderWalk(node interface{}, callback func(interface{}) (bool)) (bool) {
77+
root := node.(*BstElement)
78+
79+
goDown := func(curNode *BstElement) (*BstElement, bool) {
80+
if curNode.left != nil {
81+
return curNode.left, true
82+
} else if curNode.right != nil {
83+
return curNode.right, true
84+
}
85+
return curNode, false
86+
}
87+
88+
goUp := func(curNode *BstElement) (*BstElement, bool) {
89+
if curNode == root || curNode.right != nil {
90+
return curNode.right, true
91+
} else if curNode == curNode.parent.left {
92+
for curNode == curNode.parent.left {
93+
curNode = curNode.parent
94+
if curNode == root || curNode.right != nil {
95+
return curNode.right, true
96+
}
97+
}
98+
} else {
99+
parentNode := curNode.parent
100+
parentRightNode := parentNode.right
101+
parentNode.right = nil
102+
curNode = t.Successor(parentNode).(*BstElement)
103+
parentNode.right = parentRightNode
104+
}
105+
return curNode, false
106+
}
107+
108+
down := true
109+
for curNode := root; curNode != nil; {
110+
if down {
111+
stop := callback(curNode)
112+
if stop {
113+
return true
114+
}
115+
curNode, down = goDown(curNode)
116+
} else {
117+
curNode, down = goUp(curNode)
118+
}
119+
}
120+
return false
121+
}
122+
```
123+
124+
--------
125+
### 时间复杂度
126+
O(n),n个节点的bst有n-1条边,这三个遍历每条边最多走两次,一次定位一次回溯,所以复杂度为O(2(n-1))=O(n)
127+
### 空间复杂度
128+
真正的非递归非栈,O(1)
129+

0 commit comments

Comments
 (0)