二叉樹
樹
樹 是 N(N>=0)個節點的有限集。當N=0時稱為空樹。 在任意一棵非空樹中:
- 1、有且僅有一個特定的稱為根節點
- 2、當N>1時,其餘節點可分為M個互不相交的有限集合,其中每一個集合本身又是一棵樹,並且稱為根的子樹
結點擁有的子樹樹稱為結點度
度為0的結點稱為葉結點或者終端結點
度不為0的結點稱為非終端結點或者分支結點
除根結點之外,分支節點也稱為內部結點。樹的度是樹內部各結點的度的最大值
節點間的關係
- 1、結點的子樹的根稱為該結點的孩子(Child),相應的,該結點稱為孩子的雙親(Parent)
- 2、同一個雙親的孩子之間稱為兄弟
結點的層次從根開始定義起,根為第一層,根的孩子為第二層,依次類推
為什麼要有樹結構
樹結構是一種天然的組織結構,將資料使用樹儲存的時候,出奇的高效
我們生活中有很多樹結構
二叉樹
- 1、每個結點最多有兩棵樹
- 2、左樹和右樹是有順序的,次序不能顛倒
- 3、即使樹中某個結點只有一棵樹,也要區分它是左子樹還是右子樹
- 4、左斜樹:所有的結點都只有左子斜樹的二叉樹叫左斜樹
- 5、右斜樹:所有的結點都只有右子斜樹的二叉樹叫右斜樹
- 6、滿二叉樹:所有的結點都存在左子樹和右子樹,並且所有葉子都在同一層上
- 7、完全二叉樹:所有的結點都存在左子樹和右子樹,左右兩邊不一定在同一層次
二叉樹的基本資料結構
class Node: NSObject {
var E:Int
var left:Node?
var right:Node?
}
複製程式碼
二分搜尋樹(Binary Search Tree)
二分搜尋樹也是一種二叉樹 二分搜尋樹的每個節點的值
- 大於其左子樹的所有節點的值
- 小於其右子樹的所有節點的值
我們現在就來實現一個簡單的二分搜尋樹吧
節點
//節點
class Node: NSObject {
var E:Int = 0
var left:Node?
var right:Node?
override init() {
super.init()
}
init(E:Int) {
self.E = E
self.left = nil
self.right = nil
}
}
複製程式碼
我們實現的二分搜尋樹實現以下功能
- 1、判斷樹是否為空
- 2、新增元素
- 3、檢視二分搜尋樹中是否包含某個元素
- 4、二分搜尋樹的前序遍歷、中序遍歷、後序遍歷
- 5、尋找二分搜尋樹的最小元素,刪除二分搜尋樹的最小元素
- 6、尋找二分搜尋樹的最大元素、刪除二分搜尋樹的最大元素
- 7、刪除任意的節點
我們建立一個BTS
類,裡面有兩個成員變數
var size = 0 //樹的大小
var root:Node! //根節點
複製程式碼
1、判斷樹是否為空
//判斷是否為空
func isEmpty() -> Bool{
return size == 0;
}
複製程式碼
2、新增元素
//新增元素
func add(E:Int) {
if root == nil {
size += 1
root = Node(E: E)
}else{
addNode(E: E, node: root)
}
}
// 向以node為根的二分搜尋樹中插入元素e,遞迴演算法
private
func addNode(E:Int,node:Node) {
//遞迴用法,先判斷結束語句
if node.E == E {
return
}else if((E < node.E) && (node.left == nil)){
//遍歷到最後,新增左葉子
node.left = Node(E: E)
size += 1
return
}else if((E > node.E) && (node.right == nil)){
//遍歷到最後,新增右葉子
node.right = Node(E: E)
size += 1
return
}
//遞迴呼叫
if E < node.E {
addNode(E: E, node: node.left ?? Node())
}else{
addNode(E: E, node: node.right ?? Node())
}
}
複製程式碼
思路:
- 1、首先判斷root是否為空,如果root為空的話,第一個新增的元素就是root
- 2、在root不為空的時候,我們需要迴圈遍歷,E>節點值的時候新增到右子樹,E<節點值的時候新增到左子樹,知道迴圈遍歷到最後一個節點為空的時候,新增上去
3、檢視二分搜尋樹中是否包含某個元素
//檢視二分搜尋樹中是否包含某個元素
func contain(E:Int) -> Bool {
return containNode(E: E, node: root)
}
// 看以node為根的二分搜尋樹中是否包含元素e, 遞迴演算法
private
func containNode(E:Int,node:Node?) -> Bool {
if node == nil {
return false
}
if node?.E == E{
return true
}else if (node?.E)! > E {
//左
return containNode(E: E, node: node?.left )
}else{
//右
return containNode(E: E, node: node?.right)
}
}
複製程式碼
4、前序遍歷、中序遍歷、後序遍歷
前序遍歷:規則是若二叉樹為空,則空操作返回,否則先訪問根節點,然後前續遍歷左子樹,然後再前序遍歷右子樹
//二分搜尋樹的前序遍歷
func preOrder() {
preOrder(node: root)
}
// 前序遍歷以node為根的二分搜尋樹, 遞迴演算法
private
func preOrder(node:Node?) {
//結束條件
if node == nil {
return
}
print(node?.E ?? "nil")
preOrder(node: node?.left)
preOrder(node: node?.right)
}
複製程式碼
中序遍歷:規則是若樹為空,則空操作返回,否則從根節點開始(注意並不是先訪問根節點),中序遍歷根節點是左子樹,然後是訪問根節點,最後中序遍歷右子樹
// 二分搜尋樹的中序遍歷
func inOrder() {
inOrder(node: root)
}
// 中序遍歷以node為根的二分搜尋樹, 遞迴演算法
private
func inOrder(node:Node?) {
//結束條件
if node == nil {
return
}
inOrder(node: node?.left)
print(node?.E ?? "nil")
inOrder(node: node?.right)
}
複製程式碼
後序遍歷:規則是若樹為空,則空操作返回,否則從左到右先子葉後節點的方式遍歷訪問左右子樹,最後是訪問根節點
// 二分搜尋樹的後序遍歷
func postOrder() {
postOrder(node: root)
}
// 後序遍歷以node為根的二分搜尋樹, 遞迴演算法
private
func postOrder(node:Node?) {
//結束條件
if node == nil {
return
}
inOrder(node: node?.left)
inOrder(node: node?.right)
print(node?.E ?? "nil")
}
複製程式碼
5、尋找二分搜尋樹的最小元素,刪除二分搜尋樹的最小元素
尋找二分搜尋樹的最小元素
// 從二分搜尋樹中刪除最小值所在節點, 返回最小值
func removeMin() -> Int {
if size == 0 {
return 10086
}
root = removeMin(node: root)
return minimum()
}
return minimum(node: root).E
}
//查詢最小資料 遞迴演算法
private
func minimum(node:Node?) -> Node{
if node?.left == nil {
return node ?? Node()
}
return minimum(node: node?.left)
}
複製程式碼
思路:
- 1、根據二分搜尋樹的定義我們知道,最小值一定在最左側子節點上面,我們一直往最左側子節點遍歷就可以了
刪除二分搜尋樹的最小元素
// 從二分搜尋樹中刪除最小值所在節點, 返回最小值
func removeMin() -> Int {
if size == 0 {
return 10086
}
root = removeMin(node: root)
return minimum()
}
// 刪除掉以node為根的二分搜尋樹中的最小節點
// 返回刪除節點後新的二分搜尋樹的根
private
func removeMin(node:Node?) -> Node? {
if node?.left == nil {
size -= 1
let rightNode = node?.right
node?.right = nil
return rightNode
}
node?.left = removeMin(node: node?.left)
return node
}
複製程式碼
思考: 在刪除最小元素的時候我們需要考慮兩種情況
- 1、最小元素沒有子樹了
- 2、最小元素有右子樹
針對上面這種,我們程式碼可以這樣寫
if node?.left == nil {
size -= 1
let rightNode = node?.right
node?.right = nil
return rightNode
}
複製程式碼
在最小元素有有右子樹的時候,我們需要把這個右子樹佔據我們刪除的那個子樹的位置
6、尋找二分搜尋樹的最大元素、刪除二分搜尋樹的最大元素
尋找二分搜尋樹的最大元素
// 尋找二分搜尋樹的最大元素
func maximum() -> Int {
if size == 0 {
return 10086
}
return maximum(node: root)
}
//查詢最大資料 遞迴演算法
private
func maximum(node:Node?) -> Int {
if node?.right == nil {
return node?.E ?? 10086
}
return maximum(node: node?.right)
}
複製程式碼
刪除二分搜尋樹的最大元素
// 從二分搜尋樹中刪除最大值所在節點, 返回最大值
func removeMax() -> Int {
if size == 0 {
return 10086
}
root = removeMax(node: root)
return maximum()
}
// 刪除掉以node為根的二分搜尋樹中的最大節點
// 返回刪除節點後新的二分搜尋樹的根
private
func removeMax(node:Node?) -> Node? {
if node?.right == nil {
size -= 1
let leftNode = node?.left
node?.left = nil
return leftNode
}
node?.right = removeMax(node: node?.right)
return node
}
複製程式碼
7、刪除任意的節點
// 刪除為E的節點
func remove(E:Int) ->Node{
root = remove(E: E, node: root)
return root
}
// 刪除掉以node為根的二分搜尋樹中值為e的節點, 遞迴演算法
// 返回刪除節點後新的二分搜尋樹的根
private
func remove(E:Int,node:Node?) -> Node? {
if node == nil {
return nil
}
if E < (node?.E)! {
node?.left = remove(E: E, node: node?.left)
return node
}else if E > (node?.E)! {
node?.right = remove(E: E, node: node?.right)
return node
}else{
//找到了
// 待刪除節點左子樹為空的情況
if node?.left == nil{
let rightNode = node?.right
node?.right = nil
size -= 1
return rightNode
}
// 待刪除節點右子樹為空的情況
if node?.right == nil{
let leftNode = node?.left
node?.left = nil
size -= 1
return leftNode
}
// 待刪除節點左右子樹均不為空的情況
// 找到比待刪除節點大的最小節點, 即待刪除節點右子樹的最小節點
// 用這個節點頂替待刪除節點的位置
let successor = minimum(node: node?.right)
successor.right = removeMin(node: node?.right)
successor.left = node?.left
node?.right = nil
node?.left = nil
return successor
}
}
複製程式碼
思考:
- 1、在找到待刪除節點以後,左節點為空時,這個時候跟刪除最小值類似
// 待刪除節點左子樹為空的情況
if node?.left == nil{
let rightNode = node?.right
node?.right = nil
size -= 1
return rightNode
}
複製程式碼
- 2、在找到待刪除節點以後,右節點為空時,這個時候跟刪除最大值類似
// 待刪除節點右子樹為空的情況
if node?.right == nil{
let leftNode = node?.left
node?.left = nil
size -= 1
return leftNode
}
複製程式碼
- 3、待刪除節點左右子樹均不為空的情況 找到比待刪除節點大的最小節點, 即待刪除節點右子樹的最小節點;用這個節點頂替待刪除節點的位置