剑指offer面试题6

面试题6: 重建二叉树

题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含有重复的数字。例如输入前序遍历序列{1, 2,4,7,3,5,6,8} 和中序遍历序列{4,7,2,1,5,3,8,6},则重建出如下图所示的二叉树并输出它的头结点。


预备知识

树的特点:根结点没有父节点,根节点外的每一个结点都只有一个父节点;叶结点没有子结点,叶结点外的所有结点都有一个或多个子结点;父节点和子结点之间用指针链接

二叉树:二叉树是一种特殊的树结构,它的每个结点最多只有两个子结点。定义为:

struct BinaryTreeNode 
{
    int                    m_nValue; 
    BinaryTreeNode*        m_pLeft;  
    BinaryTreeNode*        m_pRight; 
};

二叉树的操作:遍历(前序遍历【根结点-左子结点-右子结点】;中序遍历【左子结点-根结点-右子结点】;后序遍历【左子结点-右子结点-根结点】 ——根据根节点的访问顺序定义),其中每一种遍历都有两种不同的实现方法-【递归】和【循环】,这样3中遍历就有6中实现;另外,递归实现要比循环实现要简捷很多。除此以外,还有一种遍历是:宽度优先遍历(即一层一层的访问结点,同一层以从左到右访问)、创建、销毁等。

//创建二叉树
BinaryTreeNode* CreateBinaryTreeNode(int value)
{
    BinaryTreeNode* pNode = new BinaryTreeNode(); //分配内存空间
    pNode->m_nValue = value;    //给结点赋值
    pNode->m_pLeft = NULL;      //指向左子结点
    pNode->m_pRight = NULL;     //指向右子结点

    return pNode;
}

//连接树结点
void ConnectTreeNodes(BinaryTreeNode* pParent, BinaryTreeNode* pLeft, BinaryTreeNode* pRight)
{
    if(pParent != NULL)
    {
        pParent->m_pLeft = pLeft;   //左子结点
        pParent->m_pRight = pRight; //右子结点
    }
}

//输出单个树的结点,同时输出它的子结点
void PrintTreeNode(BinaryTreeNode* pNode)
{
    if(pNode != NULL)
    {
        printf("value of this node is: %d\n", pNode->m_nValue);

        if(pNode->m_pLeft != NULL)
            printf("value of its left child is: %d.\n", pNode->m_pLeft->m_nValue);
        else
            printf("left child is null.\n");

        if(pNode->m_pRight != NULL)
            printf("value of its right child is: %d.\n", pNode->m_pRight->m_nValue);
        else
            printf("right child is null.\n");
    }
    else
    {
        printf("this node is null.\n");
    }

    printf("\n");
}

//遍历并输出树的所有结点,递归调用
void PrintTree(BinaryTreeNode* pRoot)
{
    PrintTreeNode(pRoot);

    if(pRoot != NULL)
    {
        if(pRoot->m_pLeft != NULL)
            PrintTree(pRoot->m_pLeft);

        if(pRoot->m_pRight != NULL)
            PrintTree(pRoot->m_pRight);
    }
}

//销毁树结点,递归销毁所有树结点
void DestroyTree(BinaryTreeNode* pRoot)
{
    if(pRoot != NULL)
    {
        BinaryTreeNode* pLeft = pRoot->m_pLeft;
        BinaryTreeNode* pRight = pRoot->m_pRight;

        delete pRoot;
        pRoot = NULL;

        DestroyTree(pLeft);
        DestroyTree(pRight);
    }
}

二叉搜索树:左子结点总是小于或等于根结点,而右子结点总是大于或等于根结点

二叉树特例——堆:堆分为最大堆(特点是:根结点的值最大)和最小堆(特点是:根节点的值最小),很多需要快速找到最大值或最小值的问题都可以用堆解决。

二叉树特例——红黑树:把树中的结点定义为红、黑两种颜色,并通过规则确保从根结点到叶结点的最长路径的长度不超过最短路径的两倍;C++STL中的set、multiset、map、multimap等数据结构都是基于红黑树实现的。

代码:

BinaryTree.h

#pragma once

struct BinaryTreeNode 
{
    int                    m_nValue;  //结点值
    BinaryTreeNode*        m_pLeft;   //指向左子结点
    BinaryTreeNode*        m_pRight;  //指向右子结点
};

__declspec( dllexport ) BinaryTreeNode* CreateBinaryTreeNode(int value);  //创建树结点
__declspec( dllexport ) void ConnectTreeNodes(BinaryTreeNode* pParent, BinaryTreeNode* pLeft, BinaryTreeNode* pRight); //连接结点
__declspec( dllexport ) void PrintTreeNode(BinaryTreeNode* pNode); //输出单个结点
__declspec( dllexport ) void PrintTree(BinaryTreeNode* pRoot);     //遍历结点并输出
__declspec( dllexport ) void DestroyTree(BinaryTreeNode* pRoot);   //销毁树结点

BinaryTree.cpp

#include "StdAfx.h"
#include "BinaryTree.h"

//创建二叉树
BinaryTreeNode* CreateBinaryTreeNode(int value)
{
    BinaryTreeNode* pNode = new BinaryTreeNode(); //分配内存空间
    pNode->m_nValue = value;    //给结点赋值
    pNode->m_pLeft = NULL;      //指向左子结点
    pNode->m_pRight = NULL;     //指向右子结点

    return pNode;
}

//连接树结点
void ConnectTreeNodes(BinaryTreeNode* pParent, BinaryTreeNode* pLeft, BinaryTreeNode* pRight)
{
    if(pParent != NULL)
    {
        pParent->m_pLeft = pLeft;   //左子结点
        pParent->m_pRight = pRight; //右子结点
    }
}

//输出单个树的结点,同时输出它的子结点
void PrintTreeNode(BinaryTreeNode* pNode)
{
    if(pNode != NULL)
    {
        printf("value of this node is: %d\n", pNode->m_nValue);

        if(pNode->m_pLeft != NULL)
            printf("value of its left child is: %d.\n", pNode->m_pLeft->m_nValue);
        else
            printf("left child is null.\n");

        if(pNode->m_pRight != NULL)
            printf("value of its right child is: %d.\n", pNode->m_pRight->m_nValue);
        else
            printf("right child is null.\n");
    }
    else
    {
        printf("this node is null.\n");
    }

    printf("\n");
}

//遍历并输出树的所有结点,递归调用
void PrintTree(BinaryTreeNode* pRoot)
{
    PrintTreeNode(pRoot);

    if(pRoot != NULL)
    {
        if(pRoot->m_pLeft != NULL)
            PrintTree(pRoot->m_pLeft);

        if(pRoot->m_pRight != NULL)
            PrintTree(pRoot->m_pRight);
    }
}

//销毁树结点,递归销毁所有树结点
void DestroyTree(BinaryTreeNode* pRoot)
{
    if(pRoot != NULL)
    {
        BinaryTreeNode* pLeft = pRoot->m_pLeft;
        BinaryTreeNode* pRight = pRoot->m_pRight;

        delete pRoot;
        pRoot = NULL;

        DestroyTree(pLeft);
        DestroyTree(pRight);
    }
}

main.cpp


#include "stdafx.h"
#include "..\Utilities\BinaryTree.h"
#include <exception>   //exception,异常处理

BinaryTreeNode* ConstructCore(int* startPreorder, int* endPreorder, int* startInorder, int* endInorder);

BinaryTreeNode* Construct(int* preorder, int* inorder, int length)
{
    if(preorder == NULL || inorder == NULL || length <= 0)
        return NULL;

    return ConstructCore(preorder, preorder + length - 1,
        inorder, inorder + length - 1);  //调用函数ConstructCore
}

BinaryTreeNode* ConstructCore
(
    int* startPreorder, int* endPreorder, 
    int* startInorder, int* endInorder
)
{
    // 前序遍历序列的第一个数字是根结点的值
    int rootValue = startPreorder[0];            //根结点
    BinaryTreeNode* root = new BinaryTreeNode(); //创建一棵二叉树
    root->m_nValue = rootValue;            //赋值
    root->m_pLeft = root->m_pRight = NULL; //指针赋值

    if(startPreorder == endPreorder)
    {
        if(startInorder == endInorder && *startPreorder == *startInorder)
            return root;
        else
            throw std::exception("Invalid input.");
    }

    // 在中序遍历中找到根结点的值
    int* rootInorder = startInorder;
    while(rootInorder <= endInorder && *rootInorder != rootValue) //查找根结点的索引
        ++ rootInorder;

    if(rootInorder == endInorder && *rootInorder != rootValue)
        throw std::exception("Invalid input.");

    int leftLength = rootInorder - startInorder;        //左子树结点个数
    int* leftPreorderEnd = startPreorder + leftLength;

	//递归构建子树
    if(leftLength > 0)
    {
        // 构建左子树
        root->m_pLeft = ConstructCore(startPreorder + 1, leftPreorderEnd, 
            startInorder, rootInorder - 1);
    }
    if(leftLength < endPreorder - startPreorder)
    {
        // 构建右子树
        root->m_pRight = ConstructCore(leftPreorderEnd + 1, endPreorder,
            rootInorder + 1, endInorder);
    }

    return root;
}

// ====================测试代码====================
void Test(char* testName, int* preorder, int* inorder, int length)
{
    if(testName != NULL)
        printf("%s begins:\n", testName);

    printf("The preorder sequence is: ");
    for(int i = 0; i < length; ++ i)
        printf("%d ", preorder[i]);   //前序遍历值数组
    printf("\n");

    printf("The inorder sequence is: ");
    for(int i = 0; i < length; ++ i)
        printf("%d ", inorder[i]);    //后序遍历值数组
    printf("\n");

    try
    {
		//构建树
        BinaryTreeNode* root = Construct(preorder, inorder, length);
		//输出树
        PrintTree(root);
		//销毁树
        DestroyTree(root);
    }
    catch(std::exception& exception)
    {
        printf("Invalid Input.\n");
    }
}

// 普通二叉树
//              1
//           /     \
//          2       3  
//         /       / \
//        4       5   6
//         \         /
//          7       8
void Test1()
{
    const int length = 8;
    int preorder[length] = {1, 2, 4, 7, 3, 5, 6, 8};
    int inorder[length] = {4, 7, 2, 1, 5, 3, 8, 6};

    Test("Test1", preorder, inorder, length);
}

// 所有结点都没有右子结点
//            1
//           / 
//          2   
//         / 
//        3 
//       /
//      4
//     /
//    5
void Test2()
{
    const int length = 5;
    int preorder[length] = {1, 2, 3, 4, 5};
    int inorder[length] = {5, 4, 3, 2, 1};

    Test("Test2", preorder, inorder, length);
}

// 所有结点都没有左子结点
//            1
//             \ 
//              2   
//               \ 
//                3 
//                 \
//                  4
//                   \
//                    5
void Test3()
{
    const int length = 5;
    int preorder[length] = {1, 2, 3, 4, 5};
    int inorder[length] = {1, 2, 3, 4, 5};

    Test("Test3", preorder, inorder, length);
}

// 树中只有一个结点
void Test4()
{
    const int length = 1;
    int preorder[length] = {1};
    int inorder[length] = {1};

    Test("Test4", preorder, inorder, length);
}

// 完全二叉树
//              1
//           /     \
//          2       3  
//         / \     / \
//        4   5   6   7
void Test5()
{
    const int length = 7;
    int preorder[length] = {1, 2, 4, 5, 3, 6, 7};
    int inorder[length] = {4, 2, 5, 1, 6, 3, 7};

    Test("Test5", preorder, inorder, length);
}

// 输入空指针
void Test6()
{
    Test("Test6", NULL, NULL, 0);
}

// 输入的两个序列不匹配
void Test7()
{
    const int length = 7;
    int preorder[length] = {1, 2, 4, 5, 3, 6, 7};
    int inorder[length] = {4, 2, 8, 1, 6, 3, 7};

    Test("Test7: for unmatched input", preorder, inorder, length);
}

int _tmain(int argc, _TCHAR* argv[])
{
    Test1();
    Test2();
    Test3();
    Test4();
    Test5();
    Test6();
    Test7();

    return 0;
}


/*******************7月17日*************************/

// 面试题6.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <stack>

/***二叉树的定义和基本操作***/
struct BinaryTreeNode 
{
	int				m_nValue;  //值
	BinaryTreeNode*	m_pLeft;   //左子针
	BinaryTreeNode* m_pRight;  //右子针
};
/**********基本操作*********/
//创建一个树的单个节点
BinaryTreeNode* CreateTreeNode(int value)
{
	BinaryTreeNode* pNode = new BinaryTreeNode();
	pNode->m_nValue = value;
	pNode->m_pLeft = NULL;
	pNode->m_pRight = NULL;

	return pNode;
}

//连接节点,构成一颗二叉树
void ConnectTreeNode(BinaryTreeNode* pRoot, BinaryTreeNode* pLeft, BinaryTreeNode* pRight)
{
	if(pRoot == NULL)
		return;
	pRoot->m_pLeft = pLeft;
	pRoot->m_pRight = pRight;
}

//输出单个二叉树
void PrintTreeNode(BinaryTreeNode* pNode)
{
	if(pNode == NULL)
	{
		printf("the value of this node is null.\n");
		return;
	}
	printf("the value of this node is: %d\n", pNode->m_nValue);

	if(pNode->m_pLeft != NULL)
		printf("the value of its left child is %d\n", pNode->m_pLeft->m_nValue);
	else
		printf("its left child is null.\n");
	if(pNode->m_pRight != NULL)
		printf("the value of its right child is %d\n", pNode->m_pRight->m_nValue);
	else
		printf("its right child is null.\n");
	
	printf("\n");

}

//输出整棵二叉树(先序遍历递归)
void PrintTree(BinaryTreeNode* pRoot)
{
	PrintTreeNode(pRoot);

	if(pRoot != NULL)
	{
		//左树
		if(pRoot->m_pLeft != NULL)
			PrintTree(pRoot->m_pLeft);
		//右树
		if(pRoot->m_pRight != NULL)
			PrintTree(pRoot->m_pRight);	
	}
}
//先序遍历(递归)
void PreOrderTraverse(BinaryTreeNode* pRoot)
{
	if(pRoot == NULL) return;
	 printf("%d ", pRoot->m_nValue);

	//左树
	if(pRoot->m_pLeft != NULL)
		PreOrderTraverse(pRoot->m_pLeft);
	//右树
	if(pRoot->m_pRight != NULL)
		PreOrderTraverse(pRoot->m_pRight);

}

//中序遍历(非递归)
void InOrderTraverse(BinaryTreeNode* pRoot)
{
	std::stack<BinaryTreeNode*> pNode;
	BinaryTreeNode* p = pRoot;
	pNode.push(pRoot);

	while(!pNode.empty())
	{
		while(pNode.top() != NULL && p!=NULL )
		{
			p = p->m_pLeft;
			pNode.push(p);
		}
		//BinaryTreeNode* node = 
		pNode.pop();  //空指针出栈

		if(!pNode.empty())
		{
			p = pNode.top(); //栈顶元素
			printf("%d ",p->m_nValue);
			pNode.pop();
			p = p->m_pRight;
			pNode.push(p);
		}
	}

}


//销毁二叉树
void DestroyTree(BinaryTreeNode* pRoot)
{
	if(pRoot == NULL)
		return;
	BinaryTreeNode* pLNode = pRoot->m_pLeft;
	BinaryTreeNode* pRNode = pRoot->m_pRight;

	delete pRoot;
	pRoot = NULL;
	
	if(pLNode != NULL)
		DestroyTree(pLNode);
	if(pRNode != NULL)
		DestroyTree(pRNode);
}

/******************面试题6***********/
BinaryTreeNode* ConstructCore(int * startPreOrder, int* endPreOrder, int* startInOrder, int* endInOrder);
//重建二叉树
BinaryTreeNode* Construct(int* preOrder, int* inOrder, int length)
{
	if (preOrder == NULL || inOrder == NULL || length <= 0)
		return NULL;

	return ConstructCore(preOrder, preOrder + length - 1, inOrder, inOrder + length - 1);
}

BinaryTreeNode* ConstructCore(int * startPreOrder, int* endPreOrder, int* startInOrder, int* endInOrder)
{
	//前序遍历的第一个值是根结点值
	int rootValue = startPreOrder[0];
	BinaryTreeNode* root = new BinaryTreeNode();
	root->m_nValue = rootValue;
	root->m_pLeft = root->m_pRight = NULL;

	//只有一个结点的情况
	if(startPreOrder == endPreOrder)
	{
		if(startInOrder == endInOrder && *startPreOrder == *startInOrder)
			return root;
		else
			throw std::exception("Invalid input.");
	}

	//中序遍历找根结点值
	int* rootInOrder = startInOrder;
	while(rootInOrder <= endInOrder && *rootInOrder != rootValue)
		++ rootInOrder;

	//当中序遍历和前序遍历不匹配,找不到根结点
	if(rootInOrder == endInOrder && *rootInOrder != rootValue)
		throw std::exception("Invalid input");

	int leftLength = rootInOrder - startInOrder;
	int* leftPreOrderEnd = startPreOrder + leftLength;

	if(leftLength > 0)
	{
		//构建左子树
		root->m_pLeft = ConstructCore(startPreOrder + 1, leftPreOrderEnd, startInOrder, rootInOrder - 1);
	}

	if(leftLength < endPreOrder - startPreOrder)
	{
		//构建右子树
		root->m_pRight = ConstructCore(leftPreOrderEnd + 1, endPreOrder, rootInOrder + 1, endInOrder);
	}

	return root;
}

/********测试*********/
void Test1()
{
	//创建树的节点
	BinaryTreeNode* pNode1 = CreateTreeNode(1);
	BinaryTreeNode* pNode2 = CreateTreeNode(2);
	BinaryTreeNode* pNode3 = CreateTreeNode(3);
	BinaryTreeNode* pNode4 = CreateTreeNode(4);
	BinaryTreeNode* pNode5 = CreateTreeNode(5);
	BinaryTreeNode* pNode6 = CreateTreeNode(6);
	BinaryTreeNode* pNode7 = CreateTreeNode(7);
	BinaryTreeNode* pNode8 = CreateTreeNode(8);

	//连接树的节点,构成一棵二叉树
	ConnectTreeNode(pNode1, pNode2, pNode3);
	ConnectTreeNode(pNode2, pNode4, NULL);
	ConnectTreeNode(pNode4, NULL, pNode7);

	ConnectTreeNode(pNode3, pNode5, pNode6);
	ConnectTreeNode(pNode5, NULL, NULL);
	ConnectTreeNode(pNode6, pNode8, NULL);

	//输出二叉树
	PrintTree(pNode1);
	printf("\n");
	PreOrderTraverse(pNode1);
	printf("\n");

	InOrderTraverse(pNode1);

	//销毁二叉树
	DestroyTree(pNode1);

}
//完全二叉树
void Test2()
{
	int preorder[] ={1,2,4,5,3,6};
	int inorder[] = {4,2,5,1,6,3};
	try {
		BinaryTreeNode*  biTree = Construct(preorder, inorder,sizeof(preorder)/sizeof(int));
	
		PrintTree(biTree);
		DestroyTree(biTree);

	}catch(...)
	{
		printf("参数错误");
	}
	

}
//满二叉树
void Test3()
{
	int preorder[] ={1,2,4,5,3,6,7};
	int inorder[] = {4,2,5,1,6,3,7};
	BinaryTreeNode*  biTree = Construct(preorder, inorder,sizeof(preorder)/sizeof(int));
	PrintTree(biTree);
	DestroyTree(biTree);

}

//一般二叉树
void Test4()
{
	int preorder[] ={1,2,4,5,3,6};
	int inorder[] = {4,2,5,1,3,6};
	BinaryTreeNode*  biTree = Construct(preorder, inorder,sizeof(preorder)/sizeof(int));
	PrintTree(biTree);
	DestroyTree(biTree);

}

//只有左树
void Test5()
{
	int preorder[] ={1,2,3};
	int inorder[] = {3,2,1};
	BinaryTreeNode*  biTree = Construct(preorder, inorder,sizeof(preorder)/sizeof(int));
	PrintTree(biTree);
	DestroyTree(biTree);

}

//只有右树
void Test6()
{
	int preorder[] ={1,2,3};
	int inorder[] = {1,2,3};
	BinaryTreeNode*  biTree = Construct(preorder, inorder,sizeof(preorder)/sizeof(int));
	PrintTree(biTree);
	DestroyTree(biTree);

}

//单结点
void Test7()
{
	int preorder[] ={1};
	int inorder[] = {1};
	BinaryTreeNode*  biTree = Construct(preorder, inorder,sizeof(preorder)/sizeof(int));
	PrintTree(biTree);
	DestroyTree(biTree);

}
//空二叉树
void Test8()
{
	BinaryTreeNode*  biTree = Construct(NULL, NULL,0);
	PrintTree(biTree);
	DestroyTree(biTree);

}

void Test9()
{
	int preorder[] ={1,2,4,5,3,6};
	int inorder[] = {4,2,5,1,6,4};
	try {
		BinaryTreeNode*  biTree = Construct(preorder, inorder,sizeof(preorder)/sizeof(int));
	
		PrintTree(biTree);
		DestroyTree(biTree);

	}catch(...)
	{
		printf("参数错误");
	}
	
}
int _tmain(int argc, _TCHAR* argv[])
{
	//Test1();
	//Test2();
	//Test3();
	//Test4();
	//Test5();
	//Test6();
	//Test7();
	//Test8();
	Test9();

	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值