二叉树的各种操作(遍历/深度/距离/转换)
来源:互联网 发布:linux文件实时同步ftp 编辑:程序博客网 时间:2024/05/19 20:43
转载出处:http://www.61mon.com/index.php/archives/191/
(1):前序遍历,中序遍历,后序遍历;
(2):层次遍历;
(3):求树的节点数;
(4):求树的叶子数;
(5):求树的深度;
(6):求二叉树第 k 层的节点个数;
(7):判断两棵二叉树是否结构相同;
(8):求二叉树的镜像;
(9):求两个节点的最低公共祖先节点;
(10):求任意两节点距离;
(11):找出二叉树中某个节点的所有祖先节点;
(12):二叉树前序中序推后序;
(13):判断二叉树是不是平衡二叉树;
(14):判断二叉树是不是完全二叉树;
(15):判断是否是二叉查找树的后序遍历结果;
(16):给定一个二叉查找树中的节点,找出在中序遍历下它的后继和前驱;
(17):二分查找树转化为排序的循环双链表;
(18):二叉树路径等于目标值的所有路径。
(19):二叉树的所有路径。
二叉树节点定义如下:
struct BinaryTreeNode{ int m_nValue; BinaryTreeNode* m_pLeft; BinaryTreeNode* m_pRight;};(1):前序遍历,中序遍历,后序遍历;
前序遍历:对于当前节点,先输出该节点,然后输出它的左孩子,最后输出它的右孩子。
递归版本:
void PreOrderTraverse(BinaryTreeNode * pRoot) { if(pRoot == NULL) return; Visit(pRoot); PreOrderTraverse(pRoot->m_pLeft); // 前序遍历左子树 PreOrderTraverse(pRoot->m_pRight); // 前序遍历右子树 }非递归版本:
/* 前序遍历非递归版 */void PreOrderNonRec(Node * node){ stack<Node*> S; Node * p = node; while (p || !S.empty()) { while (p) { cout << p->data << " "; //先输出当前节点 S.push(p); p = p->left; //然后输出左孩子 } //while结束意味着左孩子已经全部输出 if (!S.empty()) { p = S.top()->right; //最后输出右孩子 S.pop(); } }}
中序遍历:对于当前节点,先输出它的左孩子,然后输出该节点,最后输出它的右孩子
递归版本:
/* 中序遍历递归版 */void InOrderRec(Node * node){ if (node == nullptr) return; InOrderRec(node->left); //先输出左孩子 cout << node->data << " "; //然后输出当前节点 InOrderRec(node->right); //最后输出右孩子}非递归版本:
/* 中序遍历非递归版 */void InOrderNonRec(Node * node){ stack<Node*> S; Node * p = node; while (p || !S.empty()) { while (p) { S.push(p); p = p->left; } //while结束意味着左孩子为空 if (!S.empty()) { cout << S.top()->data << " "; //左孩子已经全部输出,接着输出当前节点 p = S.top()->right; //左孩子全部输出,当前节点也输出后,最后输出右孩子 S.pop(); } }}
后序遍历:对于当前节点,先输出它的左孩子,然后输出它的右孩子,最后输出该节点
递归版本:
/* 后序遍历递归版 */void PostOrderRec(Node * node){ if (node == nullptr) return; PostOrderRec(node->left); //先输出左孩子 PostOrderRec(node->right); //然后输出右孩子 cout << node->data << " "; //最后输出当前节点}非递归版本:
/* 后序遍历非递归版 */void PostOrderNonRec(Node * node){ Node * pre = nullptr; Node * cur = node; stack<Node*> S; S.push(cur); while (!S.empty()) { cur = S.top(); if ((!cur->left && !cur->right) || //第一个输出的必是无左右孩子的叶子节点,只要第一个节点输出, (pre && (pre == cur->left || pre == cur->right))) //以后的pre就不会是空。此处的判断语句加入一个pre,只是用来 { //确保可以正确输出第一个节点。 cout << cur->data << " "; //左右孩子都全部输出,再输出当前节点 pre = cur; S.pop(); } else { if (cur->right) S.push(cur->right); //先进右孩子,再进左孩子,取出来的才是左孩子 if (cur->left) S.push(cur->left); } }}
(2):层次遍历;
层次遍历:广度优先算法,分层输出节点,使用队列的先进先出特性保存头节点,把当前层都压入队列后依次出列输出。
void LevelOrder(Node * node){ Node * p = node; queue<Node*> Q; //队列 Q.push(p); while (!Q.empty()) { p = Q.front(); cout << p->data << " "; Q.pop(); if (p->left) Q.push(p->left); //注意顺序,先进左孩子 if (p->right) Q.push(p->right); }}最大深度:
(3):求树的节点数;
树的节点数采用递归方式:
int CountNodes(Node * node){ if (node == nullptr) return 0; return CountNodes(node->left) + CountNodes(node->right) + 1;}
(4):求树的叶子数;
树的叶子:和树的节点数有所不同,更改递归返回点。
int CountLeaves(Node * node){ if (node == nullptr) return 0; if (!node->left && !node->right) return 1; return CountLeaves(node->left) + CountLeaves(node->right);}
(5):求树的深度;
树的深度:
采用递归方式:
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); }
可以采用广度优先的方法计算,输出一层后计数,最后得到的数字即为最大层数。
int GetDepth(TreeNode *root) { if (!root) { return 0; } int curDep = 0; queue<TreeNode *>queuelist; queuelist.push(root); TreeNode *curNode; while (!queuelist.empty()) { int size = queuelist.size(); for (int i = 0; i < size; i++) { curNode = queuelist.front(); queuelist.pop(); if (curNode->left) { queuelist.push(curNode->left); } if (curNode->right) { queuelist.push(curNode->right); } } curDep++; } return curDep;}(6):求二叉树第 k 层的节点个数;
第K层节点个数:递归控制,每次向下一层则K减1,直到到达K层。
int GetKLevel(Node * node, int k){ if (node == nullptr) return 0; if (k == 1) return 1; return GetKLevel(node->left, k - 1) + GetKLevel(node->right, k - 1);}非递归方式,采用和树的深度相同的思想:
int GetKLevel(TreeNode *root, int K) { if (!root) { return 0; } int curDep = 0; queue<TreeNode *>queuelist; queuelist.push(root); TreeNode *curNode; while (!queuelist.empty()) { int size = queuelist.size(); if ( k == curDep ) { return size; } for (int i = 0; i < size; i++) { curNode = queuelist.front(); queuelist.pop(); if (curNode->left) { queuelist.push(curNode->left); } if (curNode->right) { queuelist.push(curNode->right); } } curDep++; } return 0;}
(7):判断两棵二叉树是否结构相同;
结构相同:不考虑数字是否相同,只考虑左右子树是否一致。递归方法解最简单。
bool StructureCmp(Node * node1, Node * node2){ if (node1 == nullptr && node2 == nullptr) return true; else if (node1 == nullptr || node2 == nullptr) return false; return StructureCmp(node1->left, node2->left) && Str1uctureCmp(node1->right, node2->right);}
(8):求二叉树的镜像;
镜像:左右翻转。
递归方式:
void Mirror(Node * node){ if (node == nullptr) return; Node * temp = node->left; node->left = node->right; node->right = temp; Mirror(node->left); Mirror(node->right);}
(9):求两个节点的最低公共祖先节点;
最低公共祖先:需要注意两个节点本来就在一个树上的情况,此时最低公共祖先就是其中靠近根节点的那个。
递归方式:
Node * FindLCA(Node * node, Node * target1, Node * target2){ if (node == nullptr) return nullptr; if (node == target1 || node == target2) return node; Node * left = FindLCA(node->left, target1, target2); Node * right = FindLCA(node->right, target1, target2); if (left && right) //分别在左右子树 return node; return left ? left : right; //都在左子树或右子树}(10):求任意两节点距离;
任意两节点距离:两节点到最低公共祖先的距离之和。
int FindLevel(Node * node, Node * target){ if (node == nullptr) return -1; if (node == target) return 0; int level = FindLevel(node->left, target); //先在左子树找 if (level == -1) level = FindLevel(node->right, target); //如果左子树没找到,在右子树找 if (level != -1) //找到了,回溯 return level + 1; return -1; //如果左右子树都没找到}int DistanceNodes(Node * node, Node * target1, Node * target2){ Node * lca = FindLCA(node, target1, target2); //找到最低公共祖先节点 int level1 = FindLevel(lca, target1); int level2 = FindLevel(lca, target2); return level1 + level2;}(11):找出二叉树中某个节点的所有祖先节点;
所有祖先节点:
bool FindAllAncestors(Node * node, Node * target){ if (node == nullptr) return false; if (node == target) return true; if (FindAllAncestors(node->left, target) || FindAllAncestors(node->right, target)) //找到了 { cout << node->data << " "; return true; //回溯 } return false; //如果左右子树都没找到}(12):二叉树前序中序推后序;
/* 前序遍历和中序遍历结果以长度为n的数组存储,pos1为前序数组下标,pos2为后序下标 */int pre_order_arry[n];int in_order_arry[n];void PrintPostOrder(int pos1, int pos2, int n){ if (n == 1) { cout << pre_order_arry[pos1]; return; } if (n == 0) return; int i = 0; for (; pre_order_arry[pos1] != in_order_arry[pos2 + i]; i++) ; PrintPostOrder(pos1 + 1, pos2, i); PrintPostOrder(pos1 + i + 1, pos2 + i + 1, n - i - 1); cout << pre_order_arry[pos1];}(13):判断二叉树是不是平衡二叉树;
平衡二叉树:一棵高度平衡的二叉树的定义是:一棵二叉树中每个节点的两个子树的深度相差不会超过1。
int getDepth (TreeNode *root) { if(!root) return 0; int left = getDepth(root->left); int right = getDepth(root->right); return ((left>right)?left:right)+1; } bool isBalanced(TreeNode *root) { // write your code here if(!root) return true; int left = getDepth(root->left); int right = getDepth(root->right); if((left > right + 1)||(right > left+1)) {
return false;
} else{ return isBalanced(root->left)&&isBalanced(root->right); } }
(14):判断二叉树是不是完全二叉树;
完全二叉树:除最后一层外,每一层上的结点数均达到最大值;在最后一层上只缺少右边的若干结点。
bool IsCBT(Node * node){ bool flag = false; queue<Node*> Q; Q.push(node); while (!Q.empty()) { Node * p = Q.front(); Q.pop(); if (flag) { if (p->left || p->right) return false; } else { if (p->left && p->right) { Q.push(p->left); Q.push(p->right); } else if (p->right) //只有右节点 return false; else if (p->left) //只有左节点 { Q.push(p->left); flag = true; } else //没有节点 flag = true; } } return true;}flag用来标识是否已经出现了第一个没有右孩子的节点,如果出现了则认为是倒数第二行,然后依次判断倒数第一行的各个节点还有没有孩子。
(15):判断是否是二叉查找树的后序遍历结果;
int array[n]; //长度为n的序列,注意begin和end遵循的是左闭右闭原则bool IsSequenceOfBST(int begin, int end){ if (end - begin <= 0) return true; int root_data = array[end]; //数组尾元素为根节点 int i = begin; for (; array[i] < root_data; i++); //取得左子树 int j = i; for (; j < end; j++) if (array[j] < root_data) //此时右子树应该都大于根节点;若存在小于,直接return false return false; return IsSequenceOfBST(begin, i - 1) && IsSequenceOfBST(i, end - 1); //左右子树是否都满足}(16):给定一个二叉查找树中的节点,找出在中序遍历下它的后继和前驱;
查找后续节点:
如果节点中有指向父亲节点的指针(假如根节点的父节点为 nullptr),则:
(1):如果当前节点有右孩子,则后继节点为这个右孩子的最左孩子;
(2):如果当前节点没有右孩子;
(2.1):当前节点为根节点,返回 nullptr;
(2.2):当前节点只是个普通节点,也就是存在父节点;
(2.2.1):当前节点是父亲节点的左孩子,则父亲节点就是后继节点;
(2.2.2):当前节点是父亲节点的右孩子,沿着父亲节点往上走,直到 n-1 代祖先是 n 代祖先的左孩子,则后继为 n 代祖
先或遍历到根节点也没找到符合的,则当前节点就是中序遍历的最后一个节点,返回 nullptr。
Node * Increment(Node * node){ if (node->right) { node = node->right; while (node->left) node = node->left; } else { Node * p = node->parent; while (p && p->right == node) { node = p; p = p->parent; } node = p; } return node;}
(17):二分查找树转化为排序的循环双链表;
循环双链表:使用中序遍历的方法输出索引数组,再进行组合可以达目的,时间O(n),空间O(n);
如果限制使用的空间,则需要在遍历的同时构建链表,使用递归方法:
/* 合并两个a,b两个循环双向链表 */Node * Append(Node * a, Node * b){ if (a == nullptr) return b; if (b == nullptr) return a; //分别得到两个链表的最后一个元素 Node * a_last = a->left; Node * b_last = b->left; //将两个链表头尾相连 a_last->right = b; b->left = a_last; a->left = b_last; b_last->right = a; return a;}/* 递归的解决二叉树转换为双链表 */Node * TreeToList(Node * node){ if (node == nullptr) return nullptr; //递归解决子树 Node * left_list = TreeToList(node->left); Node * right_list = TreeToList(node->right); //把根节点转换为一个节点的双链表。方便后面的链表合并 node->left = node; node->right = node; //合并之后即为升序排列 left_list = Append(left_list, node); left_list = Append(left_list, right_list); return left_list;}
(18):二叉树路径等于目标值的所有路径;
路径和:最容易想到的办法就是递归,需要注意传入的参数是引用。
class Solution {public: /** * @param root the root of binary tree * @param target an integer * @return all valid paths */ void path(vector<vector<int>>&ve, vector<int> v,int val, TreeNode* root){ if(root==NULL)return; val=val-root->val; v.push_back(root->val); if(val==0&&root->left==NULL&&root->right==NULL){ ve.push_back(v); return; } path(ve,v,val,root->left); path(ve,v,val,root->right); } vector<vector<int>> binaryTreePathSum(TreeNode *root, int target) { // Write your code here vector<vector<int>> ve; vector<int>v; path(ve,v,target,root); return ve; }};
(19):二叉树的所有路径;
vector<string> binaryTreePaths(TreeNode* root) { // Write your code here vector<string> res; if(root==NULL) return res; binaryTreePathsCore(root,res,to_string(root->val)); return res;}void binaryTreePathsCore(TreeNode* root,vector<string> &str,string strpath){ if(root->left==NULL&&root->right==NULL)//叶子结点 { str.push_back(strpath); return; } if(root->left!=NULL) binaryTreePathsCore(root->left,str,strpath+"->"+to_string(root->left->val)); if(root->right!=NULL) binaryTreePathsCore(root->right,str,strpath+"->"+to_string(root->right->val));}
- 二叉树的各种操作(遍历/深度/距离/转换)
- 遍历二叉树的各种操作(非递归遍历)
- 遍历二叉树的各种操作(非递归遍历)
- 遍历二叉树的各种操作(非递归遍历)
- 遍历二叉树的各种操作(非递归遍历)
- 遍历二叉树的各种操作(非递归遍历)
- 遍历二叉树的各种操作(非递归遍历)
- 遍历二叉树的各种操作(非递归遍历)
- 遍历二叉树的各种操作(非递归遍历)
- 遍历二叉树的各种操作(非递归遍历)
- 遍历二叉树的各种操作(非递归遍历)
- 遍历二叉树的各种操作(非递归遍历)
- 遍历二叉树的各种操作(非递归遍历)
- 遍历二叉树的各种操作(非递归遍历)
- 遍历二叉树的各种操作(非递归遍历)
- 遍历二叉树的各种操作(非递归遍历)
- 遍历二叉树的各种操作(非递归遍历)
- 遍历二叉树的各种操作(非递归遍历)
- 图解PCIE原理(从软件角度)
- 持续集成之jenkins实践教程:基础篇(1): 邮件设置
- ORB-SLAM(四)追踪
- 时间戳相减的几种方法
- ORB-SLAM(五)优化
- 二叉树的各种操作(遍历/深度/距离/转换)
- ORB-SLAM(六)回环检测
- zoj 3509
- 最长公共子串与最长公共子序列
- FreeImage 3.17.0 在VS2015下编译及遇到问题解决
- pcie 驱动程序分析
- JAVA选择排序
- js日历控件
- struts2粗略总结