数据结构—二叉树相关概念及经典面试题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/z_x_m_m_q/article/details/82875735

二叉树概念

一棵二叉树是结点的有限集合,该集合或者为空, 或者是由根结点加上两棵分别称为左子树和右子树的二叉树构成

二叉树的特点:

  1. 每个结点最多有两棵子树,即二叉树不存在度大于2的结点
  2. 二叉树的子树有左右之分,其子树的次序不能颠倒

满二叉树、完全二叉树

满二叉树:在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子节点都在同一层上

完全二叉树: 如果一棵具有N个结点的二叉树的结构与满二叉树的前N个 结点的结构相同,称为完全二叉树

二叉树性质

  • 若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有2^{i-1} (i>0)个结点
  • 若规定只有根节点的二叉树的深度为1,则深度为K的二叉树的最大结点数是2^{k}-1 (k>=0)  
  • 对任何一棵二叉树, 如果其叶结点个数为 n0, 度为2的非叶结点个数 为 n2,则有n0=n2+1
  • 具有n个结点的完全二叉树的深度k为\lg (n+1)上取整

对于具有n个结点的完全二叉树,如果按照从上至下从左至右的顺序 对所有节点从0开始编号,则对于序号为i的结点有:

  1. 若i>0,双亲序号:(i-1)/2; 
  2. i=0,i为根节点编号,无双亲结点
  3. 若2i+1<n,左孩子序号:2i+1,否则无左孩子
  4. 若2i+2<n,右孩子序号:2i+2,否则无右孩子 

二叉树存储结构

 二叉树有顺序存储和链式存储结构

扫描二维码关注公众号,回复: 4367871 查看本文章

顺序存储结构:

对于一棵完全二叉树所有结点按照层序自顶向下,同一层自左向右顺 序编号,就得到一个节点的顺序序列

优点:存储完全二叉树,简单省空间

缺点:存储一般二叉树尤其单支树,存储空间利用不高 

链式存储结构:

二叉链结构结点的定义:

typedef int DataType;
typedef struct BinaryTreeNode  //二叉树结构定义
{
        struct BinaryTreeNode* _left;  //指向当前结点左孩子
        struct BinaryTreeNode* _right;  //指向当前结点右孩子
        DataType _data;  //当前结点数据
}BTNode;

三叉链结构结点定义:

typedef int DataType;
typedef struct BinaryTreeNode  //二叉树结构定义
{
          struct BinaryTreeNode* _pParent;  //指向当前节点的双亲结点
          struct BinaryTreeNode* _pLeft;  //指向当前结点左孩子
          struct BinaryTreeNode* _pRight;  //指向当前结点右孩子
          ataType _data;  //当前结点数据
}BTNode;

二叉树的操作 (面试题) 

创建二叉树:

对二叉树进行其他操作的基础是要创建出二叉树,我们采用递归的方式创建二叉树,从根结点开始先处理它的左子树,再处理它的右子树(对子树也是同样的),递归是比较抽象的,所以我先给出实现的代码,然后详细说一下递归是如何实现二叉树创建的

// 创建二叉树 
//a:数组指针  pIndex:下标指针  invalib:给的非结点元素,用其表示一个结点无孩子(左、右)
BTNode* CreateBTree(DataType* a, size_t* pIndex, DataType invalid)
{
	assert(a);
	if (a[*pIndex] == invalid)
		return NULL;

	BTNode* root = BuyBTNode(a[*pIndex]);

	++(*pIndex);
	root->_left = CreateBTree(a,pIndex,invalid);

	++(*pIndex);
	root->_right = CreateBTree(a, pIndex, invalid);

	return root;
}

思考:创建二叉树是参数为什么给的是指针变量 pIndex,而不是变量index?

我在走递归过程时以上图中已经很好的体现了,相信对这块的理解没有问题

前序遍历

  • 递归实现
void BTreePrevOrder(BTNode* root)  //前序遍历
{
	if (root == NULL)
		return;

	printf("%d  ",root->_data);  

	BTreePrevOrder(root->_left);  
	BTreePrevOrder(root->_right); 
}
  • 非递归实现
void BTreePrevOrderNonR(BTNode* root)  //非递归前序遍历
{
	assert(root);

	Stack s;
	StackInit(&s);

	BTNode* cur = root;
	while (cur != NULL || StackEmpty(&s) != 0) {

		while (cur) {
			printf("%d  ",cur->_data);
			StackPush(&s,cur);

			cur = cur->_left;
		}

		BTNode* top = StackTop(&s);
		cur = top->_right;
		StackPop(&s);
	}
}

中序遍历

  •  递归实现
void BTreeInOrder(BTNode* root)  //中序遍历
{
	if (root == NULL)
		return;

	BTreeInOrder(root->_left);
	printf("%d  ",root->_data);
	BTreeInOrder(root->_right);
}
  • 非递归实现
void BTreeInOrderNonR(BTNode* root)  //非递归中序遍历
{
	assert(root);

	Stack s;
	StackInit(&s);

	BTNode* cur = root;
	while (cur != NULL || StackEmpty(&s) != 0) {

		while (cur) {
			StackPush(&s, cur);

			cur = cur->_left;
		}

		BTNode* top = StackTop(&s);
		printf("%d  ", top->_data);
		cur = top->_right;
		StackPop(&s);
	}
}

后序遍历

  •  递归实现
void BTreePostOrder(BTNode* root)  //后序遍历
{
	if (root == NULL)
		return;

	BTreePostOrder(root->_left);
	BTreePostOrder(root->_right);
	printf("%d  ",root->_data);
}
  • 非递归实现
void BTreePostOrderNonR(BTNode* root)  //非递归后序遍历
{
	Stack s;
	StackInit(&s);

	BTNode* cur = root;
	BTNode* prev = NULL;  //记录被遍历的前一个结点
	while (cur != NULL || StackEmpty(&s) != 0) {

		while (cur) {
			StackPush(&s, cur);

			cur = cur->_left;
		}

		BTNode* top = StackTop(&s);
		if (top->_right == NULL || top->_right == prev)
		{
			printf("%d  ", top->_data);
			StackPop(&s);
			prev = top;
		}
		else
			cur = top->_right;
	}
}

层序遍历 

void BTreeLevelOrder(BTNode* root)  //层序遍历
{
	assert(root);

	Queue q;
	QueueInit(&q);

	QueuePush(&q,root);

	while (QueueEmpty(&q) != 0) {

		BTNode* head = QueueHead(&q);
		printf("%d  ",head->_data);
		QueuePop(&q);

		if (head->_left != NULL)
			QueuePush(&q,head->_left);

		if (head->_right != NULL)
			QueuePush(&q, head->_right);

	}
}

结点个数 

size_t BTreeSize(BTNode* root)  //结点个数
{
	if (root == NULL)
		return 0;

        //左子树的个数+右子树的个数
	return 1 + BTreeSize(root->_left) + BTreeSize(root->_right);
}

 叶子结点个数

size_t BTreeLeafSize(BTNode* root)  //叶子结点的个数
{
	if (root == NULL)  //这个返回条件不能不写
		return 0;
	if (root->_left == NULL && root->_right == NULL)
		return 1;

	//左子树叶结点+右子树叶结点
	return BTreeLeafSize(root->_left) + BTreeLeafSize(root->_right);
}

 第K层结点个数

size_t BTreeKLevelSize(BTNode* root, size_t k)  //第k层的结点个数
{
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;

	//左子树第k-1层的结点数+右子树第k-1层的结点树
	return BTreeKLevelSize(root->_left,k-1) + BTreeKLevelSize(root->_right,k-1);
}

二叉树的深度 

size_t BTreeDepth(BTNode* root)  //二叉树的深度
{
	if (root == NULL)
		return 0;

	//返回左子树和右子树中较深的(加1:根节点)
	return (1 + BTreeDepth(root->_left)) >= (1 + BTreeDepth(root->_right)) ? (1+BTreeDepth(root->_left)) : (1+BTreeDepth(root->_right));
}

判断一个节点是否在一棵二叉树中

BTNode* BTreeFind(BTNode* root, BTNDataType x)  //查看某个结点是否存在
{
	if (root == NULL)
		return NULL;

	if (root->_data == x)
		return root;

	BTNode* ret;
	ret = BTreeFind(root->_left,x);
	if (ret != NULL)
		return ret;

	ret = BTreeFind(root->_right,x);
	return ret;
}

 判断一棵二叉树是否是完全二叉树

int IsCompleteBTree(BTNode* root)  //判断是否是完全二叉树
{
	assert(root);

	Queue q;
	QueueInit(&q);

	QueuePush(&q,root);

	while (QueueEmpty(&q) != 0) {

		BTNode* head = QueueHead(&q);
		QueuePop(&q);
		if (head == NULL)
			break;

		if (head->_left != NULL || head->_right != NULL)
		{
			QueuePush(&q,head->_left);
			QueuePush(&q,head->_right);
		}
	}

	while (QueueEmpty(&q) != 0) {

		BTNode* head = QueueHead(&q);
		if (head == NULL)
		{
			if (QueueEmpty(&q) != 0)
				return 0;  //返回0表示不是完全二叉树

			else
				return 1;  //返回1表示是完全二叉树
		}
		else
			QueuePop(&q);
	}

	return 1;
}

求二叉树中两个结点的最近公共祖先结点

判断一棵二叉树是否是平衡二叉树

求二叉树中最远的两个结点之间的距离

有前序遍历和中序遍历重建二叉树(前序遍历结果:1,2,3,4,5,6 中序 遍历结果:4,2,5,1,6,3)

求二叉树的镜像

将二叉搜索树转换成一个排序的双向链表。要求:不能创建任何新的 结点,只能调整树种结点指针的指向  
 

猜你喜欢

转载自blog.csdn.net/z_x_m_m_q/article/details/82875735