二叉树

目录

1.二叉树概念及结构

1.1 概念

1.2 特殊的二叉树

1.3 二叉树的性质

2.二叉树的顺序结构及实现

2.1 二叉树的顺序结构

2.2 堆的概念及结构

 2.3 堆的实现

2.3.1 堆向下调整算法

 2.3.2 堆的创建

2.3.4 堆的插入

2.3.5 堆的删除

2.4 堆的应用

2.4.1 堆排序

 2.4.2 TOP-K问题

3.二叉树链式结构的实现

3.1 二叉树的创建

3.2 二叉树的遍历

3.2.1 前序、中序以及后序遍历

3.3 节点个数及高度等


1.二叉树概念及结构

1.1 概念

一个二叉树是结点的一个有限集合,该集合

        1.或者为空

        2.由一个根结点加上两棵分别称为左子树和右子树的二叉树组成

1.2 特殊的二叉树

1.满二叉树:一个二叉树,如果每一个结点树都达到最大值,则这个二叉树就是满二叉树.

2.完全二叉树:对于深度为k的,n个结点的二叉树,当且仅当每一个结点都与深度为k的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树.满二叉树是一种特殊的完全二叉树. 

 

1.3 二叉树的性质

        1.若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有2^(i-1)个结点

        2.若规定根节点的层数为1,则深度为h的二叉树的最大结点数是2^h-1.

        3.对任意一棵二叉树,如果度为0其叶子结点个数为n0,度为2的分支结点个数为n2,则n0=n2+1


2.二叉树的顺序结构及实现

2.1 二叉树的顺序结构

普通二叉树不适合用数组来存储,因为可能会存在大量的空间浪费,而完全二叉树更适合使用顺序结构存储.现实中我们通常把堆使用顺序结构的数组来存储.

2.2 堆的概念及结构

如果有一个关键码的集合K = { k_{0}k_{1},k_{2},k_{3}...,k_{n-1} },把它的所有元素按完全二叉树的顺序存储方式存储
在一个一维数组中,并满足:K_{i}<=K_{2*i+1} 且K_{i}<=K_{2*i+2}(K_{i}>K_{2*i+1}K_{i}>K_{2*i+2})i=0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆的性质

  • 堆中的某个结点总是不大于或不小于其父结点的值
  • 堆总是一棵完全二叉树

 

 2.3 堆的实现

2.3.1 堆向下调整算法

现在我们给出一个数组,逻辑上看做一棵完全二叉树,我们通过根节点开始的向下调整算法可以把它调整成一个小堆.向下调整算法有一个前提:左右子树必须是一个堆,才能调整

 

void AdjustDown(HPDataType* a,int size,int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		if (a[child + 1] < a[child] && child+1 < size)
		{
			child++;
		}
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = child * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

 2.3.2 堆的创建

 下面我们给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。根节点左右子树不是堆,我们怎么调整呢?这里我们从倒数的第一个非叶子节点的子树开始调整,一直调整到根节点的树,就可以调整成堆。

 

for (int i = (n - 1 - 1) / 2;i >= 0;i--)
{
	AdjustDown(a, n ,i);
}

2.3.4 堆的插入

先插入一个10到数组的尾上,再进行向上调整算法,直到满足堆。

 

2.3.5 堆的删除

 删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。

2.4 堆的应用

2.4.1 堆排序

堆排序即利用堆的思想来进行排序,总共分为两个步骤:

1.建堆

  • 升序:建大堆
  • 降序:建小堆

2.利用堆删除思想来排序

建堆和堆删除中都用到了向下调整,因此掌握了向下调整,就可以完成堆排序。

void HeapSort(int* a, int n)
{
	//向上调整:时间复杂度O(N*log N)
	//for (int i = 1;i < n;i++)
	//{
	//	AdjustUp(a,i);
	//}
	
	//向下调整:时间复杂度O(N)
	for (int i = (n - 1 - 1) / 2;i >= 0;i--)
	{
		AdjustDown(a, n ,i);
	}

	int end = n - 1;
	//O(N*log N)
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}
}

 2.4.2 TOP-K问题

TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。

1. 用数据集合中前K个元素来建堆

  • 前k个最大的元素,则建小堆
  • 前k个最小的元素,则建大堆

2. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素

将剩余N-K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素。

void PrintTopK(int* a, int n, int k)
{
	int* KMinHeap = (int*)malloc(sizeof(int) * k);
	assert(KMinHeap);
	//先把前k个数建成小堆
	for (int i = 0;i < k;i++)
	{
		KMinHeap[i] = a[i];
	}

	for (int j = (k - 1 - 1) / 2;j >= 0;j--)
	{
		AdjustDown(KMinHeap, k, j);
	}

	//后面n-k个数依次与堆顶的数比较,比堆顶的数大就进堆
	for (int i = k; i < n; i++)
	{
		if (a[i] > KMinHeap[0])
		{
			KMinHeap[0] = a[i];
			AdjustDown(KMinHeap, k, 0);
		}
	}
	
	for (int j = 0; j < k;j++)
	{
		printf("%d ", KMinHeap[j]);
	}
	printf("\n");
}

3.二叉树链式结构的实现

3.1 二叉树的创建

typedef int BTDataType;
typedef struct BinaryTreeNode
{
    BTDataType _data;
    struct BinaryTreeNode* left;
    struct BinaryTreeNode* right;
}BTNode;
BTNode* CreatBinaryTree()
{
    BTNode* node1 = BuyNode(1);
    BTNode* node2 = BuyNode(2);
    BTNode* node3 = BuyNode(3);
    BTNode* node4 = BuyNode(4);
    BTNode* node5 = BuyNode(5);
    BTNode* node6 = BuyNode(6);
    node1->_left = node2;
    node1->_right = node4;
    node2->_left = node3;
    node4->_left = node5;
    node4->_right = node6;
    return node1;
}

3.2 二叉树的遍历

3.2.1 前序、中序以及后序遍历

学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。

 按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历:

  1. 1前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
  2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
  3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后

这里主要介绍前序遍历,剩余两个遍历都是前序类似

 

 

void PreOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("# ");
		return;
	}
	printf("%d ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}

void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("# ");
		return;
	}
	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}

void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("# ");
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}

3.3 节点个数及高度等

// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	return BinaryTreeSize(root->left) 
		+ BinaryTreeSize(root->right) + 1;
}

// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	if (root->left == 0 && root->right == 0)
		return 1;
	return BinaryTreeLeafSize(root->left)
		+ BinaryTreeLeafSize(root->right);
}

// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;
	return BinaryTreeLevelKSize(root->left, k - 1)
		+ BinaryTreeLevelKSize(root->right, k - 1);
}

// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{

	if (root == NULL)
		return NULL;
	if (root->data == x)
		return root;
	BTNode* ret1 = BinaryTreeFind(root->left, x);
	if (ret1)
		return ret1;
	BTNode* ret2 = BinaryTreeFind(root->right, x);
	if (ret2)
		return ret2;
	return NULL;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值