iOS 二叉樹相關算法實現

什么是二叉樹?

在計算機科學中,二叉樹是每個節點最多有兩個子樹的樹結構。通常子樹被稱作“左子樹”和“右子樹”,左子樹和右子樹同時也是二叉樹。二叉樹的子樹有左右之分,并且次序不能任意顛倒。二叉樹是遞歸定義的,所以一般二叉樹的相關題目也都可以使用遞歸的思想來解決,當然也有一些可以使用非遞歸的思想解決,我下面列出的一些算法有些采用了遞歸,有些是非遞歸的。

什么是二叉排序樹?

二叉排序樹又叫二叉查找樹或者二叉搜索樹,它首先是一個二叉樹,而且必須滿足下面的條件:

1)若左子樹不空,則左子樹上所有結點的值均小于它的根節點的值;

2)若右子樹不空,則右子樹上所有結點的值均大于它的根結點的值

3)左、右子樹也分別為二叉排序樹

4)沒有鍵值相等的節點(?可能是因為不好處理鍵值相等的節點到底是左節點還是右節點吧)

概念就介紹這么多,都是來自網上,下面主要看算法和具體實現代碼。

二叉樹節點定義

采用單項鏈表的形式,只從根節點指向孩子節點,不保存父節點。

復制代碼
/***  二叉樹節點*/
@interface BinaryTreeNode : NSObject/***  值*/
@property (nonatomic, assign) NSInteger value;
/***  左節點*/
@property (nonatomic, strong) BinaryTreeNode *leftNode;
/***  右節點*/
@property (nonatomic, strong) BinaryTreeNode *rightNode;@end
復制代碼

創建二叉排序樹

二叉樹中左右節點值本身沒有大小之分,所以如果要創建二叉樹,就需要考慮如何處理某個節點是左節點還是右節點,如何終止某個子樹而切換到另一個子樹。 因此我選擇了二叉排序樹,二叉排序樹中對于左右節點有明確的要求,程序可以自動根據鍵值大小自動選擇是左節點還是右節點。

復制代碼
/***  創建二叉排序樹*  二叉排序樹:左節點值全部小于根節點值,右節點值全部大于根節點值**  @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]; //壓入右節點
        }}//層次遍歷完,仍然沒有找到位置,返回nilreturn 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<0return 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種情況://1、最遠距離經過根節點:距離 = 左子樹深度 + 右子樹深度NSInteger distance = [self depthOfTree:rootNode.leftNode] + [self depthOfTree:rootNode.rightNode];//2、最遠距離在根節點左子樹上,即計算左子樹最遠距離NSInteger disLeft = [self maxDistanceOfTree:rootNode.leftNode];//3、最遠距離在根節點右子樹上,即計算右子樹最遠距離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中后面的節點壓入pathif ([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;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/247888.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/247888.shtml
英文地址,請注明出處:http://en.pswp.cn/news/247888.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

vux 組件庫首次使用安裝

1、首先通過腳手架新建一個項目&#xff0c;過程略。 創建完項目后&#xff0c;在項目里安裝vux&#xff0c; 通過命令 npm install vux --save 安裝 2、安裝vux-loader&#xff0c; 通過命令 npm install vux-loader --save-dev 安裝&#xff08;vux文檔沒說明&#xff09; 3、…

@Component 和 @Bean 的區別

Spring幫助我們管理Bean分為兩個部分&#xff0c;一個是注冊Bean&#xff0c;一個裝配Bean。完成這兩個動作有三種方式&#xff0c;一種是使用自動配置的方式、一種是使用JavaConfig的方式&#xff0c;一種就是使用XML配置的方式。 Compent 作用就相當于 XML配置 Component pub…

js動態驗證碼獲取

<!DOCTYPE html> <html lang"cn"> <head><meta charset"UTF-8"><title>短信驗證碼</title> </head> <body> <input type"number" id"tel" value"13303861063"> <…

Base64 算法原理,以及編碼、解碼【加密、解密】 介紹

Base64編碼&#xff0c;是我們程序開發中經常使用到的編碼方法。它是一種基于用64個可打印字符來表示二進制數據的表示方法。它通常用作存儲、傳輸一些二進制數據編碼方法&#xff01;也是MIME&#xff08;多用途互聯網郵件擴展&#xff0c;主要用作電子郵件標準&#xff09;中…

js通過身份證獲取年齡

// 獲取用戶的身份證號碼let identityCard this.idNum.replace(/\s/g, "");//判斷長度let len identityCard.length;//設置新的變量var strBirthday "";//根據長度獲取年月日if (len 18) {strBirthday identityCard.substr(6, 4) "/" identi…

爬取豆瓣top250

#xpath #第一種方法 可在開發者工具中找到標簽&#xff0c;右鍵copy xpath&#xff0c;有時需去掉tbody標簽 #第二種方法 簡單學習xpath&#xff0c;自己書寫&#xff0c;掌握基本語法即可&#xff0c;簡單的層級關系#先將csv文件以記事本打開&#xff0c;更改編碼為ASNI&…

TCP/IP,Http,Socket,XMPP的區別

網絡由下往上分為 物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層和應用層。 通過初步的了解&#xff0c;我知道IP協議對應于網絡層&#xff0c;TCP協議對應于傳輸層&#xff0c;而HTTP協議對應于應用層&#xff0c; 三者從本質上來說沒有可比性&#xff0c; socket則是對…

LED音樂頻譜之點陣

轉載請注明出處&#xff1a;http://blog.csdn.net/ruoyunliufeng/article/details/37967455 一.硬件 這里的LED選擇直插的霧面LED&#xff0c;亮度可以還不失美觀。注意每行要加上限流電阻。74HC138&#xff08;三八譯碼器&#xff09;作為列選&#xff0c;每行都連著74HC595&a…

上架相關——App Store 上架流程

說實話&#xff0c;公司要上架一個自己做的一個小項目。為了完成這個任務&#xff0c;菜鳥的我一遍找資料一遍跟著做&#xff0c;一遍修改錯誤一遍查找解決方案。網上的資料大部分都是2015年以前的資料&#xff0c;資料有點不夠過時&#xff0c;而且步驟配圖也不是很詳細&#…

this.$router 的三種跳轉頁面方法

第一種&#xff1a; this.$router.push(需要跳轉到的路徑名稱)此方法跳轉后&#xff0c;會在歷史欄目中保存路勁地址&#xff0c;當點擊歷史標簽時可以進行訪問 第二種&#xff1a; this.$router.replace(需要跳轉到的路徑名稱)此方法跳轉后&#xff0c;會在歷史欄目中不保存…

cf777c

題意&#xff1a;給你一個n*m的數陣 對于一行到另一行&#xff0c;若存在一列從上到下遞減&#xff0c;則稱之符合題意 The first line of the input contains two positive integers n and m (1?≤?nm?≤?100?000) — the number of rows and the number of columns in t…

上架相關——appstore 更新app版本

注&#xff1a;此片文章是基于app已經上架&#xff0c;也就是證書都已經配置好的前提下。 首先是還是app打包 修改版本號 修改project處的pp文件 檢查無誤后打包打包完成后upload to app store 漫長的等待。。 上傳到appstore進入iTunesConnect 選擇我的app 選擇對應app點…

輸入框輸入數字,且不能有小數點存在

基于Vue項目進行設置 <template><comp v-if"update"></comp><button click"reload()">刷新comp組件</button></template><script>import comp from /views/comp.vueexport default {name: parentComp,data() {r…

iOS開發 藍牙技術4.0詳解

前言 前端時間,同學在做項目過程中遇到關于藍牙方面的問題,今天我就給大家進行詳細的進行講解下藍牙在iOS開發中的具體實現.在介紹藍牙前,大家要搞清楚什么是藍牙? 什么是藍牙? 隨著藍牙低功耗技術BLE&#xff08;Bluetooth Low Energy&#xff09;的發展&#xff0c;藍牙技術…

前端面試題(五)

position的屬性有哪些&#xff1f; 1、absolute生成絕對定位的元素&#xff0c;相對于值不為 static的第一個父元素進行定位。 2、fixed &#xff08;老IE不支持&#xff09;生成絕對定位的元素&#xff0c;相對于瀏覽器窗口進行定位。 3、relative生成相對定位的元素&#xff…

qrcode.js 二維碼生成器

二維碼生成 并顯示&#xff1a; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns"http://www.w3.org/1999/xhtml" xml:lang"ko" …

SQL -- 多表查詢

-- --SQL基礎-->多表查詢 -- /* 一、多表查詢 簡言之&#xff0c;根據特定的連接條件從不同的表中獲取所需的數據 笛卡爾集的產生條件&#xff1a; 省略連接條件 連接條件無效 第一個表中的所有行與第二個表中的所有行相連接 二、多表查詢語法&#xff1a;*/ SELECT tab…

如何解決兩個相鄰的span中間有空隙

span中間不要有換行、或者空格 或者在樣式上加上float&#xff1a;left轉載于:https://www.cnblogs.com/lst619247/p/10944341.html

Vue項目中Table設置 render 函數

statusList1: {0: "",1: "",2: "藥品服務費收入",3: "特藥服務費收入",4: "直保經紀費",5: "再保經紀費",6: "廣告費",},render: (h, params) > {return this.colorCommon(h, params.row, "1&q…

AVPlayer設置從哪兒開始播放

avplayer 播放視頻 首先介紹幾個方法吧和屬性吧。 - (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(dispatch_queue_t)queue usingBlock:(void (^)(CMTime time))block 這個方法可以用于跟新進度條。 - (void)seekToTime:(CMTime)time completionHandler:(v…