二叉树-经典问题复习

节点定义

typedef struct Btree {
    int v;
    struct Btree *left;
    struct Btree *right;
}*Btree;


插入节点

Btree insertNode(Btree root, int value) {
    Btree ptr=root;
    Btree tmpNode;
    Btree newNode = malloc(sizeof(Btree));
    newNode->v = value;
    newNode->left = NULL;
    newNode->right = NULL;
    
    if (ptr == NULL)
        return newNode;
    while (ptr != NULL) {
        tmpNode = ptr;
        if (ptr->v < value)
            ptr = ptr->right;
        else
            ptr = ptr->left;
    }
    if (tmpNode->v < value)
        tmpNode->right = newNode;
    else
        tmpNode->left = newNode;
    return root;
}

建立二叉树

Btree BtreeCreate(){
    Btree root = NULL;
    int value;
    printf("Please insert nodes(end with zero) %d:\n",&value);

    while (value != 0){
        root = insertNode(root,value);
        scanf("%d",&value);
    }
    return root;
}

求二叉树节点个数

递归解法:
(1)如果二叉树为空,节点个数为0
(2)如果二叉树不为空,二叉树节点个数 = 左子树节点个数 + 右子树节点个数 + 1

int getNodeNum(Btree root){
    if (root == NULL)
        return 0;
    return getNodeNum(root->left)+getNodeNum(root->right)+1;
}

求二叉树深度

递归解法:
(1)如果二叉树为空,二叉树的深度为0
(2)如果二叉树不为空,二叉树的深度 = max(左子树深度, 右子树深度) + 1
int getDepth(Btree root){
        if (root ==NULL)
            return0;
        int maxLeft = getDepth(root->left);
        int maxRight = getDepth(root->right);
        return maxLeft<maxRight? (maxRight+1) : (maxLeft+1);
}

前序中序后序遍历

 前序遍历递归解法
(1)如果二叉树为空,空操作
(2)如果二叉树不为空,访问根节点,前序遍历左子树,前序遍历右子树
void preOrderTranverse(Btree root){
    if (root == NULL)
        return ;
    visit(root);
    preOrderTranverse(root->left);
    preOrderTranverse(root->right);
}
中序遍历递归解法
(1)如果二叉树为空,空操作。
(2)如果二叉树不为空,中序遍历左子树,访问根节点,中序遍历右子树
void inOrderTranverse(Btree root){
    if (root == NULL)
        return;
    inOrderTranverse(root->left);
    visit(root);
    inOrderTranverse(root->right);
}
后序遍历递归解法
(1)如果二叉树为空,空操作
(2)如果二叉树不为空,后序遍历左子树,后序遍历右子树,访问根节点
void postOrderTranverse(Btree root){
    if (root == NULL)
        return;
    postOrderTranverse(root->left);
    postOrderTranverse(root->right);
    visit(root);
}

分层遍历二叉树(按层次从左到又 从上到下)

相当于广度优先搜索,使用队列实现。队列初始化,将根节点压入队列。当队列不为空,进行如下操作:弹出一个节点,访问,若左子节点或右子节点不为空,将其压入队列。
void levelTranverse(Btree root){
    if (root == NULL)
        return;
    queue<Btree> treeQueue;
    treeQueue.push(root);
    while(treeQueue.empty()!=0){
        Btree tmp = treeQueue.front();
        treeQueue.pop();
        visit(tmp);
        if (tmp->left!=NULL)
            treeQueue.push(tmp->left);
        if (tmp->right!=NULL)
            treeQueue.push(tmp->right);
    }
}

二叉查找树变为有序的双向链表

要求不能创建新节点,只调整指针。
递归解法:
(1)如果二叉树查找树为空,不需要转换,对应双向链表的第一个节点是NULL,最后一个节点是NULL
(2)如果二叉查找树不为空:
如果左子树为空,对应双向有序链表的第一个节点是根节点,左边不需要其他操作;
如果左子树不为空,转换左子树,二叉查找树对应双向有序链表的第一个节点就是左子树转换后双向有序链表的第一个节点,同时将根节点和左子树转换后的双向有序链  表的最后一个节点连接;
如果右子树为空,对应双向有序链表的最后一个节点是根节点,右边不需要其他操作;
如果右子树不为空,对应双向有序链表的最后一个节点就是右子树转换后双向有序链表的最后一个节点,同时将根节点和右子树转换后的双向有序链表的第一个节点连接。
Btree BtreeToList(Btree root){
	Btree head,tail;
	BtreeToListRwcusion(root,head,tail);
	return head;
}
void BtreeToListRecusion(Btree root, Btree head, Btree tail) {
	if (root == NULL){
		head = NULL;
		tail = NULL;
		return;
	}
	Btree lt,rt;
	BtreeToList(root->left,head,lt);
	BtreeToList(root->right,rt,tail);
	
	if (root->left != NULL){
		root->ledt = lt;
		lt->right = root;
	}
	else
		head = root;
	if (root->right != NULL){
		rt->left = root;
		root->right = rt;
	}
	else 
		root = tail;
}

求二叉树第K层节点数

递归解法:
(1)如果二叉树为空或者k<1返回0
(2)如果二叉树不为空并且k==1,返回1
(3)如果二叉树不为空且k>1,返回左子树中k-1层的节点个数与右子树k-1层节点个数之和

int getNodeNumKthLevel(Btree root, int k){
    if (root == NULL)
        return 0;
    if (k == 1)
        return 1;
    int leftNum = getNodeNumKthLevel(root->left,k-1);
    int rightNum = getNodeNumKthLevel(root->right,k-1);
    return leftNum + rightNum;
}

求二叉树叶子节点个数

递归解法:
(1)如果二叉树为空,返回0
(2)如果二叉树不为空且左右子树为空,返回1
(3)如果二叉树不为空,且左右子树不同时为空,返回左子树中叶子节点个数加上右子树中叶子节点个数
int getAllLeafNodes(Btree root){
    if (root == NULL)
        return;
    if (root->left == NULL && root->right == NULL){
        return 1;
    }
    int leftNum = getAllLeafNodes(root->left);
    int rightNum = getAllLeafNodes(root->right);
    return leftNum + rightNum;
}

判断两棵二叉树是否结构相同

不考虑数据内容。结构相同意味着对应的左子树和对应的右子树都结构相同。
递归解法:
(1)如果两棵二叉树都为空,返回真
(2)如果两棵二叉树一棵为空,另一棵不为空,返回假
(3)如果两棵二叉树都不为空,如果对应的左子树和右子树都同构返回真,其他返回假

注意:Unix用户编译以下程序需要加上头文件 stdbool.h(C99标准)

bool structIosmor(Btree rootFirst, Btree rootSecond){
    if (rootFirst == NULL && rootSecond == NULL)
        return true;
    if (rootFirst == NULL || rootSecond == NULL)
        return false;
    bool first = structIosmor(rootFirst->left,rootSecond->left);
    bool second = structIosmor(rootFirst->right,rootSecond->right);
    return first&&second;
}


判断二叉树是不是平衡二叉树

递归解法:
(1)如果二叉树为空,返回真
(2)如果二叉树不为空,如果左子树和右子树都是AVL树并且左子树和右子树高度相差不大于1,返回真,其他返回假

bool isAVL(Btree root, int height){
    if (root == NULL){
        height = 0;
        return true;
    }
    int heightLeft;
    bool resultLeft = isAVL(root->left,heightLeft);
    int heightRight;
    bool resultRight = isAVL(root->right,heightRight);

    if (resultLeft && resultRight && abs(heightLeft-heightRight)<=1){
        height = max(heightLeft,heightRight)+1;
        return true;
    }
    else{
        height = max(heightLeft,heightRight)+1;
        return false;
    }
}

二叉树的镜像

递归解法:
(1)如果二叉树为空,返回空
(2)如果二叉树不为空,求左子树和右子树的镜像,然后交换左子树和右子树

Btree treeImage(Btree root){
    if (root == NULL)
        return  NULL;
    Btree leftImage = treeImage(root->left);
    Btree rightImage = treeImage(root->right);
    root->left = rightImage;
    root->right = leftImage;
    return root;
}

求二叉树中两个节点最低的公共祖先节点

递归解法(效率太低):
(1)如果两个节点分别在根节点的左子树和右子树,则返回根节点
(2)如果两个节点都在左子树,则递归处理左子树;如果两个节点都在右子树,则递归处理右子树

bool findNode(Btree root, Btree node){
    if (root == NULL || node == NULL)
        return false;
    if (root == node)
        return true;
    bool found = findNode(root->left,node);
    if(!found){
         found = findNode(root->right,node);
    }
    return found;
}

Btree findLastParent(Btree root, Btree node1, Btree node2){
   if (root == NULL || node1 == NULL || node2 == NULL){
        return NULL;
   }
   if (findNode(root->left,node1)){
        if (findNode(root->right,node2)){
            return root;       
         }
        else{
         return findLastParent(root->left,node1,node2);  
        }
   }
   else{
        if (findNode(root->left,node2))
            return root;
        else
            return findLastParent(root->right,node1,node2);
   }
}

非递归解法:
先求从根节点到两个节点的路径,然后再比较对应路径的节点就行,最后一个相同的节点也就是他们在二叉树中的最低公共祖先节点

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;
}

求二叉树节点的最大距离

即二叉树中相距最远的两个节点之间的距离。
递归解法:
距离最远的两点必然在以某个节点A为根的子树上,它们间的路径必然经过该子树的根节点A。因而,以任意一个节点B为根的子树,计算出经过该子树根节点B的最大距离,则所有最大距离的最大值就是所要求的二叉树的最大距离,即“树的直径”。而经过树的根节点的最大距离为:左子树的高度+右子树的高度+2(假设空节点的高度为-1),因而,原问题等同于“计算每个节点的左子树和右子树的高度和,取最大值”C语言是不支持引用的,需要用支持C++的编译器。
int treeDepth(Btree root,int& maxDistance){
    if (root == NULL)
        return -1;
    int leftDepth = treeDepth(root->left,maxDistance) + 1;
    int rightDepth = treeDepth(root->right,maxDistance) + 1;
    int distance = leftDepth + rightDepth;
    if (maxDistance < distance)
        maxDistance = distance;
    return max(leftDepth,rightDepth);
}
int findTreeDiameter(Btree root){
    if (root == NULL)
        return 0;
    int maxDistance = 0;
    treeDepth(root,maxDistance);
    return maxDistance;
}

由前序遍历和中序遍历重构二叉树

二叉树前序遍历序列中,第一个元素总是树的根节点的值。中序遍历序列中,左子树的节点的值位于根节点的值的左边,右子树的节点的值位
于根节点的值的右边。
递归解法:
(1)如果前序遍历为空或中序遍历为空或节点个数小于等于0,返回NULL。
(2)创建根节点。前序遍历的第一个数据就是根节点的数据,在中序遍历中找到根节点的位置,可分别得知左子树和右子树的前序和中序遍
历序列,重建左右子树。

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;
}

判断二叉树是不是完全二叉树

若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全
二叉树。
有如下算法,按层次(从上到下,从左到右)遍历二叉树,当遇到一个节点的左子树为空时,则该节点右子树必须为空,且后面遍历的节点左
右子树都必须为空,否则不是完全二叉树。

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;
}




  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值