最近在幫公司校招~~ 因此來整理一些數據結構方面的知識,這些知識呢,光看一遍理解仍是很淺的,看過跟動手作過一遍的同窗仍是很容易分辨的喲~html
一直以爲數據結構跟算法,就比如金庸小說裏的《九陽神功》,學會九陽神功後,有了內功基礎,再去學習其餘武功,速度就有質的提高node
內容大概包含這些,會分多篇文章來整理:算法
- 二叉搜索樹
- 平衡二叉樹(AVL)
- 二叉堆
- 堆排序
- 四叉樹
- 八叉樹
- 圖,深度優先DFS、廣度優先BFS
- 最短路徑
二叉樹,也就是每一個節點最多有兩個孩子的樹。多用於搜索,查找,還有能夠用來求最短編碼的哈弗曼樹,也稱爲最優二叉樹。數據結構
如圖,樹的每一個有孩子的節點都知足:左節點的值<根節點的值<右節點的值條件的樹,稱爲二叉排序樹,也叫二叉搜索樹。
ide
若是對這個樹進行中序遍歷,就能獲得一個排序的數列,很是簡單,下面貼出插入操做跟遍歷的代碼
插入操做學習
public void Add(BinaryTree node) { if (node.Value < Value) { if (this.Left != null) { this.Left.Add(node); } else { this.Left = node; } } else { if (this.Right != null) { this.Right.Add(node); } else { this.Right = node; } } }
中序遍歷輸出排序列表this
public void InOrder(List<int> list) { if (Left != null) { Left.InOrder(list); } list.Add(this.Value); if (Right != null) { Right.InOrder(list); } }
可是二叉排序樹極端的狀況,效率會變成鏈表線性結構,這樣查找起來時間複雜度會變成O(n),就失去了樹形結構的意義,如圖:
編碼
這時就要引出咱們的另一種二叉樹樹結構了3d
平衡二叉樹(AVL)簡單來講就是插入的時候,要保證子節點的平衡,別老往一邊一直插入下去,那樣又成了鏈表效率了code
首先來搞懂這個幾個定義
平衡因子:即左子樹的高度減去右子樹的高度
平衡二叉樹上全部節點的平衡因子都必須爲:-一、0和1。不然該二叉樹就不是平衡二叉樹
以下圖,圖左邊是一顆平衡二叉樹,圖右根節點平衡因子爲-2,則不是平衡二叉樹
如何保持樹的平衡
每當插入一個節點的時候,都檢查此次插入是否會破壞平衡性,如果,則找出最小不平衡子樹,在保持二叉排序樹的前提下,進行相應旋轉,使之成爲新的平衡子樹。
一般會有四種旋轉狀況:
單向右旋平衡處理
也有地方稱爲Left Left旋轉,是否是以爲很奇怪,一下左,一下右邊的,它估計是想把你轉暈,好套出你的花唄密碼。
那麼究竟是什麼意思呢,請看下圖
這棵樹有三個節點:6,4,2
咱們把節點2當成是最新插入進來的節點,因爲這個節點2的插入,致使節點6的平衡因子變成了2,不符合-一、0、1的規定,破壞了平衡性,因此咱們須要對節點6進行右旋轉,而節點2又是節點6的Left節點的Left節點,因此也稱爲LL旋轉。
右旋操做
也就是若是結點6的左孩子節點4有右孩子,則將節點4的右孩子變成節點6的左孩子,最後將節點6變成節點4的右孩子
單向左旋平衡處理
左旋平衡處理也叫RR旋轉,是LL的鏡像操做
雙向旋轉(先右後左)平衡處理 (Right Left)
爲何會有這種狀況出現呢,由於咱們的平衡樹,首先也是一顆二叉排序樹,必須知足左節點<根節點<右節點的插入規則。
因此以下圖,節點4插入致使樹失去平衡,單向旋轉已經不能知足要求了,須要先讓節點6右旋,而後再把節點2左旋
雙向旋轉(先左後右)平衡處理 (Left Right)
同理,是RL的鏡像操做
代碼實現
//右旋轉 public BinaryTree RightRotate(BinaryTree root) { BinaryTree lchild = root.Left; root.Left = lchild.Right; lchild.Right = root; return lchild; } //左旋轉 public BinaryTree LeftRotate(BinaryTree root) { BinaryTree rchild = root.Right; root.Right = rchild.Left; rchild.Left = root; return rchild; } //先左後右旋轉 public BinaryTree LeftRightRotate(BinaryTree root) { root.Left = root.Left.LeftRotate(root); return RightRotate(root); } //先右後左旋轉 public BinaryTree RightLeftRotate(BinaryTree root) { root.Right = root.Right.RightRotate(root); return LeftRotate(root); } //計算平衡因子,取絕對值 public int Balance(BinaryTree root) { int val = 0; if (root.Left != null) val += Height(root.Left); if (root.Right != null) val -= Height(root.Right); return Math.Abs(val); } //計算樹的高度 public int Height(BinaryTree root) { int leftHeight = 0; int rightHeight = 0; if (root != null && root.Left != null) { leftHeight += Height(root.Left); } if (root != null && root.Right != null) { rightHeight += Height(root.Right); } return rightHeight > leftHeight ? ++rightHeight : ++leftHeight; }
插入操做
public BinaryTree Inster(BinaryTree root, int key) { if (root == null) { root = new BinaryTree(key); } else if (key < root.Value)//插入到左邊 { root.Left = Inster(root.Left, key); if (Balance(root) > 1)//插入左節點致使樹失衡了 { if (key < root.Left.Value)//LL處理,右旋 { root = RightRotate(root); } else { root = LeftRightRotate(root);//LR處理,先左後右 } } } else { root.Right = Inster(root.Right, key); if (Balance(root) > 1)//插入右節點致使失衡 { if (key > root.Right.Value)//RR處理, 左旋 { root = LeftRotate(root); } else { root = RightLeftRotate(root);//RL處理,先右後左 } } } return root; }
使用平衡二叉樹後,查詢起來時間複雜度就從O(n)變爲了O( log n)。
平衡二叉樹的優勢在於由於樹結構維護的較好,因此搜索查詢速度很快,但在插入,刪除的時候,爲了保持樹的平衡會作一次或屢次旋轉。
適合用於插入刪除操做少,而搜索操做不少的狀況。
爲了減小插入,刪除在旋轉方面的消耗,另外一種自平衡樹結構出現了
它就是:紅黑樹
紅黑樹不追求"徹底平衡",即不像AVL那樣要求節點的 |平衡因子| <= 1,它只要求部分達到平衡,可是提出了爲節點增長顏色,紅黑是用非嚴格的平衡來換取增刪節點時候旋轉次數的下降,任何不平衡都會在三次旋轉以內解決,而AVL是嚴格平衡樹,所以在增長或者刪除節點的時候,根據不一樣狀況,旋轉的次數比紅黑樹要多。
學會了AVL在去看紅黑樹也就很簡單了~~
http://www.javashuo.com/article/p-rzzewkvd-ks.html
https://baijiahao.baidu.com/s?id=1577200621749785094&wfr=spider&for=pc