二叉树非递归遍历
先序
头结点入栈,每次循环顺着左路一直找到底,先打印结点,遇右压栈就可以了。
void PreOrderNorOP(pBTNode pRoot)
{
Stack s;
init(&s);
pushBack(&s, pRoot);
while (!isEmpty(&s)) {
pBTNode pCur = top(&s);
pop(&s);
while (pCur) {
printf("%c ", pCur->_data);
if (pCur->_pRight) {
pushBack(&s, pCur->_pRight);
}
pCur = pCur->_pLeft;
}
}
}
解法二:类似于层序遍历的写法
void PreOrderNor(pBTNode pRoot)
{
Stack s;
init(&s);
pushBack(&s, pRoot);
while (!isEmpty(&s)) {
pBTNode pCur = top(&s);
pop(&s);
printf("%c ", pCur->_data);
if (pCur->_pRight)
pushBack(&s, pCur->_pRight);
if (pCur->_pLeft)
pushBack(&s, pCur->_pLeft);
}
}
中序
用pCur指向当前处理的结点,向左找到底,压栈保存沿途路径。
void InOrderNor(pBTNode pRoot)
{
if (pRoot) {
Stack s;
init(&s);
pBTNode pCur = pRoot;
while (!isEmpty(&s) || pCur) {
//找到最左结点
while (pCur) {
pushBack(&s, pCur);
pCur = pCur->_pLeft;
}
//无左结点,打印出栈
pCur = top(&s);
printf("%c ", pCur->_data);
pop(&s);
pCur = pCur->_pRight;
}
}
}
后序
后序就比较麻烦了。只用一个pCur完成不了任务了,因为如果像中序最后一句返回当前栈顶元素的右孩子,而下次循环还需要判断栈顶(父节点)和右孩子的关系,就会造成死循环。
所以用三个指针。
pCur:迭代临时指针,每次找到左路最底的NULL。仅在遇右子树时改变。
pTop: 代码围绕栈顶元素来写。
pPre:记录上一个打印的结点。例如在C-》F的时候需要判断右子树走过没,如果打印过F了,就记录下来。
void PostOrderNor(pBTNode pRoot)
{
if (pRoot) {
Stack s;
init(&s);
pBTNode pCur = pRoot;
pBTNode pPre = NULL;
pBTNode pTop = NULL;
while (!isEmpty(&s) || pCur) {
while (pCur) {
pushBack(&s, pCur);
pCur = pCur->_pLeft;
}
pTop = top(&s);
//判断如果栈顶结点有右结点且没走过,遍历右子树
if (pTop->_pRight && pPre != pTop->_pRight) {
pCur = pTop->_pRight;
}
else {
//左右子树都遍历过了,打印根节点
pPre = pTop;
printf("%c ", pTop->_data);
pop(&s);
//pCur = top(&s)->_pRight;右子树退回来死循环
}
}
}
}
判断是否完全二叉树——层序
找到第一个不饱和结点(无右,或者无左无右),找到后置标志,队列中其后结点不能有子节点。
int IsComTree(pBTNode pRoot)
{
LQueue q;
InitQueue(&q);
PushQueue(&q, pRoot);
int flag = 0;
while (!QueueEmpyt(&q)) {
pBTNode pCur = QueueFront(&q);
PopQueue(&q);
if (flag == 1) {
if (pCur->_pLeft || pCur->_pRight) {
return 0;
}
}
if (pCur->_pLeft && pCur->_pRight) {
//正常的双子结点
PushQueue(&q, pCur->_pLeft);
PushQueue(&q, pCur->_pRight);
}
else if (pCur->_pLeft) {
//第一个不饱和结点
flag = 1;
PushQueue(&q, pCur->_pLeft);
}
else if ((NULL == pCur->_pLeft && NULL == pCur->_pRight)) {
//也不饱和,但子节点不入队列
flag = 1;
}
else if(pCur->_pRight){
//没有左子树,有右子数
return 0;
}
}
return 1;
}
判断结点是否在树里
1.传值
pBTNode IsDataInTree(pBTNode pRoot, BTDataType data)
{
if (pRoot) {
if (pRoot->_data == data) {
return pRoot;
}
pBTNode pRet = NULL;
if (pRet = IsDataInTree(pRoot->_pLeft, data)) {
return pRet;
}
if (pRet = IsDataInTree(pRoot->_pRight, data)) {
return pRet;
}
}
return NULL;
}
2.传值
int IsNodeInTree(pBTNode pRoot, pBTNode node)
{
if (pRoot) {
if (pRoot == node) {
return 1;
}
if (IsNodeInTree(pRoot->_pLeft, node)) {
return 1;
}
if (IsNodeInTree(pRoot->_pRight, node)) {
return 1;
}
}
return 0;
}
求两个结点的最低公共祖先
普通树
/*递归求两个结点最低公共祖先
若两个结点有一个是根,则根节点是公共祖先。
若两个结点都在左子树,则在左子树找,同理在右子树找。若一左一右,则root是公共祖先
*/
pBTNode ComAscNodeR(pBTNode root, pBTNode n1, pBTNode n2)
{
if (root == NULL) {
return NULL;
}
if (root == n1 || root == n2) {
return root;
}
if (IsNodeInTree(root->_pLeft, n1) && IsNodeInTree(root->_pLeft, n2)) {
return ComAscNodeR(root->_pLeft, n1, n2);
}
else if(IsNodeInTree(root->_pRight, n1) && IsNodeInTree(root->_pRight, n2)){
return ComAscNodeR(root->_pRight, n1, n2);
}
else {
return root;
}
}
/*
非递归求两个结点最低公共祖先
记录从根节点到n1的路径,n2的路径。先出栈路径较长的至两个路径等长,之后同时出栈,第一个相等则为祖先
*/
pBTNode ComAscNodeNor(pBTNode root, pBTNode n1, pBTNode n2)
{
Stack s1;
Stack s2;
init(&s1);
init(&s2);
GetPath(&s1, root, n1);
GetPath(&s2, root, n2);
int size1 = stackSize(&s1);
int size2 = stackSize(&s2);
while (size1 > size2) {
pop(&s1);
size1--;
}
while (size1 < size2) {
pop(&s2);
size2--;
}
while (!isEmpty(&s1) && top(&s1)!=top(&s2)) {
pop(&s1);
pop(&s2);
}
return top(&s1);
}
int GetPath(Stack* s, pBTNode root, pBTNode node) {
if (root) {
pushBack(s, root);
if (root == node) {
return 1;
}
if (GetPath(s, root->_pLeft, node) || GetPath(s, root->_pRight, node)) {
return 1;
}
pop(s);
}
return 0;
}
二叉搜索树
/*
二叉搜索树找最低公共祖先
如果有一个结点值等于根,则root是祖先。若两个都比根小则祖先在左子树,若一个大一个小,则是root。
*/
pBTNode ComAscNodeSearch(pBTNode root, pBTNode n1, pBTNode n2)
{
if (NULL == root) {
return NULL;
}
if (n1->_data == root->_data || n2->_data == root->_data) {
return root;
}
if (n1->_data < root->_data && n2->_data < root->_data) {
return ComAscNodeR(root->_pLeft, n1, n2);
}
else if (n1->_data > root->_data && n2->_data > root->_data) {
return ComAscNodeR(root->_pRight, n1, n2);
}
else {
return root;
}
}
三叉树,每个结点包含parent指针。
转化为从node1,node2到root结点的两条链表求交点的问题。求出len1,len2,较长的先走几步至两个链表同长度,若结点相同则为交点,不相同则两链表指针各前进一步。
判断一棵二叉树是否是平衡二叉树
//含有重复递归
int IsBalance(pBTNode root)
{
if (root == NULL) {
return 1;
}
int left = Height(root->_pLeft);
int right = Height(root->_pRight);
int diff = left - right;
if (diff > 1 && diff < -1) {
return 0;
}
return IsBalance(root->_pLeft)&&IsBalance(root->_pRight);
}
//后序思想,精髓在于每次向下递之前用变量记录归回时的值。
int IsBalanceOP(pBTNode root, int* depth)
{
if (root == NULL) {
*depth = 0;
return 1;
}
int left, right;
if (IsBalanceOP(root->_pLeft, &left) && IsBalanceOP(root->_pRight, &right)) {
int diff = left - right;
if (diff <= 1 && diff >= -1) {
*depth = left > right ? left + 1 : right + 1;
return 1;
}
}
return 0;
}
前序遍历和中序遍历重建二叉树
//此题过程复杂,必须画图仔细掌握一次递归的细节过程
pBTNode _RebuildTree(BTDataType* preStart, BTDataType* preEnd, BTDataType* inStart, BTDataType* inEnd);
pBTNode RebuildTree(BTDataType* preOrder, BTDataType* inOrder, int length)
{
if (preOrder == NULL || inOrder == NULL ) {
return NULL;
}
return _RebuildTree(preOrder, preOrder + length - 1, inOrder, inOrder + length - 1);
}
pBTNode _RebuildTree(BTDataType* preStart, BTDataType* preEnd, BTDataType* inStart, BTDataType* inEnd)
{
//创建当前根节点
pBTNode pNew = BuyBinTreeNode(preStart[0]);
//在中序里找到根节点
BTDataType* pRoot = inStart;
while (pRoot != inEnd && *pRoot != *preStart ) {
pRoot++;
}
if (pRoot == inEnd && *pRoot != *preStart) {
exit(1);
}
//计算中序左子树节点数
int leftLength = pRoot - inStart;
//计算前序左子树节点数
BTDataType* preLeft = preStart + leftLength;
if (leftLength > 0) {
//当前根节点还有左子树
pNew->_pLeft = _RebuildTree(preStart + 1, preLeft, inStart, pRoot - 1);
}
//重点!当上一个根节点左子树的节点数 > 当前根节点左子树节点,证明当前根节点有右子树
if (preEnd - preStart > leftLength) {
pNew->_pRight = _RebuildTree(preLeft + 1, preEnd, pRoot + 1, inEnd);
}
return pNew;
}
c++版本思路
/* 先序遍历第一个位置肯定是根节点node,
中序遍历的根节点位置在中间p,在p左边的肯定是node的左子树的中序数组,p右边的肯定是node的右子树的中序数组
另一方面,先序遍历的第二个位置到p,也是node左子树的先序子数组,剩下p右边的就是node的右子树的先序子数组
把四个数组找出来,分左右递归调用即可
struct TreeNode* reConstructBinaryTree(vector<int> pre, vector<int> in){
//获得序列的长度
//分别存储先序序列的左子树,先序序列的右子树,中序序列的左子树,中序序列的右子树
//先序遍历第一个位置肯定是根节点node,取其值
//新建一个树结点,并传入结点值
//p用于存储中序序列中根结点的位置
//建立中序序列的左子树和前序序列的左子树
//前序第一个为根节点,+1从下一个开始记录
//建立中序序列的右子树和前序序列的右子树
//取出前序和中序遍历根节点左边和右边的子树
//递归,再对其进行上述所有步骤,即再区分子树的左、右子子数,直到叶节点
}
*/