這幾天詳細瞭解了下二叉樹的相關算法,緣由是看了唐boy的一篇博客( 你會翻轉二叉樹嗎? ),還有一篇關於 百度的校園招聘面試經歷 ,深入體會到二叉樹的重要性。因而乎,從網上收集並整理了一些關於二叉樹的資料,及相關算法的實現(主要是Objective-C的,可是算法思想是相通的),以便之後複習時查閱。html
在計算機科學中,二叉樹是每一個節點最多有兩個子樹的樹結構。一般子樹被稱做「左子樹」和「右子樹」,左子樹和右子樹同時也是二叉樹。二叉樹的子樹有左右之分,而且次序不能任意顛倒。二叉樹是遞歸定義的,因此通常二叉樹的相關題目也均可以使用遞歸的思想來解決,固然也有一些可使用非遞歸的思想解決,我下面列出的一些算法有些採用了遞歸,有些是非遞歸的。java
二叉排序樹又叫二叉查找樹或者二叉搜索樹,它首先是一個二叉樹,並且必須知足下面的條件:node
1)若左子樹不空,則左子樹上全部結點的值均小於它的根節點的值;面試
2)若右子樹不空,則右子樹上全部結點的值均大於它的根結點的值算法
3)左、右子樹也分別爲二叉排序樹數組
4)沒有鍵值相等的節點(?多是由於很差處理鍵值相等的節點究竟是左節點仍是右節點吧)網絡
概念就介紹這麼多,都是來自網上,下面主要看算法和具體實現代碼。函數
採用單項鍊表的形式,只從根節點指向孩子節點,不保存父節點。post
/**
* 二叉樹節點
*/
@interface BinaryTreeNode : NSObject /** * 值 */ @property (nonatomic, assign) NSInteger value; /** * 左節點 */ @property (nonatomic, strong) BinaryTreeNode *leftNode; /** * 右節點 */ @property (nonatomic, strong) BinaryTreeNode *rightNode; @end
二叉樹中左右節點值自己沒有大小之分,因此若是要建立二叉樹,就須要考慮如何處理某個節點是左節點仍是右節點,如何終止某個子樹而切換到另外一個子樹。 所以我選擇了二叉排序樹,二叉排序樹中對於左右節點有明確的要求,程序能夠自動根據鍵值大小自動選擇是左節點仍是右節點。atom
/**
* 建立二叉排序樹
* 二叉排序樹:左節點值所有小於根節點值,右節點值所有大於根節點值
*
* @param values 數組 * * @return 二叉樹根節點 */ + (BinaryTreeNode *)createTreeWithValues:(NSArray *)values { BinaryTreeNode *root = nil; for (NSInteger i=0; i<values.count; i++) { NSInteger value = [(NSNumber *)[values objectAtIndex:i] integerValue]; root = [BinaryTree addTreeNode:root value:value]; } return root; } /** * 向二叉排序樹節點添加一個節點 * * @param treeNode 根節點 * @param value 值 * * @return 根節點 */ + (BinaryTreeNode *)addTreeNode:(BinaryTreeNode *)treeNode value:(NSInteger)value { //根節點不存在,建立節點 if (!treeNode) { treeNode = [BinaryTreeNode new]; treeNode.value = value; NSLog(@"node:%@", @(value)); } else if (value <= treeNode.value) { NSLog(@"to left"); //值小於根節點,則插入到左子樹 treeNode.leftNode = [BinaryTree addTreeNode:treeNode.leftNode value:value]; } else { NSLog(@"to right"); //值大於根節點,則插入到右子樹 treeNode.rightNode = [BinaryTree addTreeNode:treeNode.rightNode value:value]; } return treeNode; }
相似索引操做,按層次遍歷,位置從0開始算。
/**
* 二叉樹中某個位置的節點(按層次遍歷)
*
* @param index 按層次遍歷樹時的位置(從0開始算) * @param rootNode 樹根節點 * * @return 節點 */ + (BinaryTreeNode *)treeNodeAtIndex:(NSInteger)index inTree:(BinaryTreeNode *)rootNode { //按層次遍歷 if (!rootNode || index < 0) { return nil; } NSMutableArray *queueArray = [NSMutableArray array]; //數組當成隊列 [queueArray addObject:rootNode]; //壓入根節點 while (queueArray.count > 0) { BinaryTreeNode *node = [queueArray firstObject]; if (index == 0) { return node; } [queueArray removeObjectAtIndex:0]; //彈出最前面的節點,仿照隊列先進先出原則 index--; //移除節點,index減小 if (node.leftNode) { [queueArray addObject:node.leftNode]; //壓入左節點 } if (node.rightNode) { [queueArray addObject:node.rightNode]; //壓入右節點 } } //層次遍歷完,仍然沒有找到位置,返回nil return nil; }
先訪問根,再遍歷左子樹,再遍歷右子樹。典型的遞歸思想。
/**
* 先序遍歷
* 先訪問根,再遍歷左子樹,再遍歷右子樹
*
* @param rootNode 根節點 * @param handler 訪問節點處理函數 */ + (void)preOrderTraverseTree:(BinaryTreeNode *)rootNode handler:(void(^)(BinaryTreeNode *treeNode))handler { if (rootNode) { if (handler) { handler(rootNode); } [self preOrderTraverseTree:rootNode.leftNode handler:handler]; [self preOrderTraverseTree:rootNode.rightNode handler:handler]; } }
調用方法以下:(用到了block)
NSMutableArray *orderArray = [NSMutableArray array];
[BinaryTree preOrderTraverseTree:root handler:^(BinaryTreeNode *treeNode) { [orderArray addObject:@(treeNode.value)]; }]; NSLog(@"先序遍歷結果:%@", [orderArray componentsJoinedByString:@","]);
先遍歷左子樹,再訪問根,再遍歷右子樹。
對於二叉排序樹來講,中序遍歷獲得的序列是一個從小到大排序好的序列。
/**
* 中序遍歷
* 先遍歷左子樹,再訪問根,再遍歷右子樹
*
* @param rootNode 根節點 * @param handler 訪問節點處理函數 */ + (void)inOrderTraverseTree:(BinaryTreeNode *)rootNode handler:(void(^)(BinaryTreeNode *treeNode))handler { if (rootNode) { [self inOrderTraverseTree:rootNode.leftNode handler:handler]; if (handler) { handler(rootNode); } [self inOrderTraverseTree:rootNode.rightNode handler:handler]; } }
先遍歷左子樹,再遍歷右子樹,再訪問根
/**
* 後序遍歷
* 先遍歷左子樹,再遍歷右子樹,再訪問根
*
* @param rootNode 根節點 * @param handler 訪問節點處理函數 */ + (void)postOrderTraverseTree:(BinaryTreeNode *)rootNode handler:(void(^)(BinaryTreeNode *treeNode))handler { if (rootNode) { [self postOrderTraverseTree:rootNode.leftNode handler:handler]; [self postOrderTraverseTree:rootNode.rightNode handler:handler]; if (handler) { handler(rootNode); } } }
按照從上到下、從左到右的次序進行遍歷。先遍歷完一層,再遍歷下一層,所以又叫廣度優先遍歷。須要用到隊列,在OC裏能夠用可變數組來實現。
/**
* 層次遍歷(廣度優先)
*
* @param rootNode 二叉樹根節點 * @param handler 訪問節點處理函數 */ + (void)levelTraverseTree:(BinaryTreeNode *)rootNode handler:(void(^)(BinaryTreeNode *treeNode))handler { if (!rootNode) { return; } NSMutableArray *queueArray = [NSMutableArray array]; //數組當成隊列 [queueArray addObject:rootNode]; //壓入根節點 while (queueArray.count > 0) { BinaryTreeNode *node = [queueArray firstObject]; if (handler) { handler(node); } [queueArray removeObjectAtIndex:0]; //彈出最前面的節點,仿照隊列先進先出原則 if (node.leftNode) { [queueArray addObject:node.leftNode]; //壓入左節點 } if (node.rightNode) { [queueArray addObject:node.rightNode]; //壓入右節點 } } }
二叉樹的深度定義爲:從根節點到葉子結點依次通過的結點造成樹的一條路徑,最長路徑的長度爲樹的深度。
1)若是根節點爲空,則深度爲0;
2)若是左右節點都是空,則深度爲1;
3)遞歸思想:二叉樹的深度=max(左子樹的深度,右子樹的深度)+ 1
/**
* 二叉樹的深度
*
* @param rootNode 二叉樹根節點 * * @return 二叉樹的深度 */ + (NSInteger)depthOfTree:(BinaryTreeNode *)rootNode { if (!rootNode) { return 0; } if (!rootNode.leftNode && !rootNode.rightNode) { return 1; } //左子樹深度 NSInteger leftDepth = [self depthOfTree:rootNode.leftNode]; //右子樹深度 NSInteger rightDepth = [self depthOfTree:rootNode.rightNode]; return MAX(leftDepth, rightDepth) + 1; }
二叉樹的寬度定義爲各層節點數的最大值。
/**
* 二叉樹的寬度
*
* @param rootNode 二叉樹根節點 * * @return 二叉樹寬度 */ + (NSInteger)widthOfTree:(BinaryTreeNode *)rootNode { if (!rootNode) { return 0; } NSMutableArray *queueArray = [NSMutableArray array]; //數組當成隊列 [queueArray addObject:rootNode]; //壓入根節點 NSInteger maxWidth = 1; //最大的寬度,初始化爲1(由於已經有根節點) NSInteger curWidth = 0; //當前層的寬度 while (queueArray.count > 0) { curWidth = queueArray.count; //依次彈出當前層的節點 for (NSInteger i=0; i<curWidth; i++) { BinaryTreeNode *node = [queueArray firstObject]; [queueArray removeObjectAtIndex:0]; //彈出最前面的節點,仿照隊列先進先出原則 //壓入子節點 if (node.leftNode) { [queueArray addObject:node.leftNode]; } if (node.rightNode) { [queueArray addObject:node.rightNode]; } } //寬度 = 當前層節點數 maxWidth = MAX(maxWidth, queueArray.count); } return maxWidth; }
遞歸思想:二叉樹全部節點數=左子樹節點數+右子樹節點數+1
/**
* 二叉樹的全部節點數
*
* @param rootNode 根節點 * * @return 全部節點數 */ + (NSInteger)numberOfNodesInTree:(BinaryTreeNode *)rootNode { if (!rootNode) { return 0; } //節點數=左子樹節點數+右子樹節點數+1(根節點) return [self numberOfNodesInTree:rootNode.leftNode] + [self numberOfNodesInTree:rootNode.rightNode] + 1; }
1)根節點爲空,則節點數爲0;
2)層爲1,則節點數爲1(即根節點)
3)遞歸思想:二叉樹第k層節點數=左子樹第k-1層節點數+右子樹第k-1層節點數
/**
* 二叉樹某層中的節點數
*
* @param level 層 * @param rootNode 根節點 * * @return 層中的節點數 */ + (NSInteger)numberOfNodesOnLevel:(NSInteger)level inTree:(BinaryTreeNode *)rootNode { if (!rootNode || level < 1) { //根節點不存在或者level<0 return 0; } if (level == 1) { //level=1,返回1(根節點) return 1; } //遞歸:level層節點數 = 左子樹level-1層節點數+右子樹level-1層節點數 return [self numberOfNodesOnLevel:level-1 inTree:rootNode.leftNode] + [self numberOfNodesOnLevel:level-1 inTree:rootNode.rightNode]; }
葉子節點,又叫終端節點,是左右子樹都是空的節點。
/**
* 二叉樹葉子節點數
*
* @param rootNode 根節點 * * @return 葉子節點數 */ + (NSInteger)numberOfLeafsInTree:(BinaryTreeNode *)rootNode { if (!rootNode) { return 0; } //左子樹和右子樹都是空,說明是葉子節點 if (!rootNode.leftNode && !rootNode.rightNode) { return 1; } //遞歸:葉子數 = 左子樹葉子數 + 右子樹葉子數 return [self numberOfLeafsInTree:rootNode.leftNode] + [self numberOfLeafsInTree:rootNode.rightNode]; }
二叉樹中任意兩個節點都有且僅有一條路徑,這個路徑的長度叫這兩個節點的距離。二叉樹中全部節點之間的距離的最大值就是二叉樹的直徑。
有一種解法,把這個最大距離劃分了3種狀況:
1)這2個節點分別在根節點的左子樹和右子樹上,他們之間的路徑確定通過根節點,並且他們確定是根節點左右子樹上最遠的葉子節點(他們到根節點的距離=左右子樹的深度)。
2)這2個節點都在左子樹上
3)這2個節點都在右子樹上
綜上,只要取這3種狀況中的最大值,就是二叉樹的直徑。
/**
* 二叉樹最大距離(直徑)
*
* @param rootNode 根節點 * * @return 最大距離 */ + (NSInteger)maxDistanceOfTree:(BinaryTreeNode *)rootNode { if (!rootNode) { return 0; } // 方案一:(遞歸次數較多,效率較低) //分3種狀況: //一、最遠距離通過根節點:距離 = 左子樹深度 + 右子樹深度 NSInteger distance = [self depthOfTree:rootNode.leftNode] + [self depthOfTree:rootNode.rightNode]; //二、最遠距離在根節點左子樹上,即計算左子樹最遠距離 NSInteger disLeft = [self maxDistanceOfTree:rootNode.leftNode]; //三、最遠距離在根節點右子樹上,即計算右子樹最遠距離 NSInteger disRight = [self maxDistanceOfTree:rootNode.rightNode]; return MAX(MAX(disLeft, disRight), distance); }
這個方案效率較低,由於計算子樹的深度和最遠距離是分開遞歸的,存在重複遞歸遍歷的狀況。其實一次遞歸,就能夠分別計算出深度和最遠距離,因而有了第二種方案:
/**
* 二叉樹最大距離(直徑)
*
* @param rootNode 根節點 * * @return 最大距離 */ + (NSInteger)maxDistanceOfTree:(BinaryTreeNode *)rootNode { if (!rootNode) { return 0; } // 方案2:將計算節點深度和最大距離放到一次遞歸中計算,方案一是分別單獨遞歸計算深度和最遠距離 TreeNodeProperty *p = [self propertyOfTreeNode:rootNode]; return p.distance; } /** * 計算樹節點的最大深度和最大距離 * * @param rootNode 根節點 * * @return TreeNodeProperty */ + (TreeNodeProperty *)propertyOfTreeNode:(BinaryTreeNode *)rootNode { if (!rootNode) { return nil; } TreeNodeProperty *left = [self propertyOfTreeNode:rootNode.leftNode]; TreeNodeProperty *right = [self propertyOfTreeNode:rootNode.rightNode]; TreeNodeProperty *p = [TreeNodeProperty new]; //節點的深度depth = 左子樹深度、右子樹深度中最大值+1(+1是由於根節點佔了1個depth) p.depth = MAX(left.depth, right.depth) + 1; //最遠距離 = 左子樹最遠距離、右子樹最遠距離和橫跨左右子樹最遠距離中最大值 p.distance = MAX(MAX(left.distance, right.distance), left.depth+right.depth); return p; }
既是尋路問題,又是查找節點問題。
定義一個存放路徑的棧(不是隊列了,可是仍是用可變數組來實現的)
1)壓入根節點,再從左子樹中查找(遞歸進行的),若是未找到,再從右子樹中查找,若是也未找到,則彈出根節點,再遍歷棧中上一個節點。
2)若是找到,則棧中存放的節點就是路徑所通過的節點。
/**
* 二叉樹中某個節點到根節點的路徑
*
* @param treeNode 節點 * @param rootNode 根節點 * * @return 存放路徑節點的數組 */ + (NSArray *)pathOfTreeNode:(BinaryTreeNode *)treeNode inTree:(BinaryTreeNode *)rootNode { NSMutableArray *pathArray = [NSMutableArray array]; [self isFoundTreeNode:treeNode inTree:rootNode routePath:pathArray]; return pathArray; } /** * 查找某個節點是否在樹中 * * @param treeNode 待查找的節點 * @param rootNode 根節點 * @param path 根節點到待查找節點的路徑 * * @return YES:找到,NO:未找到 */ + (BOOL)isFoundTreeNode:(BinaryTreeNode *)treeNode inTree:(BinaryTreeNode *)rootNode routePath:(NSMutableArray *)path { if (!rootNode || !treeNode) { return NO; } //找到節點 if (rootNode == treeNode) { [path addObject:rootNode]; return YES; } //壓入根節點,進行遞歸 [path addObject:rootNode]; //先從左子樹中查找 BOOL find = [self isFoundTreeNode:treeNode inTree:rootNode.leftNode routePath:path]; //未找到,再從右子樹查找 if (!find) { find = [self isFoundTreeNode:treeNode inTree:rootNode.rightNode routePath:path]; } //若是2邊都沒查找到,則彈出此根節點 if (!find) { [path removeLastObject]; } return find; }
首先須要明白,根節點確定是二叉樹中任意兩個節點的公共父節點(不必定是最近的),所以二叉樹中2個節點的最近公共父節點必定在從根節點到這個節點的路徑上。所以咱們能夠先分別找到從根節點到這2個節點的路徑,再從這兩個路徑中找到最近的公共父節點。
/**
* 二叉樹中兩個節點最近的公共父節點
*
* @param nodeA 第一個節點 * @param nodeB 第二個節點 * @param rootNode 二叉樹根節點 * * @return 最近的公共父節點 */ + (BinaryTreeNode *)parentOfNode:(BinaryTreeNode *)nodeA andNode:(BinaryTreeNode *)nodeB inTree:(BinaryTreeNode *)rootNode { if (!rootNode || !nodeA || !nodeB) { return nil; } if (nodeA == nodeB) { return nodeA; } //從根節點到節點A的路徑 NSArray *pathA = [self pathOfTreeNode:nodeA inTree:rootNode]; //從根節點到節點B的路徑 NSArray *pathB = [self pathOfTreeNode:nodeB inTree:rootNode]; //其中一個節點不在樹中,則沒有公共父節點 if (pathA.count == 0 || pathB == 0) { return nil; } //從後往前推,查找第一個出現的公共節點 for (NSInteger i = pathA.count-1; i>=0; i--) { for (NSInteger j = pathB.count - 1; j>=0; j--) { if ([pathA objectAtIndex:i] == [pathB objectAtIndex:j]) { //找到 return [pathA objectAtIndex:i]; } } } return nil; }
從查找最近公共父節點衍生出來的。
/**
* 二叉樹中兩個節點之間的路徑
*
* @param nodeA 第一個節點 * @param nodeB 第二個節點 * @param rootNode 二叉樹根節點 * * @return 兩個節點間的路徑 */ + (NSArray *)pathFromNode:(BinaryTreeNode *)nodeA toNode:(BinaryTreeNode *)nodeB inTree:(BinaryTreeNode *)rootNode { if (!rootNode || !nodeA || !nodeB) { return nil; } NSMutableArray *path = [NSMutableArray array]; if (nodeA == nodeB) { [path addObject:nodeA]; [path addObject:nodeB]; return path; } //從根節點到節點A的路徑 NSArray *pathA = [self pathOfTreeNode:nodeA inTree:rootNode]; //從根節點到節點B的路徑 NSArray *pathB = [self pathOfTreeNode:nodeB inTree:rootNode]; //其中一個節點不在樹中,則沒有路徑 if (pathA.count == 0 || pathB == 0) { return nil; } //從後往前推,查找第一個出現的公共節點 for (NSInteger i = pathA.count-1; i>=0; i--) { [path addObject:[pathA objectAtIndex:i]]; for (NSInteger j = pathB.count - 1; j>=0; j--) { //找到公共父節點,則將pathB中後面的節點壓入path if ([pathA objectAtIndex:i] == [pathB objectAtIndex:j]) { j++; //j++是爲了避開公共父節點 while (j<pathB.count) { [path addObject:[pathB objectAtIndex:j]]; j++; } return path; } } } return nil; }
能夠從兩個節點之間的路徑衍生出來。
/**
* 二叉樹兩個節點之間的距離
*
* @param nodeA 第一個節點 * @param nodeB 第二個節點 * @param rootNode 二叉樹根節點 * * @return 兩個節點間的距離(-1:表示沒有找到路徑) */ + (NSInteger)distanceFromNode:(BinaryTreeNode *)nodeA toNode:(BinaryTreeNode *)nodeB inTree:(BinaryTreeNode *)rootNode { if (!rootNode || !nodeA || !nodeB) { return -1; } if (nodeA == nodeB) { return 0; } //從根節點到節點A的路徑 NSArray *pathA = [self pathOfTreeNode:nodeA inTree:rootNode]; //從根節點到節點B的路徑 NSArray *pathB = [self pathOfTreeNode:nodeB inTree:rootNode]; //其中一個節點不在樹中,則沒有路徑 if (pathA.count == 0 || pathB == 0) { return -1; } //從後往前推,查找第一個出現的公共節點 for (NSInteger i = pathA.count-1; i>=0; i--) { for (NSInteger j = pathB.count - 1; j>=0; j--) { //找到公共父節點 if ([pathA objectAtIndex:i] == [pathB objectAtIndex:j]) { //距離=路徑節點數-1 (這裏要-2,由於公共父節點重複了一次) return (pathA.count - i) + (pathB.count - j) - 2; } } } return -1; }
你會翻轉二叉樹嗎?若是不會,那對不起,咱們不會錄用你!
翻轉二叉樹,又叫求二叉樹的鏡像,就是把二叉樹的左右子樹對調(固然是遞歸的)
/**
* 翻轉二叉樹(又叫:二叉樹的鏡像)
*
* @param rootNode 根節點 * * @return 翻轉後的樹根節點(其實就是原二叉樹的根節點) */ + (BinaryTreeNode *)invertBinaryTree:(BinaryTreeNode *)rootNode { if (!rootNode) { return nil; } if (!rootNode.leftNode && !rootNode.rightNode) { return rootNode; } [self invertBinaryTree:rootNode.leftNode]; [self invertBinaryTree:rootNode.rightNode]; BinaryTreeNode *tempNode = rootNode.leftNode; rootNode.leftNode = rootNode.rightNode; rootNode.rightNode = tempNode; return rootNode; }
徹底二叉樹定義爲: 若設二叉樹的高度爲 h ,除第 h 層外,其它各層的結點數都達到最大個數,第 h 層有葉子結點,而且葉子結點都是從左到右依次排布。
徹底二叉樹必須知足2個條件:
1) 若是某個節點的右子樹不爲空,則它的左子樹必須不爲空
2) 若是某個節點的右子樹爲空,則排在它後面的節點必須沒有孩子節點
這裏還須要理解「排在它後面的節點」,回頭看看層次遍歷算法,咱們就能知道在層次遍歷時,是從上到下從左到右遍歷的,先將根節點彈出隊列,再壓入孩子節點,所以「排在它後面的節點」有2種狀況:
1) 同層次的後面的節點
2) 同層次的前面的節點的孩子節點(由於遍歷前面的節點時,會彈出節點,同時將孩子節點壓入隊列)
經過上面的分析,咱們能夠設置一個標誌位flag,當子樹知足徹底二叉樹時,設置flag=YES。當flag=YES而節點又破壞了徹底二叉樹的條件,那麼它就不是徹底二叉樹。
/**
* 是否徹底二叉樹
* 徹底二叉樹:若設二叉樹的高度爲h,除第h層外,其它各層的結點數都達到最大個數,第h層有葉子結點,而且葉子結點都是從左到右依次排布
*
* @param rootNode 根節點 * * @return YES:是徹底二叉樹,NO:不是徹底二叉樹 */ + (BOOL)isCompleteBinaryTree:(BinaryTreeNode *)rootNode { if (!rootNode) { return NO; } //左子樹和右子樹都是空,則是徹底二叉樹 if (!rootNode.leftNode && !rootNode.rightNode) { return YES; } //左子樹是空,右子樹不是空,則不是徹底二叉樹 if (!rootNode.leftNode && rootNode.rightNode) { return NO; } //按層次遍歷節點,找到知足徹底二叉樹的條件: //條件1:若是某個節點的右子樹不爲空,則它的左子樹必須不爲空 //條件2:若是某個節點的右子樹爲空,則排在它後面的節點必須沒有孩子節點 //排在該節點後面的節點有2種:1)同層次的後面的節點 2)同層次的前面的節點的孩子節點(由於遍歷前面的節點的時候,會將節點從隊列裏pop,同時把它的孩子節點push到隊列裏) NSMutableArray *queue = [NSMutableArray array]; [queue addObject:rootNode]; BOOL isComplete = NO; //是否已經知足徹底二叉樹 while (queue.count > 0) { BinaryTreeNode *node = [queue firstObject]; [queue removeObjectAtIndex:0]; //左子樹爲空且右子樹不爲空,則不是徹底二叉樹 if (!node.leftNode && node.rightNode) { return NO; } if (isComplete && (node.leftNode || node.rightNode)) { //前面的節點已知足徹底二叉樹,若是還有孩子節點,則不是徹底二叉樹 return NO; } //右子樹爲空,則已經知足徹底二叉樹 if (!node.rightNode) { isComplete = YES; } //壓入 if (node.leftNode) { [queue addObject:node.leftNode]; } if (node.rightNode) { [queue addObject:node.rightNode]; } } return isComplete; }
滿二叉樹定義爲: 除了葉結點外每個結點都有左右子葉且葉子結點都處在最底層的二叉樹
滿二叉樹的一個特性是:葉子數=2^(深度-1),所以咱們能夠根據這個特性來判斷二叉樹是不是滿二叉樹。
/**
* 是否滿二叉樹
* 滿二叉樹:除了葉結點外每個結點都有左右子葉且葉子結點都處在最底層的二叉樹
*
* @param rootNode 根節點 * * @return YES:滿二叉樹,NO:非滿二叉樹 */ + (BOOL)isFullBinaryTree:(BinaryTreeNode *)rootNode { if (!rootNode) { return NO; } //二叉樹深度 NSInteger depth = [self depthOfTree:rootNode]; //二叉樹葉子節點數 NSInteger leafNum = [self numberOfLeafsInTree:rootNode]; //滿二叉樹特性:葉子數=2^(深度-1) if (leafNum == pow(2, (depth - 1))) { return YES; } return NO; }
平衡二叉樹定義爲: 它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過 1,而且左右兩個子樹都是一棵平衡二叉樹。平衡二叉樹又叫AVL樹。
/**
* 是否平衡二叉樹
* 平衡二叉樹:即AVL樹,它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,而且左右兩個子樹都是一棵平衡二叉樹
*
* @param rootNode 根節點 * * @return YES:平衡二叉樹,NO:非平衡二叉樹 */ + (BOOL)isAVLBinaryTree:(BinaryTreeNode *)rootNode { static NSInteger height; if (!rootNode) { height = 0; return YES; } if (!rootNode.leftNode && !rootNode.rightNode) { height = 1; return YES; } BOOL isAVLLeft = [self isAVLBinaryTree:rootNode.leftNode]; NSInteger heightLeft = height; BOOL isAVLRight = [self isAVLBinaryTree:rootNode.rightNode]; NSInteger heightRight = height; height = MAX(heightLeft, heightRight)+1; if (isAVLLeft && isAVLRight && ABS(heightLeft-heightRight) <= 1) { return YES; } return NO; }
以上就是我目前整理的一些二叉樹相關的算法,算法資料和思想都來源於網絡,若有錯誤,歡迎指正!後續若是有新的算法,我也會更新進去。