一、二叉树的基本概念
1、二叉树:在二叉树中,每个节点最多有两个节点,一般被称为左子节点和右子节点,并且二叉树的子数有左右之分,其次序不能任意颠倒
2、二叉树节点的实现
public class GeneralTreeNode{
public var val : String
public var left : GeneralTreeNode?
public var right : GeneralTreeNode?
public init(_ val : String){
self.val = val
}
}
3、树的深度:在二叉树中,节点的层次从根开始,根为第一层,节点的最大层次为树的深度
深度算法的实现:
//计算树的最大深度
private func maxDepth(_ root : GeneralTreeNode?)->Int{
guard let root = root else{
return 0
}
return max(maxDepth(root.left), maxDepth(root.right))+1
}
func maxDepth()->Int{
return maxDepth(root)
}
4、二叉树的特性
- 特性1:在二叉树的第i层上至多有2^(i-1)(i >= 1)个节点。
- 如果层数是从零开始数的话,那么低i层上的节点数就是2^i,因为二叉树层与层之间的节点数是以2的指数幂进行增长的。如果根节点算是第0层的话,那么第n层的节点数就是2^n次幂。
- 特性2:深度为k的二叉树至多有2^k-1(k>=1)个节点。
- 由数学上的递加公式就可以很容易的推出来。由特性1易知每层最多有多少个节点,那么深度为k的话,说明一共有k层,那么共有节点数为:2^0 + 2^1 + 2^2 + 2^(k-1) = 2^k - 1。
- 特性3:二叉树的叶子节点数为n0, 度为2的节点数为n2, 那么n0 = n2 + 1。
- 推出n0 = n2 + 1这个公式并不难。我们假设叶子节点,也就是度数为0的节点的个数为n0, 度数为1的节点为n1, 度数为2的节点n2。那么二叉树的节点总数 n = n0 + n1 + n2。因为除了根节点外其余的节点入度都为1,所以二叉树的度数为n-1,当然度的个数可以使用出度来算,即为2*n2+n1,所以n-1=2*n2+n1。以n=n0+n1+n2与n-1=2*n2+n1这两个公式我们很容易的推出n0 = n2 + 1。
- 特性4:具有n个结点的完全二叉树的深度为log2n + 1 (向下取整,比如3.5,就取3)。
- 基于完全二叉树的特点,我们假设完全二叉树的深度为k, 那么二叉树的结点个数的范围为2(k-1)-1 <= n <= 2k-1。由这个表达式我们很容易推出特性4。
5、先序创建二叉树
用数组存储二叉树的节点,递归创建二叉树的根节点、左子树、右子树
class GeneralTree{
private var root : GeneralTreeNode?
fileprivate var items : Array<String>
fileprivate var index = -1
init(_ items : Array<String>) {
self.items = items
self.root = self.createTree()
}
//以先序二叉树创建二叉树
fileprivate func createTree()->GeneralTreeNode?{
self.index = self.index+1
if index < self.items.count && index >= 0{
let item = self.items[index]
if item == ""{
return nil
}else{
let root = GeneralTreeNode(item)
root.left = createTree()
root.right = createTree()
return root
}
}
return nil
}
}
二、二叉查找树
1、二叉查找树是一种特殊的二叉树,其特点是左子树节点的值都小于跟节点的值,右子树节点的值都大于根节点的值
2、判定一棵二叉树是否是二叉查找树,可以根据二叉查找树的定义判断
具体实现:
注意:1)二叉树本身是由递归定义的,从原理上说,所有二叉树的题目都可以用递归来解决
2)二叉树类似的题目一般容易涉及左子树和右子树的问题,所以在写helper函数时需要考虑到左右子树的问题
3)不要忘记处理节点为nil的情况,尤其要注意根节点为nil的情况
//判断一棵树是否是二叉查找树
func isValidBST(_ root : GeneralTreeNode?)->Bool{
return _helper(root, nil, nil)
}
private func _helper(_ node : GeneralTreeNode?, _ min : String?, _ max : String?)->Bool{
guard let node = node else{
return true
}
//所有的右子节点>root
if let min = min, node.val <= min {
return false
}
//所有的左子节点<root
if let max = max, node.val >= max {
return false
}
return _helper(node.left, min,node.val)&&_helper(node.right, node.val, max)
}
三、二叉树的遍历
二叉树的遍历主要包括:前序、中序、后序遍历,以及层次遍历即广度优先遍历
1、前序遍历:根节点->左子树->右子树
//前序遍历
private func preorderTraversal(_ root : GeneralTreeNode?){
//遍历跟节点
guard let root = root else {
print("空", separator: "", terminator: " ")
return
}
print(root.val, separator: "", terminator: " ")
preorderTraversal(root.left)
preorderTraversal(root.right)
}
func preOrder(){
print("先序遍历")
preorderTraversal(root)
print("\n")
}
2、中序遍历:左子树->根节点->右子树
//中序遍历
private func inorderTraversal(_ root : GeneralTreeNode?){
guard let root = root else {
print("空", separator: "", terminator: " ")
return
}
inorderTraversal(root.left)
print(root.val, separator: "", terminator: " ")
inorderTraversal(root.right)
}
func inOrder(){
print("中序遍历")
inorderTraversal(root)
print("\n")
}
3、后序遍历:左子树->右子树->根节点
//后序遍历
private func postOrderTraversal(_ root : GeneralTreeNode?){
guard let root = root else {
print("空", separator: "", terminator: " ")
return
}
postOrderTraversal(root.left)
postOrderTraversal(root.right)
print(root.val, separator: "", terminator: " ")
}
func postOrder(){
print("后序遍历")
postOrderTraversal(root)
print("\n")
}
4、层次遍历(广度优先遍历)
层次遍历 其实就是以二叉树的根节点为起始位置的广度搜索BFS
//层级遍历--队列实现
private func levelOrder(_ root : GeneralTreeNode?){
var res = [[String]]()
//用数组实现队列
var queue = [GeneralTreeNode]()
if let root = root {
queue.append(root)
}
while queue.count > 0 {
var size = queue.count
var level = [String]()
for _ in 0..<size{
let node = queue.removeFirst()
level.append(node.val)
if let left = node.left {
queue.append(left)
}
if let right = node.right{
queue.append(right)
}
}
res.append(level)
}
print("res \(res)")
}
func levelOrder(){
print("层级遍历")
levelOrder(root)
print("\n")
}
5、示例
let items: Array<String> = ["A", "B", "D", "", "", "E", "", "", "C", "","F", "", ""]
let generalBinaryTree: GeneralTree = GeneralTree(items)
//二叉树的遍历
generalBinaryTree.preOrder()
generalBinaryTree.inOrder()
generalBinaryTree.postOrder()
generalBinaryTree.levelOrder()
运行结果
先序遍历
A B D 空 空 E 空 空 C 空 F 空 空
中序遍历
空 D 空 B 空 E 空 A 空 C 空 F 空
后序遍历
空 空 D 空 空 E B 空 空 空 F C A
层级遍历
res [["A"], ["B", "C"], ["D", "E", "F"]]
四、二叉树的线索化
所谓 线索化 其实就是利用二叉树的空节点来将二叉树转化为链表的结构,主要针对中序遍历
在中序遍历中, 线索化 就是将空的左子树的指针指向中序遍历结果的前驱,空的右子树指针指向中序遍历该节点的后继
//二叉树线索化
public class BinaryTreeNode{
public var val : String
public var left : BinaryTreeNode?
public var right : BinaryTreeNode?
//true 指向左子树,false 指向前驱
public var leftTag : Bool = true
//true 指向右子树,false 指向后继
public var rightTag : Bool = true
public init(_ val : String){
self.val = val
}
}
class BinaryTree{
private var root : BinaryTreeNode?
fileprivate var items : Array<String>
fileprivate var index = -1
fileprivate var preNode : BinaryTreeNode?
fileprivate var headNode : BinaryTreeNode?
init(_ items : Array<String>) {
self.items = items
self.root = self.createTree()
self.headNode = BinaryTreeNode("")
self.headNode?.left = self.root
self.headNode?.leftTag = true
self.preNode = headNode
}
//以先序二叉树创建二叉树
fileprivate func createTree()->BinaryTreeNode?{
self.index = self.index+1
if index < self.items.count && index >= 0{
let item = self.items[index]
if item == ""{
return nil
}else{
let root = BinaryTreeNode(item)
root.left = createTree()
root.right = createTree()
return root
}
}
return nil
}
//前序遍历
fileprivate func preorderTraversal(_ root : BinaryTreeNode?){
//遍历跟节点
guard let root = root else {
return
}
print(root.val, separator: "", terminator: " ")
if root.leftTag {
preorderTraversal(root.left)
}
if root.rightTag {
preorderTraversal(root.right)
}
}
func preOrder(){
print("先序遍历")
preorderTraversal(root)
print("\n")
}
//中序遍历,该线索化,是将结果生成一个链表
private func inorderTraversal(_ root : BinaryTreeNode?){
if root != nil{
inorderTraversal(root?.left)
//如果节点的左节点为nil,那么将该节点指向中序遍历的前驱
if root?.left == nil {
root?.leftTag = false
root?.left = preNode
}
//如果该节点的中序遍历的前驱的右节点为nil,那么将该前驱节点的右节点指向该点进行关联
if preNode?.right == nil{
preNode?.rightTag = false
preNode?.right = root
}
preNode = root
inorderTraversal(root?.right)
}
}
func inOrder(){
inorderTraversal(root)
}
func displayBinaryTree(){
print("遍历线索化二叉树\n")
var cursor = self.headNode?.right
while cursor != nil {
print((cursor?.val)!, separator: "", terminator: " -> ")
cursor = cursor?.right
}
print("end\n")
}
}
示例:
let items: Array<String> = ["A", "B", "D", "", "", "E", "", "", "C", "","F", "", ""]
//二叉树线索化
let binaryTree : BinaryTree = BinaryTree(items)
binaryTree.inOrder()
binaryTree.displayBinaryTree()
binaryTree.preOrder()
运行结果:
遍历线索化二叉树
D -> B -> E -> A -> C -> F -> end
先序遍历
A B D E C F
GeneralTree-->二叉树的创建与遍历
BinaryTree-->二叉树线索化