深入理解二叉树

树的概念:

节点的度:一个节点含有子树的个数.如上图所示A节点的度为6

叶节点/终端节点:度为0的节点,如B,C,H,I,............

父节点:一个节点含有子节点,则这个节点为该子节点的父节点,如A是B的父节点.

子节点:一个节点含有子树的根节点为子节点,如B是A的子节点

树的高度:树的结点的最大层次,如上图树的高度为4

二叉树:

二叉树是由根节点和左右子树构成(不存在度大于2的节点)

如下所示:

 满二叉树:

设一个二叉树的层数为K,总结点个数为2^k-1,则称为满二叉树.如下所示

完全二叉树:设树的层数为k,前k-1层是满二叉树,最后一层不满,但是连续的(空节点后面没有非空节点).如下所示:

 二叉树的性质:

度为0的叶结点个数为n0,度为2的分支节点个数为n2,则有n0=n2+1;

 在上述练习中,n2=199,叶子节点(度为0的节点)=n2+1=199+1=200;

在上述练习中,总节点个数=n0+n1+n2 ,n0=n2+1;所以2n=n0+n1+n2=2*n0+n1-1;n1为0/1,所以n0=n;

 最多节点个数:2^(h-1)-1;最少节点个数2^(h-1);

堆:

堆采用的是顺序存储的方式,

小堆(子节点的值大于父结点的值),大堆(父结点的值大于子结点的值)

建堆的时间复杂度为O(N),N为节点个数.

堆的代码实现:

容易出错的函数模块(向上调整)

void AdjustUp(HPDataType* a, int child)//从某个孩子的位置向上调整
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] < a[parent])//这里是小堆,谁小谁是爹,所以让child<parent。如果是大堆,谁大谁是爹,所以让child>parent
		{
			Swap(&a[child], &a[parent]);
			child = parent;//继续往上走
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
//向上调整是儿子要当爹,所以要把父亲的位置让给儿子,父亲的位置更新

向下调整:

//向下调整(父向下)
void AdjustDown(HPDataType* a, int n, int parent)
{
	int child = parent * 2 + 1;//先假设左孩子小
	while (child < n)
	{
		if (child+1<n && a[child + 1] < a[child])
		{
			++child;//如果右孩子小则+1
		}
		if (a[child] < a[parent])//因为还要将其设值成小堆,所以要向下走,谁小谁是爹,如果设置成大堆[child] 》 a[parent]
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
//向下调整是父亲当儿子.所以把儿子的位置让给父亲

堆排序:

用向上调整法建堆时间复杂度O(NlogN),向下调整时间复杂度O(N),所以使用向下调整法.

void HeapSort(int* a, int n)//堆排序
{
	//对数组建堆 2
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)//开始是从0开始的,所以最后坐标是n-1;
	{
		AdjustDown(a, i);//向下调整建堆
	}
	int end = n - 1;
	while (end > 0) //降序,建小堆
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		--end;
    }
}

TOP-K问题:

先建立k个数的小堆,然后再读取N-K个数,如果比前k个数大则进入堆中,在堆里向下调整,最后出现在堆里的数便是前k个.

void test3()
{
	int k;
	printf("请输入k>:");
	scanf("%d", &k);
	//建k个数的小堆
	int* kminheap = (int*)malloc(sizeof(int)*k);
	if (kminheap == NULL)
	{
		perror("malloc fail");
		return;
	}
	const char* file = "data.txt";
	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen error");
		return;
	}
	//读取前k个数
	for (int i = 0; i < k; i++)
	{
		fscanf(fout, "%d", & kminheap[i]);
	}
	//建k个数的小堆
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{ 
		AdjustDown(kminheap, k, i);
	}
	//读取剩下的n-k个数
	int x = 0;
	while (fscanf(fout, "%d", &x) > 0)
	{
		if (x > kminheap[0])
		{
			kminheap[0] = x;
			AdjustDown(kminheap, k, 0);
		}
	}
	//打印最大的前k个数
	for (int i = 0; i < k; i++)
	{
		printf("%d ", kminheap[i]);
	}
	printf("\n");
}
//找前K个最大的数,建一个小堆,如果比堆顶大,进入堆,向下调整

二叉树链式结构:

二叉树的遍历分为前序遍历,中序遍历和后序遍历;

前序遍历:先访问根,再访问左子树,最后访问右子树.

如上图所示,访问顺序为 1 2 3 N N N 4 5 N N 6 N N

中序遍历:先访问左子树,再访问根,最后访问右子树.

如上图所示,访问顺序为 N 3 N 2 N 1 N 5 N 4 N 6 N

后序遍历:先访问左子树,再访问右子树,最后访问根.

如上图所示,访问顺序为 N N 3 N 2 N N 5 N N 6 4 1

访问规则:访问完一边在访问另一边.

二叉树使用的是链式结构.采用的的是递归思想,递归的本质是返回条件和子问题

以计算树的高度为例,如下所示:

 二叉树层序遍历:

采用的是队列的思想,当根出队列的时候它的叶子节点便会进队列,从而实现一层一层的遍历.代码如下:

栈和队列 (以及循环队列实现)-CSDN博客(队列的代码在该链接)

void TreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		printf("%d ", front->data);

		if(front->left)
			QueuePush(&q, front->left);

		if (front->right)
			QueuePush(&q, front->right);
	}

	QueueDestroy(&q);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值