二叉树各种操作的总结
来源:互联网 发布:阿里云学生服务器 编辑:程序博客网 时间:2024/06/05 10:07
本篇文章部分参考轻松搞定面试中的二叉树题目实现。
- 求二叉树中的节点个数
- 求二叉树中叶子节点的个数
- 求二叉树的深度
- 求二叉树第K层的节点个数
- 递归遍历前序中序后序
- 非递归遍历前序中序后序层序
- 1 前序遍历
- 2 中序遍历
- 3 后序遍历
- 4 层序遍历
- 将二叉查找树变为有序的双向链表
- 求二叉树的镜像
- 判断两棵二叉树是否结构相同
- 判断二叉树是不是平衡二叉树
- 判断二叉树是否是搜索二叉树
- 判断二叉树是不是完全二叉树
- 求二叉树中两个节点的最低公共祖先节点
- 求二叉树中节点的最大距离
- 由前序遍历序列和中序遍历序列重建二叉树
所有源码:https://github.com/qzxin/BinaryTree/blob/master/binary-tree-all.cpp
1. 求二叉树中的节点个数
递归求解,二叉树的节点个数等于 左子树的个数+右子树的个数+1(根)
//求二叉树中的节点个数int GetNums(BinaryTreeNode* root) { if (root == NULL) return 0; return GetNums(root->m_pLeft) + GetNums(root->m_pRight) + 1;}
求二叉树中叶子节点的个数
叶子节点的定义是:自身非空,左右为NULL;
和求二叉树中的节点个数类似,只是必须满足左右为NULL,才算1个
int GetLeafNodeNums(BinaryTreeNode* root) { if (root == NULL) return 0; if (root->m_pLeft == NULL && root->m_pRight == NULL) return 1; int leftNums = GetLeafNodeNums(root->m_pLeft); int rightNums = GetLeafNodeNums(root->m_pRight); return leftNums+rightNums;}
2. 求二叉树的深度
递归求解,二叉树的深度等于 左右子树深度中的最大值+1(根)
// 求二叉树的深度int GetDepth(BinaryTreeNode* root) { if (root == NULL) return 0; return max(GetDepth(root->m_pLeft), GetDepth(root->m_pRight)) + 1;}
3. 求二叉树第K层的节点个数
递归实现
k = 1, nums = 1;
二叉树第K层的节点个数 = 左子树K-1层的节点个数 + 右子树K-1层的节点个数
// 求二叉树第K层的节点个数int GetNLevelNums(BinaryTreeNode* root, int k) { if (root == NULL || k == 0) return 0; if (k == 1) return 1; // 左右子树k-1层节点数的和 return GetNLevelNums(root->m_pLeft, k-1) + GetNLevelNums(root->m_pRight, k-1);}
4 . 递归遍历:前序,中序,后序
// 递归遍历:前序遍历,中序遍历,后序遍历void visit(BinaryTreeNode* root) { cout << root->m_val << " ";}void PreOrderTravel(BinaryTreeNode* root) { if (root == NULL) return; visit(root); PreOrderTravel(root->m_pLeft); PreOrderTravel(root->m_pRight);}void InOrderTravel(BinaryTreeNode* root) { if (root == NULL) return; InOrderTravel(root->m_pLeft); visit(root); InOrderTravel(root->m_pRight);}void PostOrderTravel(BinaryTreeNode* root) { if (root == NULL) return; PostOrderTravel(root->m_pLeft); PostOrderTravel(root->m_pRight); visit(root);}
5. 非递归遍历:前序,中序,后序,层序
5.1 前序遍历
先访问根,然后压右节点,左节点进栈。(访问时顺序则是,根,左,右)
void PreOrderTravel(BinaryTreeNode* root) { if (root == NULL) return; stack<BinaryTreeNode*> s; s.push(root); while (s.empty() == false) { root = s.top(); visit(root); s.pop(); if (root->m_pRight) s.push(root->m_pRight); if (root->m_pLeft) s.push(root->m_pLeft); }}
5.2 中序遍历
先入栈后访问。因为中序首先要访问左节点,所以要压到树的最后一个左节点,才开始访问;最后一个左节点没有左节点(相当于根),之后开始遍历它的右子树。
void InOrderTravelRecur(BinaryTreeNode* root) { if (root == NULL) return; stack<BinaryTreeNode*> s; BinaryTreeNode* node = root; while (node != NULL || s.empty() == false) { if (node) { s.push(node); node = node->m_pLeft; } else { node = s.top(); s.pop(); visit(node); node = node->m_pRight; } }}
5.3 后序遍历
用2个栈实现,
// 辅助栈s1:压根,压左,压右; // 栈s2: 压根,压右,压左;(出栈访问则为:左,右,根)void PostOrderTravelRecur(BinaryTreeNode* root) { if (root == NULL) return; stack<BinaryTreeNode*> s1; stack<BinaryTreeNode*> s2; BinaryTreeNode* node = root; s1.push(node); while (s1.empty() == false) { node = s1.top(); s1.pop(); s2.push(node); //根压入s2 if (node->m_pLeft) s1.push(node->m_pLeft);// 左 压入 s1 if (node->m_pRight) s1.push(node->m_pRight); // 右 压入 s1 } while (s2.empty() == false) { node = s2.top(); s2.pop(); visit(node); }}
5.4 层序遍历
逐层访问,先入先出,使用队列实现。访问根,然后将左、右入队列
void LevelOrderTravel(BinaryTreeNode* root) { if (root == NULL) return; queue<BinaryTreeNode*> q; q.push(root); while (q.empty() == false) { root = q.front(); visit(root); q.pop(); if (root->m_pLeft) q.push(root->m_pLeft); if (root->m_pRight) q.push(root->m_pRight); }}
6. 将二叉查找树变为有序的双向链表
递归实现,二叉树可以分为左子树、根、右子树
设双向链表的头尾是pFirst, pLast
- 根为空: pFirst, pLast为NULL
- 处理左子树
- 左子树空:则根为双向链表的头部
- 左子树非空:左子树双向链表的头,是最终双向链表的头;左子树双向链表的尾和根相连;
- 处理右子树
- 右子树空:根为双向链表的尾部
- 右子树非空: 右子树双向链表的尾,是最终双向链表的尾;右子树双向链表的头和根相连;
因此,除了声明最终双向链表的头尾,还要声明左、右子树转换成双向链表的头尾。
// 将二叉查找树变为有序的双向链表void CovertToList(BinaryTreeNode* root, BinaryTreeNode* &pFirst, BinaryTreeNode* &pLast) { BinaryTreeNode* pLeftFirst(NULL), *pLeftLast(NULL), *pRightFirst(NULL), *pRightLast(NULL); if (root == NULL) { pFirst = NULL; pLast = NULL; return; } if (root->m_pLeft == NULL) { pFirst = root; } else { CovertToList(root->m_pLeft, pLeftFirst, pLeftLast); pLeftLast->m_pRight = root; root->m_pLeft = pLeftLast; pFirst = pLeftFirst; } if (root->m_pRight == NULL) { pLast = root; } else { CovertToList(root->m_pRight, pRightFirst, pRightLast); pRightFirst->m_pLeft = root; root->m_pRight = pRightFirst; pLast = pRightFirst; }}
7. 求二叉树的镜像
递归的交换树的左右子树。
// 求二叉树的镜像BinaryTreeNode* MirrorTree(BinaryTreeNode* root) { if (root == NULL) return NULL; BinaryTreeNode* pLeft = MirrorTree(root->m_pLeft); BinaryTreeNode* pRight = MirrorTree(root->m_pRight); root->m_pLeft = pRight; root->m_pRight = pLeft; return root;}
8. 判断两棵二叉树是否结构相同
递归实现:先判断根是否相同,再判断左子树和右子树是否相同
// 判断两棵二叉树是否结构相同
bool TreeStructCmp(BinaryTreeNode* root1, BinaryTreeNode* root2) { if (root1 == NULL && root2 == NULL) return true; if (root1 != root2) return false; bool leftResult = TreeStructCmp(root1->m_pLeft, root2->m_pLeft); bool rightResult = TreeStructCmp(root1->m_pRight, root2->m_pRight); return leftResult && rightResult;}
9. 判断二叉树是不是平衡二叉树
只有每一个子树都是平衡树,才能保证它是平衡二叉树。因此,先判断左右子树是否是平衡的并记录左右子树的深度,再判断树是否平衡。
bool IsAVL(BinaryTreeNode* root) { int depth; return IsAVLTree(root, depth);}// 后序遍历,只访问1次节点bool IsAVLTree(BinaryTreeNode* root, int &depth) { if (root == NULL) { depth = 0; return true; } int leftDepth, rightDepth; // 后序遍历,先判断左右子树是否是平衡的,再判断树是否平衡 if (IsAVLTree(root->m_pLeft, leftDepth) && IsAVLTree(root->m_pRight, rightDepth)) { if (abs(leftDepth-rightDepth) <= 1) { depth = max(leftDepth, rightDepth)+1; return true; } } return false;}
10. 判断二叉树是否是搜索二叉树
注意:仅通过比较左节点 <= 根 <= 右节点 不能判断这是一个搜索二叉树,因为这不能保证右子树的所有节点值都大于左子树中的所有节点。如下图所示,它不是BST
41 5 0 6
通过中序遍历(从小到大的顺序)来判断是否是一个搜索二叉树。
// 判断二叉树是否是搜索二叉树bool IsBST(BinaryTreeNode* root) { int prev = INT_MIN; // 比较元素,设为最小 return IsBSTreeHelper(root, prev);}// 中序遍历,判断遍历顺序是否是从小到大bool IsBSTreeHelper(BinaryTreeNode* root, int &prev) { if (root == NULL) return true; if (IsBSTreeHelper(root->m_pLeft, prev)) { if (root->m_val >= prev) { prev = root->m_val; if (IsBSTreeHelper(root->m_pRight, prev)) return true; // 此时左子树、根、右子树都满足要求 else return false; // 右不满足 } else { return false; // 根不满足 } } else { return false; // 左不满足 }}
11. 判断二叉树是不是完全二叉树
完全二叉树,要求在最后一层的右侧才可以存在空节点。
层序遍历,一旦一个节点含有空子树后,之后所有的节点必须只含有NULL。
- 不含有空子树时,按层序将当前节点的左右子树压入队列
- 一旦含有空子树时,当前节点和队列中后续节点的左右子树,必须为空,才是完全二叉树。
/ 判断二叉树是不是完全二叉树// 层序遍历,一旦一个节点含有空子树后,之后所有的节点必须只含有NULLbool IsCompleteBinaryTree(BinaryTreeNode* root) { if (root == NULL) return true; queue<BinaryTreeNode* > q; bool HaveNULL = false; q.push(root); while (q.empty() == false) { root = q.front(); q.pop(); if (HaveNULL) { if (root->m_pLeft || root->m_pRight) return false; } else { if (root->m_pLeft && root->m_pRight) { // 左右非空 q.push(root->m_pLeft); q.push(root->m_pRight); } else if (root->m_pLeft && root->m_pRight == NULL) { // 左非空,右空 HaveNULL = true; q.push(root->m_pLeft); } else if (root->m_pLeft == NULL && root->m_pRight) { // 左空,右非空 return false; } else { // 左右为空 HaveNULL = true; } } } return true;}
12. 求二叉树中两个节点的最低公共祖先节点
递归解法:
(1)如果两个节点,1个在左子树,1个在右子树,则最低公共祖先节点是根
(2)如果两个节点都在左子树,递归处理左子树;反之处理右子树
在树中寻找 node1 ,并保存路径,如果没找到node1,递归把路径的节点pop;
在树中寻找 node2 ,并保存路径,如果没找到node1,递归把路径的节点pop;
如果有1个没有找到,则返回NULL,如果都找到,则从2条路径的开始,向后遍历,找到最后一个共同的节点。
// 求二叉树中两个节点的最低公共祖先节点BinaryTreeNode* GetLastCommonParent(BinaryTreeNode* root, BinaryTreeNode* node1, BinaryTreeNode* node2) { if (root == NULL || node1 == NULL || node2 == NULL) return NULL; list<BinaryTreeNode*> path1, path2; bool found1 = GetPath(root, node1, path1); bool found2 = GetPath(root, node2, path2); BinaryTreeNode* pLast = NULL; if (!found1 || !found2) return 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) break; pLast = *iter1; iter1++; iter2++; } return pLast;}// 获得根到该节点的路径bool GetPath(BinaryTreeNode* root, BinaryTreeNode* node, list<BinaryTreeNode*> &path) { if (root == node) { path.push_back(root); return true; } if (root == NULL) return false; bool found = false; path.push_back(root); // 路径从根开始 found = GetPath(root->m_pLeft, node, path); //左子树中查找 if (!found) found = GetPath(root->m_pRight, node, path); // 右子树中查找 if (!found) path.pop_back(); // 此子树中没找到,pop出已经压入的此子树的节点 return found;}
13. 求二叉树中节点的最大距离
二叉树中节点的最大距离maxDistance指,任意两个节点相连的路径长度的最大值。
- 树为空,距离为0;
- 树非空,最大距离有3种可能,左子树中的最大距离,右子树中的最大距离,从左子树到根然后到右子树的最大距离
int GetMaxDistance(BinaryTreeNode* root) { int maxLeft = 0, maxRight = 0; // 左右子树到根的最大距离 return GetMaxDistRecur(root, maxLeft, maxRight); // 在计算最大距离时,记录左右子树到根的最大距离}//maxLeft, maxRight 左右子树中的节点到根的最大距离int GetMaxDistRecur(BinaryTreeNode* root, int &maxLeft, int &maxRight) { if (root == NULL) { maxLeft = 0, maxRight = 0; return 0; } int maxLL, maxLR, maxRL, maxRR; int maxDistLeft, maxDistRight; // 左、右子树中的最大距离 if (root->m_pLeft) { maxDistLeft = GetMaxDistRecur(root->m_pLeft, maxLL, maxLR); maxLeft = max(maxLL, maxRR) + 1; } else { maxDistLeft = 0; maxLeft = 0; } if (root->m_pRight) { maxDistRight = GetMaxDistRecur(root->m_pRight, maxRL, maxRR); maxRight = max(maxRL, maxRR) + 1; } else { maxDistRight = 0; maxRight = 0; } return max(maxLeft+maxRight, max(maxDistLeft, maxDistRight));}
14. 由前序遍历序列和中序遍历序列重建二叉树
前序的开始是根;中序的一个节点的左侧是左子树,右侧是右子树。
因此先从前序中确定1个根,然后去中序中找到这个根,确定属于该根的左右子树的范围。递归的处理左右子树。
// 由前序遍历序列和中序遍历序列重建二叉树BinaryTreeNode* RebuildBinaryTree(int* preorder, int* inorder, int nums) { if (preorder == NULL || inorder == NULL || nums <= 0) return NULL; BinaryTreeNode* root = new BinaryTreeNode(preorder[0]); // 根 int rootPositionOnInorder = -1; for (int i = 0; i < nums; i++) { if (inorder[i] == root->m_val) { rootPositionOnInorder = i; // 中序中寻找根 break; } } if (rootPositionOnInorder == -1) { cout << "Input Error." << endl; } // Rebuild Left Tree root->m_pLeft = RebuildBinaryTree(preorder+1, inorder, rootPositionOnInorder); // Rebuild Right Tree root->m_pRight = RebuildBinaryTree(preorder+1+rootPositionOnInorder, inorder+1+rootPositionOnInorder, nums-rootPositionOnInorder-1); return root;}
- 二叉树各种操作的总结
- 二叉树的各种操作
- 二叉树的各种操作
- 二叉树的各种操作
- 二叉树的各种操作
- 二叉树的各种操作
- 二叉树的各种操作
- 二叉树的各种操作
- 二叉树各种操作
- 二叉树各种操作
- 【树】二叉树的各种操作
- 二叉查找树的各种操作
- 二叉查找树的各种操作
- 二叉查找树的各种操作源码
- 二叉树的各种操作_源代码
- 遍历二叉树的各种操作
- 遍历二叉树的各种操作
- 二叉树的各种遍历操作
- C语言常见函数重写
- poj 3468 A Simple Problem with Integers(线段树、延迟更新)
- Unique Paths II
- VC++中的字符问题
- 2.app recommendation with very sparse datasets
- 二叉树各种操作的总结
- springMVC的转发和重定向及Controller参数
- 【项目2-三数最大值】
- How to Think Critically
- C/C++带括号四则运算
- sprintf和printf http://blog.sina.com.cn/s/blog_44f08a12010007pi.html
- 台湾国立大学机器学习基石.听课笔记(第三讲): 机器学习的分类
- 天天的招聘,天天的解聘!民营医疗你的人才在哪?
- 图片裁剪类