昨天學習了一下算法,找了幾個例子,總結到一個demo里面。
什么是二叉樹?
二叉樹算法主要是遞歸的思想,維基百科上對遞歸的定義:在計算機科學中是指一種通過重復將問題分解為同類的子問題而解決問題的方法。我理解的話就是好像一個包子餡的包子,然后包子里面還是包子餡,一直循環下去,最后找到出口就是包子里面是個饅頭。
在計算機科學中,二叉樹是每個節點最多有兩個子樹的樹結構。通常子樹被稱作“左子樹”和“右子樹”,左子樹和右子樹同時也是二叉樹。二叉樹的子樹有左右之分,并且次序不能任意顛倒。二叉樹是遞歸定義的,所以一般二叉樹的相關題目也都可以使用遞歸的思想來解決,當然也有一些可以使用非遞歸的思想解決,我下面列出的一些算法有些采用了遞歸,有些是非遞歸的。
什么是二叉排序樹?
二叉排序樹又叫二叉查找樹或者二叉搜索樹,它首先是一個二叉樹,而且必須滿足下面的條件:
1)若左子樹不空,則左子樹上所有結點的值均小于它的根節點的值;
2)若右子樹不空,則右子樹上所有結點的值均大于它的根結點的值
3)左、右子樹也分別為二叉排序樹
4)沒有鍵值相等的節點
下面是具體的代碼,BinaryTreeNode.h文件
@interface BinaryTreeNode : NSObject
@property (nonatomic, assign) NSInteger value;
@property (nonatomic, strong) BinaryTreeNode *leftNode;
@property (nonatomic, strong) BinaryTreeNode *rightNode;
//創建二叉樹
+ (BinaryTreeNode *)createTreeWithValues:(NSArray *)values;
//二叉樹中某個位置的節點(按層次遍歷)
+ (BinaryTreeNode *)treeNodeAtIndex:(NSInteger)index inTree:(BinaryTreeNode *)rootNode;
//向二叉排序樹節點添加一個節點
+ (BinaryTreeNode *)addTreeNode:(BinaryTreeNode *)treeNode value:(NSInteger)value;
//翻轉二叉樹
+ (BinaryTreeNode *)invertBinaryTree:(BinaryTreeNode *)rootNode;
// 非遞歸方式翻轉
+ (BinaryTreeNode *)invertBinaryTreeNot:(BinaryTreeNode *)rootNode;
//先序遍歷:先訪問根,再遍歷左子樹,再遍歷右子樹。典型的遞歸思想。
+ (void)preOrderTraverseTree:(BinaryTreeNode *)rootNode handler:(void(^)(BinaryTreeNode *treeNode))handler;
//中序遍歷:先遍歷左子樹,再訪問根,再遍歷右子樹
+ (void)inOrderTraverseTree:(BinaryTreeNode *)rootNode handler:(void(^)(BinaryTreeNode *treeNode))handler;
//后序遍歷:先遍歷左子樹,再遍歷右子樹,再訪問根
+ (void)postOrderTraverseTree:(BinaryTreeNode *)rootNode handler:(void(^)(BinaryTreeNode *treeNode))handler;
//層次遍歷(廣度優先)
+ (void)levelTraverseTree:(BinaryTreeNode *)rootNode handler:(void(^)(BinaryTreeNode *treeNode))handler;
//二叉樹的寬度
+ (NSInteger)widthOfTree:(BinaryTreeNode *)rootNode;
//二叉樹的所有節點數
+ (NSInteger)numberOfNodesInTree:(BinaryTreeNode *)rootNode;
//二叉樹某層中的節點數
+ (NSInteger)numberOfNodesOnLevel:(NSInteger)level inTree:(BinaryTreeNode *)rootNode;
//二叉樹葉子節點數
+ (NSInteger)numberOfLeafsInTree:(BinaryTreeNode *)rootNode;
//二叉樹最大距離(直徑)
+ (NSInteger)maxDistanceOfTree:(BinaryTreeNode *)rootNode;
@end
@implementation BinaryTreeNode
/**
* 創建二叉排序樹
* 二叉排序樹:左節點值全部小于根節點值,右節點值全部大于根節點值
*
* @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 = [BinaryTreeNode 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 = [BinaryTreeNode addTreeNode:treeNode.leftNode value:value];
}
else {
NSLog(@"to right");
//值大于根節點,則插入到右子樹
treeNode.rightNode = [BinaryTreeNode addTreeNode:treeNode.rightNode value:value];
}
return treeNode;
}
/**
* 翻轉二叉樹(又叫:二叉樹的鏡像)
*
* @param rootNode 根節點
*
* @return 翻轉后的樹根節點(其實就是原二叉樹的根節點)
*/
+ (BinaryTreeNode *)invertBinaryTree:(BinaryTreeNode *)rootNode {
if (!rootNode) {
return nil;
}
if (!rootNode.leftNode && !rootNode.rightNode) {
return rootNode;
}
[BinaryTreeNode invertBinaryTree:rootNode.leftNode];
[BinaryTreeNode invertBinaryTree:rootNode.rightNode];
BinaryTreeNode *tempNode = rootNode.leftNode;
rootNode.leftNode = rootNode.rightNode;
rootNode.rightNode = tempNode;
return rootNode;
}
/**
* 非遞歸方式翻轉
*/
+ (BinaryTreeNode *)invertBinaryTreeNot:(BinaryTreeNode *)rootNode {
if (!rootNode) { return nil; }
if (!rootNode.leftNode && !rootNode.rightNode) { return rootNode; }
NSMutableArray *queueArray = [NSMutableArray array]; //數組當成隊列
[queueArray addObject:rootNode]; //壓入根節點
while (queueArray.count > 0) {
BinaryTreeNode *node = [queueArray firstObject];
[queueArray removeObjectAtIndex:0]; //彈出最前面的節點,仿照隊列先進先出原則
BinaryTreeNode *pLeft = node.leftNode;
node.leftNode = node.rightNode;
node.rightNode = pLeft;
if (node.leftNode) {
[queueArray addObject:node.leftNode];
}
if (node.rightNode) {
[queueArray addObject:node.rightNode];
}
}
return rootNode;
}
/**
* 二叉樹中某個位置的節點(按層次遍歷)
*
* @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);
}
[BinaryTreeNode preOrderTraverseTree:rootNode.leftNode handler:handler];
[BinaryTreeNode preOrderTraverseTree:rootNode.rightNode handler:handler];
}
}
/**
* 中序遍歷
* 先遍歷左子樹,再訪問根,再遍歷右子樹
*
* @param rootNode 根節點
* @param handler 訪問節點處理函數
*/
+ (void)inOrderTraverseTree:(BinaryTreeNode *)rootNode handler:(void(^) (BinaryTreeNode *treeNode))handler {
if (rootNode) {
[BinaryTreeNode inOrderTraverseTree:rootNode.leftNode handler:handler];
if (handler) {
handler(rootNode);
}
[BinaryTreeNode inOrderTraverseTree:rootNode.rightNode handler:handler];
}
}
/**
* 后序遍歷
* 先遍歷左子樹,再遍歷右子樹,再訪問根
*
* @param rootNode 根節點
* @param handler 訪問節點處理函數
*/
+ (void)postOrderTraverseTree:(BinaryTreeNode *)rootNode handler:(void(^)(BinaryTreeNode *treeNode))handler {
if (rootNode) {
[BinaryTreeNode postOrderTraverseTree:rootNode.leftNode handler:handler];
[BinaryTreeNode postOrderTraverseTree:rootNode.rightNode handler:handler];
if (handler) {
handler(rootNode);
}
}
}
/**
* 層次遍歷(廣度優先)
*
* @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]; //壓入右節點
}
}
}
/**
* 二叉樹的深度
*
* @param rootNode 二叉樹根節點
*
* @return 二叉樹的深度
*/
+ (NSInteger)depthOfTree:(BinaryTreeNode *)rootNode {
if (!rootNode) {
return 0;
}
if (!rootNode.leftNode && !rootNode.rightNode) {
return 1;
}
//左子樹深度
NSInteger leftDepth = [BinaryTreeNode depthOfTree:rootNode.leftNode];
//右子樹深度
NSInteger rightDepth = [BinaryTreeNode 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;
}
/**
* 二叉樹的所有節點數
*
* @param rootNode 根節點
*
* @return 所有節點數
*/
+ (NSInteger)numberOfNodesInTree:(BinaryTreeNode *)rootNode {
if (!rootNode) {
return 0;
}
//節點數=左子樹節點數+右子樹節點數+1(根節點)
return [BinaryTreeNode numberOfNodesInTree:rootNode.leftNode] + [BinaryTreeNode numberOfNodesInTree:rootNode.rightNode] + 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 [BinaryTreeNode numberOfNodesOnLevel:level-1 inTree:rootNode.leftNode] + [BinaryTreeNode 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 [BinaryTreeNode numberOfLeafsInTree:rootNode.leftNode] + [BinaryTreeNode numberOfLeafsInTree:rootNode.rightNode];
}
/**
* 二叉樹最大距離(直徑)
*
* @param rootNode 根節點
*
* @return 最大距離
*/
+ (NSInteger)maxDistanceOfTree:(BinaryTreeNode *)rootNode {
if (!rootNode) {
return 0;
}
// 方案一:(遞歸次數較多,效率較低)
//分3種情況:
//1、最遠距離經過根節點:距離 = 左子樹深度 + 右子樹深度
NSInteger distance = [BinaryTreeNode depthOfTree:rootNode.leftNode] + [BinaryTreeNode depthOfTree:rootNode.rightNode];
//2、最遠距離在根節點左子樹上,即計算左子樹最遠距離
NSInteger disLeft = [BinaryTreeNode maxDistanceOfTree:rootNode.leftNode];
//3、最遠距離在根節點右子樹上,即計算右子樹最遠距離
NSInteger disRight = [BinaryTreeNode maxDistanceOfTree:rootNode.rightNode];
return MAX(MAX(disLeft, disRight), distance);
}
@end
在其他頁面調用部分方法代碼:
NSArray *arr = [NSArray arrayWithObjects:@(7),@(6),@(3),@(2),@(1),@(9),@(10),@(12),@(14),@(4),@(14), nil];
BinaryTreeNode *tree = [BinaryTreeNode new];
tree = [BinaryTreeNode createTreeWithValues:arr];
BinaryTreeNode *tree1 = [BinaryTreeNode treeNodeAtIndex:3 inTree:tree];
NSLog(@"%@",tree1);
NSMutableArray *orderArray = [NSMutableArray array];
[BinaryTreeNode preOrderTraverseTree:tree handler:^(BinaryTreeNode *treeNode) {
[orderArray addObject:@(treeNode.value)];
}];
NSLog(@"先序遍歷結果:%@", [orderArray componentsJoinedByString:@","]);
NSMutableArray *orderArray1 = [NSMutableArray array];
[BinaryTreeNode inOrderTraverseTree:tree handler:^(BinaryTreeNode *treeNode) {
[orderArray1 addObject:@(treeNode.value)];
}];
NSLog(@"中序遍歷結果:%@", [orderArray1 componentsJoinedByString:@","]);
NSMutableArray *orderArray2 = [NSMutableArray array];
[BinaryTreeNode postOrderTraverseTree:tree handler:^(BinaryTreeNode *treeNode) {
[orderArray2 addObject:@(treeNode.value)];
}];
NSLog(@"后序遍歷結果:%@", [orderArray2 componentsJoinedByString:@","]);
NSMutableArray *orderArray3 = [NSMutableArray array];
[BinaryTreeNode levelTraverseTree:tree handler:^(BinaryTreeNode *treeNode) {
[orderArray3 addObject:@(treeNode.value)];
}];
NSLog(@"層次遍歷結果:%@", [orderArray3 componentsJoinedByString:@","]);
NSArray *arr = [NSArray arrayWithObjects:@(7),@(6),@(3),@(2),@(1),@(9),@(10),@(12),@(14),@(4),@(14), nil];
BinaryTreeNode *tree = [BinaryTreeNode new];
tree = [BinaryTreeNode createTreeWithValues:arr];
BinaryTreeNode *tree1 = [BinaryTreeNode treeNodeAtIndex:3 inTree:tree];
NSLog(@"%@",tree1);
NSMutableArray *orderArray = [NSMutableArray array];
[BinaryTreeNode preOrderTraverseTree:tree handler:^(BinaryTreeNode *treeNode) {
[orderArray addObject:@(treeNode.value)];
}];
NSLog(@"先序遍歷結果:%@", [orderArray componentsJoinedByString:@","]);
NSMutableArray *orderArray1 = [NSMutableArray array];
[BinaryTreeNode inOrderTraverseTree:tree handler:^(BinaryTreeNode *treeNode) {
[orderArray1 addObject:@(treeNode.value)];
}];
NSLog(@"中序遍歷結果:%@", [orderArray1 componentsJoinedByString:@","]);
NSMutableArray *orderArray2 = [NSMutableArray array];
[BinaryTreeNode postOrderTraverseTree:tree handler:^(BinaryTreeNode *treeNode) {
[orderArray2 addObject:@(treeNode.value)];
}];
NSLog(@"后序遍歷結果:%@", [orderArray2 componentsJoinedByString:@","]);
NSMutableArray *orderArray3 = [NSMutableArray array];
[BinaryTreeNode levelTraverseTree:tree handler:^(BinaryTreeNode *treeNode) {
[orderArray3 addObject:@(treeNode.value)];
}];
NSLog(@"層次遍歷結果:%@", [orderArray3 componentsJoinedByString:@","]);