数据结构之树
一,什么是树形结构
百度百科:树形结构指的是数据元素之间存在着“一对多”的树形关系的数据结构,是一类重要的非线性数据结构。在树形结构中,树根结点没有前驱结点,其余每个结点有且只有一个前驱结点。叶子结点没有后续结点,其余每个结点的后续节点数可以是一个也可以是多个。
一般形态:下图代表的是树型结构的一般形态
基本概念:
1、结点:树中的数据元素都称之为结点
2、根:最上面的结点称之为根,一颗树只有一个根且由根发展而来,从另外一个角度来说,每个结点都可以认为是其子树的根
3、父亲:结点的上层结点,如图中,结点K的父亲是E、结点L的父亲是G
4、兄弟:具有相同父亲的结点称为兄弟,图中F、G、H互为兄弟
5、结点的度:结点所拥有的子树的个数称之为结点的度,如结点B的度为3
6、树叶:度为0的结点,也叫作终端结点,图中D、K、F、L、H、I、J都是树叶
7、分支结点:度不为0的结点,也叫作非终端结点或内部结点,图中根、A、B、C、E、G都是分支结点
8、结点的层次:从根节点到树中某结点所经路径上的分支树称为该结点的层次,根节点的层次规定为1,其余结点的层次等于其父亲结点的层次+1
9、树的深度:树中结点的最大层次数,图中树的深度为4
二,什么是二叉树
网络定义:树是一种比较重要的数据结构,尤其是二叉树。二叉树是一种特殊的树(可将每个结点类比成一个人,人本身代表着结点,左右手代表着子结点),在二叉树中每个节点最多有两个子节点,一般称为左子节点和右子节点(或左孩子和右孩子),并且二叉树的子树有左右之分,其次序不能任意颠倒。二叉树是递归定义的,因此,与二叉树有关的题目基本都可以用递归思想解决。
百度百科:二叉树在图论中是这样定义的:二叉树是一个连通的无环图,并且每一个顶点的度不大于3。有根二叉树还要满足根结点的度不大于2。有了根结点之后,每个顶点定义了唯一的父结点,和最多2个子结点。
基本形态:
(1)空二叉树——如图(a);
(2)只有一个根结点的二叉树——如图(b);
(3)只有左子树——如图(c);
(4)只有右子树——如图(d);
(5)完全二叉树——如图(e)。
五种基本形态
节点定义:
1 2 3 4 5 6 | struct BinaryTreeNode { int m_nValue; BinaryTreeNode* m_pLeft; BinaryTreeNode* m_pRight; }; |
三,二叉树的相关问题
1. 求二叉树中的节点个数
递归解法:
(1)如果二叉树为空,节点个数为0
(2)如果二叉树不为空,二叉树节点个数 = 左子树节点个数 + 右子树节点个数 + 1
参考代码如下:
1 2 3 4 5 6 | int GetNodeNum(BinaryTreeNode * pRoot) { if (pRoot == NULL ) // 递归出口 return 0; return GetNodeNum(pRoot->m_pLeft) + GetNodeNum(pRoot->m_pRight) + 1; } |
2. 求二叉树的深度
递归解法:
(1)如果二叉树为空,二叉树的深度为0
(2)如果二叉树不为空,二叉树的深度 = max(左子树深度, 右子树深度) + 1
参考代码如下:
1 2 3 4 5 6 7 8 | int GetDepth(BinaryTreeNode * pRoot) { if (pRoot == NULL ) // 递归出口 return 0; int depthLeft = GetDepth(pRoot->m_pLeft); int depthRight = GetDepth(pRoot->m_pRight); return depthLeft > depthRight ? (depthLeft + 1) : (depthRight + 1); } |
3. 前序遍历,中序遍历,后序遍历
前序遍历递归解法:
(1)如果二叉树为空,空操作
(2)如果二叉树不为空,访问根节点,前序遍历左子树,前序遍历右子树
参考代码如下:
1 2 3 4 5 6 7 8 | void PreOrderTraverse(BinaryTreeNode * pRoot) { if (pRoot == NULL ) return ; Visit(pRoot); // 访问根节点 PreOrderTraverse(pRoot->m_pLeft); // 前序遍历左子树 PreOrderTraverse(pRoot->m_pRight); // 前序遍历右子树 } |
中序遍历递归解法
(1)如果二叉树为空,空操作。
(2)如果二叉树不为空,中序遍历左子树,访问根节点,中序遍历右子树
参考代码如下:
1 2 3 4 5 6 7 8 | void InOrderTraverse(BinaryTreeNode * pRoot) { if (pRoot == NULL ) return ; InOrderTraverse(pRoot->m_pLeft); // 中序遍历左子树 Visit(pRoot); // 访问根节点 InOrderTraverse(pRoot->m_pRight); // 中序遍历右子树 } |
后序遍历递归解法
(1)如果二叉树为空,空操作
(2)如果二叉树不为空,后序遍历左子树,后序遍历右子树,访问根节点
参考代码如下:
1 2 3 4 5 6 7 8 | void PostOrderTraverse(BinaryTreeNode * pRoot) { if (pRoot == NULL ) return ; PostOrderTraverse(pRoot->m_pLeft); // 后序遍历左子树 PostOrderTraverse(pRoot->m_pRight); // 后序遍历右子树 Visit(pRoot); // 访问根节点 } |
4.分层遍历二叉树(按层次从上往下,从左往右)
相当于广度优先搜索,使用队列实现。队列初始化,将根节点压入队列。当队列不为空,进行如下操作:弹出一个节点,访问,若左子节点或右子节点不为空,将其压入队列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | void LevelTraverse(BinaryTreeNode * pRoot) { if (pRoot == NULL ) return ; queue<BinaryTreeNode *> q; q.push(pRoot); while (!q.empty()) { BinaryTreeNode * pNode = q.front(); q.pop(); Visit(pNode); // 访问节点 if (pNode->m_pLeft != NULL ) q.push(pNode->m_pLeft); if (pNode->m_pRight != NULL ) q.push(pNode->m_pRight); } return ; } |
5. 将二叉查找树变为有序的双向链表
要求不能创建新节点,只调整指针。
递归解法:
(1)如果二叉树查找树为空,不需要转换,对应双向链表的第一个节点是NULL,最后一个节点是NULL
(2)如果二叉查找树不为空:
如果左子树为空,对应双向有序链表的第一个节点是根节点,左边不需要其他操作;
如果左子树不为空,转换左子树,二叉查找树对应双向有序链表的第一个节点就是左子树转换后双向有序链表的第一个节点,同时将根节点和左子树转换后的双向有序链 表的最后一个节点连接;
如果右子树为空,对应双向有序链表的最后一个节点是根节点,右边不需要其他操作;
如果右子树不为空,对应双向有序链表的最后一个节点就是右子树转换后双向有序链表的最后一个节点,同时将根节点和右子树转换后的双向有序链表的第一个节点连 接。
参考代码如下:
/******************************************************************************
参数:
pRoot: 二叉查找树根节点指针
pFirstNode: 转换后双向有序链表的第一个节点指针
pLastNode: 转换后双向有序链表的最后一个节点指针
******************************************************************************/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | void Convert(BinaryTreeNode * pRoot, BinaryTreeNode * & pFirstNode, BinaryTreeNode * & pLastNode) { BinaryTreeNode *pFirstLeft, *pLastLeft, * pFirstRight, *pLastRight; if (pRoot == NULL ) { pFirstNode = NULL ; pLastNode = NULL ; return ; } if (pRoot->m_pLeft == NULL ) { // 如果左子树为空,对应双向有序链表的第一个节点是根节点 pFirstNode = pRoot; } else { Convert(pRoot->m_pLeft, pFirstLeft, pLastLeft); // 二叉查找树对应双向有序链表的第一个节点就是左子树转换后双向有序链表的第一个节点 pFirstNode = pFirstLeft; // 将根节点和左子树转换后的双向有序链表的最后一个节点连接 pRoot->m_pLeft = pLastLeft; pLastLeft->m_pRight = pRoot; } if (pRoot->m_pRight == NULL ) { // 对应双向有序链表的最后一个节点是根节点 pLastNode = pRoot; } else { Convert(pRoot->m_pRight, pFirstRight, pLastRight); // 对应双向有序链表的最后一个节点就是右子树转换后双向有序链表的最后一个节点 pLastNode = pLastRight; // 将根节点和右子树转换后的双向有序链表的第一个节点连接 pRoot->m_pRight = pFirstRight; pFirstRight->m_pLeft = pRoot; } return ; } |
6. 求二叉树第K层的节点个数
递归解法:
(1)如果二叉树为空或者k<1返回0
(2)如果二叉树不为空并且k==1,返回1
(3)如果二叉树不为空且k>1,返回左子树中k-1层的节点个数与右子树k-1层节点个数之和
参考代码如下:
1 2 3 4 5 6 7 8 9 10 | int GetNodeNumKthLevel(BinaryTreeNode * pRoot, int k) { if (pRoot == NULL || k < 1) return 0; if (k == 1) return 1; int numLeft = GetNodeNumKthLevel(pRoot->m_pLeft, k-1); // 左子树中k-1层的节点个数 int numRight = GetNodeNumKthLevel(pRoot->m_pRight, k-1); // 右子树中k-1层的节点个数 return (numLeft + numRight); } |
7. 求二叉树中叶子节点的个数
递归解法:
(1)如果二叉树为空,返回0
(2)如果二叉树不为空且左右子树为空,返回1
(3)如果二叉树不为空,且左右子树不同时为空,返回左子树中叶子节点个数加上右子树中叶子节点个数
参考代码如下:
1 2 3 4 5 6 7 8 9 10 | int GetLeafNodeNum(BinaryTreeNode * pRoot) { if (pRoot == NULL ) return 0; if (pRoot->m_pLeft == NULL && pRoot->m_pRight == NULL ) return 1; int numLeft = GetLeafNodeNum(pRoot->m_pLeft); // 左子树中叶节点的个数 int numRight = GetLeafNodeNum(pRoot->m_pRight); // 右子树中叶节点的个数 return (numLeft + numRight); } |
8. 判断两棵二叉树是否结构相同
不考虑数据内容。结构相同意味着对应的左子树和对应的右子树都结构相同。
递归解法:
(1)如果两棵二叉树都为空,返回真
(2)如果两棵二叉树一棵为空,另一棵不为空,返回假
(3)如果两棵二叉树都不为空,如果对应的左子树和右子树都同构返回真,其他返回假
参考代码如下:
1 2 3 4 5 6 7 8 9 10 | bool StructureCmp(BinaryTreeNode * pRoot1, BinaryTreeNode * pRoot2) { if (pRoot1 == NULL && pRoot2 == NULL ) // 都为空,返回真 return true ; else if (pRoot1 == NULL || pRoot2 == NULL ) // 有一个为空,一个不为空,返回假 return false ; bool resultLeft = StructureCmp(pRoot1->m_pLeft, pRoot2->m_pLeft); // 比较对应左子树 bool resultRight = StructureCmp(pRoot1->m_pRight, pRoot2->m_pRight); // 比较对应右子树 return (resultLeft && resultRight); } |
9. 判断二叉树是不是平衡二叉树
递归解法:
(1)如果二叉树为空,返回真
(2)如果二叉树不为空,如果左子树和右子树都是AVL树并且左子树和右子树高度相差不大于1,返回真,其他返回假
参考代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | bool IsAVL(BinaryTreeNode * pRoot, int & height) { if (pRoot == NULL ) // 空树,返回真 { height = 0; return true ; } int heightLeft; bool resultLeft = IsAVL(pRoot->m_pLeft, heightLeft); int heightRight; bool resultRight = IsAVL(pRoot->m_pRight, heightRight); if (resultLeft && resultRight && abs(heightLeft - heightRight) <= 1) // 左子树和右子树都是AVL,并且高度相差不大于1,返回真 { height = max(heightLeft, heightRight) + 1; return true ; } else { height = max(heightLeft, heightRight) + 1; return false ; } } |
10. 求二叉树的镜像
递归解法:
(1)如果二叉树为空,返回空
(2)如果二叉树不为空,求左子树和右子树的镜像,然后交换左子树和右子树
参考代码如下:
1 2 3 4 5 6 7 8 9 10 11 | BinaryTreeNode * Mirror(BinaryTreeNode * pRoot) { if (pRoot == NULL ) // 返回NULL return NULL ; BinaryTreeNode * pLeft = Mirror(pRoot->m_pLeft); // 求左子树镜像 BinaryTreeNode * pRight = Mirror(pRoot->m_pRight); // 求右子树镜像 // 交换左子树和右子树 pRoot->m_pLeft = pRight; pRoot->m_pRight = pLeft; return pRoot; } |
11. 求二叉树中两个节点的最低公共祖先节点
递归解法:
(1)如果两个节点分别在根节点的左子树和右子树,则返回根节点
(2)如果两个节点都在左子树,则递归处理左子树;如果两个节点都在右子树,则递归处理右子树
参考代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | bool FindNode(BinaryTreeNode * pRoot, BinaryTreeNode * pNode) { if (pRoot == NULL || pNode == NULL ) return false ; if (pRoot == pNode) return true ; bool found = FindNode(pRoot->m_pLeft, pNode); if (!found) found = FindNode(pRoot->m_pRight, pNode); return found; } BinaryTreeNode * GetLastCommonParent(BinaryTreeNode * pRoot, BinaryTreeNode * pNode1, BinaryTreeNode * pNode2) { if (FindNode(pRoot->m_pLeft, pNode1)) { if (FindNode(pRoot->m_pRight, pNode2)) return pRoot; else return GetLastCommonParent(pRoot->m_pLeft, pNode1, pNode2); } else { if (FindNode(pRoot->m_pLeft, pNode2)) return pRoot; else return GetLastCommonParent(pRoot->m_pRight, pNode1, pNode2); } } |
递归解法效率很低,有很多重复的遍历,下面看一下非递归解法。
非递归解法:
先求从根节点到两个节点的路径,然后再比较对应路径的节点就行,最后一个相同的节点也就是他们在二叉树中的最低公共祖先节点
参考代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | bool GetNodePath(BinaryTreeNode * pRoot, BinaryTreeNode * pNode, list<BinaryTreeNode *> & path) { if (pRoot == pNode) { path.push_back(pRoot); return true ; } if (pRoot == NULL ) return false ; path.push_back(pRoot); bool found = false ; found = GetNodePath(pRoot->m_pLeft, pNode, path); if (!found) found = GetNodePath(pRoot->m_pRight, pNode, path); if (!found) path.pop_back(); return found; } BinaryTreeNode * GetLastCommonParent(BinaryTreeNode * pRoot, BinaryTreeNode * pNode1, BinaryTreeNode * pNode2) { if (pRoot == NULL || pNode1 == NULL || pNode2 == NULL ) return NULL ; list<BinaryTreeNode*> path1; bool bResult1 = GetNodePath(pRoot, pNode1, path1); list<BinaryTreeNode*> path2; bool bResult2 = GetNodePath(pRoot, pNode2, path2); if (!bResult1 || !bResult2) return NULL ; BinaryTreeNode * pLast = NULL ; list<BinaryTreeNode*>::const_iterator iter1 = path1.begin(); list<BinaryTreeNode*>::const_iterator iter2 = path2.begin(); while (iter1 != path1.end() && iter2 != path2.end()) { if (*iter1 == *iter2) pLast = *iter1; else break ; iter1++; iter2++; } return pLast; } |
在上述算法的基础上稍加变化即可求二叉树中任意两个节点的距离了。
12. 求二叉树中节点的最大距离
即二叉树中相距最远的两个节点之间的距离。
递归解法:
(1)如果二叉树为空,返回0,同时记录左子树和右子树的深度,都为0
(2)如果二叉树不为空,最大距离要么是左子树中的最大距离,要么是右子树中的最大距离,要么是左子树节点中到根节点的最大距离+右子树节点中到根节点的最大距离,同时记录左子树和右子树节点中到根节点的最大距离。
参考代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | int GetMaxDistance(BinaryTreeNode * pRoot, int & maxLeft, int & maxRight) { // maxLeft, 左子树中的节点距离根节点的最远距离 // maxRight, 右子树中的节点距离根节点的最远距离 if (pRoot == NULL ) { maxLeft = 0; maxRight = 0; return 0; } int maxLL, maxLR, maxRL, maxRR; int maxDistLeft, maxDistRight; if (pRoot->m_pLeft != NULL ) { maxDistLeft = GetMaxDistance(pRoot->m_pLeft, maxLL, maxLR); maxLeft = max(maxLL, maxLR) + 1; } else { maxDistLeft = 0; maxLeft = 0; } if (pRoot->m_pRight != NULL ) { maxDistRight = GetMaxDistance(pRoot->m_pRight, maxRL, maxRR); maxRight = max(maxRL, maxRR) + 1; } else { maxDistRight = 0; maxRight = 0; } return max(max(maxDistLeft, maxDistRight), maxLeft+maxRight); } |
13. 由前序遍历序列和中序遍历序列重建二叉树
二叉树前序遍历序列中,第一个元素总是树的根节点的值。中序遍历序列中,左子树的节点的值位于根节点的值的左边,右子树的节点的值位
于根节点的值的右边。
递归解法:
(1)如果前序遍历为空或中序遍历为空或节点个数小于等于0,返回NULL。
(2)创建根节点。前序遍历的第一个数据就是根节点的数据,在中序遍历中找到根节点的位置,可分别得知左子树和右子树的前序和中序遍
历序列,重建左右子树。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | BinaryTreeNode * RebuildBinaryTree( int * pPreOrder, int * pInOrder, int nodeNum) { if (pPreOrder == NULL || pInOrder == NULL || nodeNum <= 0) return NULL ; BinaryTreeNode * pRoot = new BinaryTreeNode; // 前序遍历的第一个数据就是根节点数据 pRoot->m_nValue = pPreOrder[0]; pRoot->m_pLeft = NULL ; pRoot->m_pRight = NULL ; // 查找根节点在中序遍历中的位置,中序遍历中,根节点左边为左子树,右边为右子树 int rootPositionInOrder = -1; for ( int i = 0; i < nodeNum; i++) if (pInOrder[i] == pRoot->m_nValue) { rootPositionInOrder = i; break ; } if (rootPositionInOrder == -1) { throw std::exception( "Invalid input." ); } // 重建左子树 int nodeNumLeft = rootPositionInOrder; int * pPreOrderLeft = pPreOrder + 1; int * pInOrderLeft = pInOrder; pRoot->m_pLeft = RebuildBinaryTree(pPreOrderLeft, pInOrderLeft, nodeNumLeft); // 重建右子树 int nodeNumRight = nodeNum - nodeNumLeft - 1; int * pPreOrderRight = pPreOrder + 1 + nodeNumLeft; int * pInOrderRight = pInOrder + nodeNumLeft + 1; pRoot->m_pRight = RebuildBinaryTree(pPreOrderRight, pInOrderRight, nodeNumRight); return pRoot; } |
同样,有中序遍历序列和后序遍历序列,类似的方法可重建二叉树,但前序遍历序列和后序遍历序列不同恢复一棵二叉树,证明略。
14.判断二叉树是不是完全二叉树
若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全
二叉树。
有如下算法,按层次(从上到下,从左到右)遍历二叉树,当遇到一个节点的左子树为空时,则该节点右子树必须为空,且后面遍历的节点左
右子树都必须为空,否则不是完全二叉树。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | bool IsCompleteBinaryTree(BinaryTreeNode * pRoot) { if (pRoot == NULL ) return false ; queue<BinaryTreeNode *> q; q.push(pRoot); bool mustHaveNoChild = false ; bool result = true ; while (!q.empty()) { BinaryTreeNode * pNode = q.front(); q.pop(); if (mustHaveNoChild) // 已经出现了有空子树的节点了,后面出现的必须为叶节点(左右子树都为空) { if (pNode->m_pLeft != NULL || pNode->m_pRight != NULL ) { result = false ; break ; } } else { if (pNode->m_pLeft != NULL && pNode->m_pRight != NULL ) { q.push(pNode->m_pLeft); q.push(pNode->m_pRight); } else if (pNode->m_pLeft != NULL && pNode->m_pRight == NULL ) { mustHaveNoChild = true ; q.push(pNode->m_pLeft); } else if (pNode->m_pLeft == NULL && pNode->m_pRight != NULL ) { result = false ; break ; } else { mustHaveNoChild = true ; } } } return result; } |
四,oc中二叉树结构类
BinaryTreeNode .h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | #import <Foundation/Foundation.h> @interface BinaryTreeNode : NSObject /** * 值 */ @property ( nonatomic ,assign) NSInteger value; /** * 左节点 */ @property ( nonatomic ,strong) BinaryTreeNode * leftNode; /** * 右节点 */ @property ( nonatomic ,strong) BinaryTreeNode * rightNode; /** * 遍历构造器 */ +(instancetype)nodeWithValue:( NSInteger ) value; @end @interface BinaryTree : NSObject //创建二叉树 +(BinaryTreeNode *)createTreeWithValues:( NSArray *)values; //反转二叉树 +(BinaryTreeNode *)invertBinaryTree:(BinaryTreeNode *)rootNode; //反转二叉树(非递归) +(BinaryTreeNode *)invertBinaryTreeWithoutRecursion:(BinaryTreeNode *)rootNode; //二叉树深度 +( NSInteger )depathOfTree:(BinaryTreeNode *)rootNode; //二叉树所有节点数。 节点数 = 左子树节点数 + 右子树节点数 + 1 (根节点) +( NSInteger )numberOfNodesInTree:(BinaryTreeNode *)rootNode; //二叉树中某个节点到根节点的路径 +( NSArray *)pathOfTreeNode:(BinaryTreeNode *)treeNode inTree:(BinaryTreeNode *)rootNode; //先序遍历 +( void )treeFirstInformationWithNode:(BinaryTreeNode *)rootNode reslutBlock:( void (^)( NSInteger value))block; //中序遍历 +( void )treeMiddleInformationWithNode:(BinaryTreeNode *)rootNode reslutBlock:( void (^)( NSInteger value))block; //后序遍历 +( void )treeLastInformationWithNode:(BinaryTreeNode *)rootNode resultBlock:( void (^)( NSInteger value)) block; @end |
BinaryTreeNode.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | #import "BinaryTreeNode.h" @implementation BinaryTreeNode +(instancetype) nodeWithValue:( NSInteger )value { BinaryTreeNode * node =[[ super alloc] init]; node.value =value; return node; } @end @implementation BinaryTree /** * 生成二叉树 */ +(BinaryTreeNode *)createTreeWithValues:( NSArray *)values { BinaryTreeNode * root = nil ; for ( NSInteger i = 0; i<values.count; i++) { NSInteger value =[( NSNumber *)[values objectAtIndex:i] integerValue]; root =[[ super class ] addTreeNode:root value:value]; } return root; } /** * 翻转二叉树(非递归) @param rootNode 根节点 @return 翻转后的根节点(其实就是原二叉树的根节点) */ +(BinaryTreeNode *)invertBinaryTreeWithoutRecursion:(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 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; } #pragma mark --遍历二叉树 //先序遍历 +( void )treeFirstInformationWithNode:(BinaryTreeNode *)rootNode reslutBlock:( void (^)( NSInteger ))block { if (block) { block(rootNode.value); } if (rootNode.leftNode) { [ self treeFirstInformationWithNode:rootNode.leftNode reslutBlock:block]; } if (rootNode.rightNode) { [ self treeFirstInformationWithNode:rootNode.rightNode reslutBlock:block]; } } //中序遍历 +( void )treeMiddleInformationWithNode:(BinaryTreeNode *)rootNode reslutBlock:( void (^)( NSInteger ))block { if (rootNode.leftNode) { [ self treeMiddleInformationWithNode:rootNode.leftNode reslutBlock:block]; } if (block) { block(rootNode.value); } if (rootNode.rightNode) { [ self treeMiddleInformationWithNode:rootNode.rightNode reslutBlock:block]; } } +( void )treeLastInformationWithNode:(BinaryTreeNode *)rootNode resultBlock:( void (^)( NSInteger ))block { if (rootNode.leftNode) { [ self treeMiddleInformationWithNode:rootNode.leftNode reslutBlock:block]; } if (rootNode.rightNode) { [ self treeMiddleInformationWithNode:rootNode.rightNode reslutBlock:block]; } if (block) { block(rootNode.value); } } +( NSInteger )depathOfTree:(BinaryTreeNode *)rootNode { if (!rootNode) return 0; if (!rootNode.leftNode && rootNode.rightNode) return 1; NSInteger leftDepth = [ self depathOfTree:rootNode.leftNode]; NSInteger rightDepth = [ self depathOfTree:rootNode.rightNode]; return MAX(leftDepth, rightDepth)+1; } +( NSInteger )numberOfNodesInTree:(BinaryTreeNode *)rootNode { if (!rootNode) return 0; return [ self numberOfNodesInTree:rootNode.leftNode] +[ self numberOfNodesInTree:rootNode.rightNode] + 1; } +( NSArray *)pathOfTreeNode:(BinaryTreeNode *)treeNode inTree:(BinaryTreeNode *)rootNode { NSMutableArray *pathArray = [ NSMutableArray array]; [ self isFoundTreeNode:treeNode inTree:rootNode routePath:pathArray]; return pathArray; } #pragma mark --Private +(BinaryTreeNode *)addTreeNode:(BinaryTreeNode *)treeNode value:( NSInteger )value { if (!treeNode) { //根节点不存在,创建节点 treeNode = [BinaryTreeNode new ]; treeNode.value =value; } else if (value <=treeNode.value){ treeNode.leftNode = [[ self class ] addTreeNode:treeNode.leftNode value:value]; } else { treeNode.rightNode =[[ self class ] addTreeNode:treeNode.rightNode value:value]; } return treeNode; } +( 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; } @end |
参考:轻松搞定面试中的二叉树题目
二叉树-你必须要懂!(二叉树相关算法实现-iOS)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)