資料結構系列:Objective-C實現二叉樹
本篇是我在學習二叉樹時做的總結,屬於面向我這種小白的文章
摘自《維基百科》
在電腦科學中,二叉樹(英語:Binary tree)是每個節點最多隻有兩個分支(即不存在分支度大於2的節點)的樹結構。通常分支被稱作“左子樹”或“右子樹”。二叉樹的分支具有左右次序,不能隨意顛倒。
與普通樹不同,普通樹的節點個數至少為1,而二叉樹的節點個數可以為0;普通樹節點的最大分支度沒有限制,而二叉樹節點的最大分支度為2;普通樹的節點無左、右次序之分,而二叉樹的節點有左、右次序之分。
摘自《百度百科》
完全二叉樹:對於深度為K的,有n個結點的二叉樹,當且僅當其每一個結點都與深度為K的滿二叉樹中編號從1至n的結點一一對應時稱之為完全二叉樹。
滿二叉樹:一個二叉樹,如果每一個層的結點數都達到最大值,則這個二叉樹就是滿二叉樹。也就是說,如果一個二叉樹的層數為K,且結點總數是(2^k) -1 ,則它就是滿二叉樹。
廢話不多說,直接上原始碼。下面是Objective-C實現的核心程式碼以及呼叫原始碼。
@implementation BinaryTree
/**
新增節點
@param item 節點根元素
*/
- (void)add:(NSInteger)item {
Node *node = [[Node alloc] initWithItem: item];
if (self.root == nil)
{
self.root = node;
return;
}
NSMutableArray *queue = [NSMutableArray array];
[queue addObject: self.root];
while (queue.count) {
Node *curNode = queue.firstObject;
[queue removeObjectAtIndex: 0];
if (curNode.leftChild == nil)
{
curNode.leftChild = node;
return;
}
else {
[queue addObject: curNode.leftChild];
}
if (curNode.rightChild == nil)
{
curNode.rightChild = node;
return;
}
else {
[queue addObject: curNode.rightChild];
}
}
}
/**
廣度遍歷
*/
- (void)breadthTraversal {
if (self.root == nil) return;
NSMutableArray *queue = [NSMutableArray array];
[queue addObject: self.root];
while (queue.count) {
Node *curNode = queue.firstObject;
[queue removeObjectAtIndex: 0];
NSLog(@"%ld", curNode.element);
if (curNode.leftChild != nil) [queue addObject: curNode.leftChild];
if (curNode.rightChild != nil) [queue addObject: curNode.rightChild];
}
}
/**
深度遍歷:前序遍歷(先序遍歷)
@param node 遍歷開始節點
*/
- (void)preorderTraversal:(Node *)node {
if (node == nil) return;
NSLog(@"%ld", node.element);
[self preorderTraversal: node.leftChild];
[self preorderTraversal: node.rightChild];
}
/**
深度遍歷:中序遍歷
@param node 遍歷開始節點
*/
- (void)inorderTraversal:(Node *)node {
if (node == nil) return;
[self inorderTraversal: node.leftChild];
NSLog(@"%ld", node.element);
[self inorderTraversal: node.rightChild];
}
/**
深度遍歷:後序遍歷
@param node 遍歷開始節點
*/
- (void)postorderTraversal:(Node *)node {
if (node == nil) return;
[self postorderTraversal: node.leftChild];
[self postorderTraversal: node.rightChild];
NSLog(@"%ld", node.element);
}
@end
實際使用案例
- (void)viewDidLoad {
[super viewDidLoad];
BinaryTree *tree = [[BinaryTree alloc] init];
for (int i = 0; i < 10; ++i)
{
[tree add: i];
}
[tree breadthTraversal]; // 廣度遍歷:0 1 2 3 4 5 6 7 8 9
[tree preorderTraversal: tree.root]; // 前序遍歷:0 1 3 7 8 4 9 2 5 6
[tree inorderTraversal: tree.root]; // 中序遍歷:7 3 8 1 9 4 0 5 2 6
[tree postorderTraversal: tree.root]; // 後序遍歷:7 8 3 9 4 1 5 6 2 0
}
寡人的思路
由於我們是為了簡單實現二叉樹,所以節點的元素型別用最基礎的NSInteger
新增方法:- (void)add:(NSInteger)item
新增的原則就是要讓整個二叉樹朝著滿二叉樹發展。
方法中的陣列 queue
定義成可變陣列,我們把它當做佇列使用,新增和刪除待處理的節點將按照先進先出的原則。在每次想要新增一個節點之前,都需要先把二叉樹的根節點新增到陣列的最前面,方便從頭查詢。
每次都從陣列中取出第一個元素,然後將這個元素從陣列中刪除,即處理一個就出佇列一個。如果取出的這個節點的左子樹不為空,說明這個節點有左子樹,那就把其左子樹放到待處理佇列(queue
陣列)中。如果這個節點左子樹為空,說明這個節點沒有左子樹,那就把要新增的這個 node
賦值上去,新增動作至此結束。右子樹同理。
廣度遍歷:- (void)breadthTraversal
逐層列印元素的值
前序遍歷:- (void)preorderTraversal:(Node *)node
先看一下前序遍歷的原理圖:
前序遍歷的原則是“根→左→右”。要把整個二叉樹看做一個整體,先去處理根,就是列印根元素。然後處理整個二叉樹的“左”,等到整個左子樹全部處理列印完,再去處理整個二叉樹的“右”。
處理整個二叉樹的“左”和整個二叉樹的“右”的時候依然要按照“根→左→右”的原則。這時就要把整個二叉樹的“左”和“右”分別重新看做一個整體。先處理這個整體的根,然後處理左子樹,最後處理右子樹。同理逐層向下。
圖中的紅色箭頭就是先序遍歷的處理順序。
根據“根→左→右”的原則:
整個二叉樹的“根”是 節點:0,第一個列印 0。這時“根”處理完,該處理“左”。
-
整個二叉樹的“左”是以 節點:1 為根的二叉樹,包括 節點:3、4、7、8、9 。到了“左”這一部分,仍然要按照“根→左→右”的原則去處理。這部分的“根”是 節點:1 ,第二個列印 1。
- “根”處理完之後,要處理這部分的“左”,就是以 節點:3 為根的二叉樹,包括 節點:7、8 。這部分還是按照“根→左→右”的原則,先處理這部分的“根”,也就是說第三個列印 3
- “根”處理完之後,處理“左”。這個“左”就是 節點:7 ,到了 節點:7 這裡就再無子樹了,所以第四個列印 7之後就說明“左”處理完了。接下來應該處理“右”,同樣這個“右”即 節點:8 也再無子樹,在第五個列印 8之後就算處理完“右”。
- 至此列印了 0、1、3、7、8 之後,我們剛剛處理好以 節點 3 為根的二叉樹也就是以 節點 1 為根的二叉樹的“左”。
- 接下來就要處理以 節點 1 為根的二叉樹的“右”,這個“右”是一個以 節點:4 為根的二叉樹。先處理這個“右”的“根”,第六個列印 4,接下來處理以 節點: 4 為根的二叉樹的“左”,第七個列印 9 。
- 到這裡我們列印了 0、1、3、7、8、4、9 ,我們處理完了以 節點: 4 為根的二叉樹也就是以 節點: 1 為根的二叉樹的“右”,那麼以 節點: 1 為根的二叉樹也已全部處理完。同時我們也已處理完以 節點: 0 為根的二叉樹的“左”。接下來同樣按照這個方式去處理以 節點: 1 為根的二叉樹的“右”。
整個二叉樹的“右”是以 節點:2 為根的二叉樹,包括 節點:5、6 。道理一樣,就不再贅述了。
說實話,這麼分析是真的累
中序遍歷:- (void)inorderTraversal:(Node *)node
下面是中序遍歷的原理圖:
圖中灰色箭頭是用來幫助查詢應該處理的“真凶”的幕後路線,紅色箭頭就是表面的查詢路線。
中序遍歷的原則是“左→根→右”,所以我們要先找到“左”。
先整體再區域性。整體是以 節點:0 為根的二叉樹,逐層向下查詢“左”依次是:以 節點:1 為根的二叉樹、以 節點:3 為根的二叉樹、節點:7。所以到 節點:7 這裡才真正找到我們要最先處理的“左”,即第一個列印 7。
列印了 7 之後,我們就處理完了以 節點:3 為根的二叉樹的“左”。接下來要處理它的“根”,所以第二個列印 3。然後要處理它的“右”,第三個列印 8。
列印了 7、3、8 之後就處理完了以 節點:1 為根的二叉樹的“左”。接下來無限迴圈的找下去。。。 我不想再繼續下去了,快要迷糊了
後序遍歷:- (void)postorderTraversal:(Node *)node
下面是後序遍歷的原理圖:
各位這個後序遍歷我就不BB了,要不然顯得我在湊字數了,雖然這段話也是在湊字數
Github完整原始碼
參考資料
相關文章
- 資料結構-二叉搜尋樹的實現資料結構
- 資料結構(樹):二叉樹資料結構二叉樹
- java實現-資料結構之二叉樹(三):線索化二叉樹Java資料結構二叉樹
- 資料結構之二叉搜尋樹—Java實現資料結構Java
- python資料結構之二叉樹的實現Python資料結構二叉樹
- 資料結構-二叉樹資料結構二叉樹
- 資料結構 - 二叉樹資料結構二叉樹
- 【資料結構】二叉樹!!!資料結構二叉樹
- 利用PHP實現常用的資料結構之二叉樹(小白系列文章六)PHP資料結構二叉樹
- 資料結構高階--二叉搜尋樹(原理+實現)資料結構
- 資料結構-平衡二叉樹資料結構二叉樹
- 資料結構之「二叉樹」資料結構二叉樹
- 資料結構——平衡二叉樹資料結構二叉樹
- 資料結構高階--AVL(平衡二叉樹)(圖解+實現)資料結構二叉樹圖解
- Python資料結構——二叉搜尋樹的實現(上)Python資料結構
- python資料結構之二叉樹遍歷的實現Python資料結構二叉樹
- 資料結構之通用樹結構的實現資料結構
- 資料結構中的樹(二叉樹、二叉搜尋樹、AVL樹)資料結構二叉樹
- 資料結構——二叉樹進階資料結構二叉樹
- 資料結構-二叉搜尋樹資料結構
- 資料結構 二叉樹遍歷資料結構二叉樹
- 【資料結構】二叉搜尋樹!!!資料結構
- 資料結構分析之二叉樹資料結構二叉樹
- 【資料結構】回顧二叉樹資料結構二叉樹
- 【資料結構】二叉樹(c++)資料結構二叉樹C++
- 資料結構二叉樹學習資料結構二叉樹
- 資料結構-二叉樹、堆、圖資料結構二叉樹
- 資料結構和演算法-Go實現二叉搜尋樹資料結構演算法Go
- 【資料結構】實現紅黑樹!!!資料結構
- 資料結構學習系列之二叉搜尋樹詳解!資料結構
- 資料結構——樹與二叉樹的遍歷資料結構二叉樹
- 重學資料結構之樹和二叉樹資料結構二叉樹
- 重學資料結構(六、樹和二叉樹)資料結構二叉樹
- [資料結構] 樹、二叉樹、森林的轉換資料結構二叉樹
- 實戰資料結構(11)_二叉樹的遍歷資料結構二叉樹
- 資料結構實驗——二叉樹的常見操作資料結構二叉樹
- 重溫資料結構系列--樹資料結構
- 資料結構初階--二叉樹介紹(基本性質+堆實現順序結構)資料結構二叉樹