数据结构-堆与二叉树

目录

前言

二叉树

1.遍历二叉树

2.二叉树的结点个数

3.求叶子结点的个数

4.求二叉树的高度

5.求二叉树第K层结点个数

简介:

应用:

1.堆排序--O(N*logN)

2.TOP-K

3.优先级队列

代码:

1.Heap.h

2.Heap.c

图示助解:

3.Test.c

总结



前言

        本篇博客对概念性的知识点涉及较少,不建议初学者用于学习。博主将会尽量用图示与代码结合便于读者理解,博主水平不高请见谅,欢迎读者提出建议、批评指正。


二叉树

1.遍历二叉树

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>

typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

BTNode* BuyNode(BTDataType x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		return NULL;
	}

	node->data = x;
	node->left = NULL;
	node->right = NULL;

	return node;
}

BTNode* CreatTree()
{
	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;
}

void PreOrder(BTNode* root) {
	if (root == NULL) {
		printf("NULL ");
		return;
	}

	printf("%d ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}

void InOrder(BTNode* root) {
	if (root == NULL) {
		printf("NULL ");
		return;
	}

	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}

void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}

	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}

2.二叉树的结点个数

//全局变量
//int size = 0;
//void TreeSize1(BTNode* root)
//{
//	if (root == NULL)
//		return;
//
//	++size;
//	TreeSize1(root->left);
//	TreeSize1(root->right);
//}

//传地址
//void TreeSize2(BTNode* root, int* psize)
//{
//	if (root == NULL)
//		return;
//
//	++(*psize);
//	TreeSize2(root->left, psize);
//	TreeSize2(root->right, psize);
//}

//优化
int TreeSize3(BTNode* root)
{
	return root == NULL ? 0 :
		TreeSize3(root->left)
		+ TreeSize3(root->right)
		+ 1;
}

3.求叶子结点的个数

int BTreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0;

	if (root->left == NULL && root->right == NULL)
		return 1;

	return BTreeLeafSize(root->left) + BTreeLeafSize(root->right);
}

4.求二叉树的高度

int TreeHeight(BTNode* root)
{
	if (root == NULL)
		return 0;

	int leftHeight = TreeHeight(root->left);
	int rightHeight = TreeHeight(root->right);

	return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

5.求二叉树第K层结点个数

int TreeKLevel(BTNode* root, int k)
{
	assert(k > 0);

	if (root == NULL)
		return 0;

	if (k == 1)
		return 1;

	return TreeKLevel(root->left, k - 1)
		+ TreeKLevel(root->right, k - 1);
}

        


简介:

1.完全二叉树

2.大堆:树的任何一个父母结点都大于等于孩子结点;

   小堆:树的任何一个父母结点都小于等于孩子结点。

应用:

1.堆排序--O(N*logN)

//O(N*logN)
void HeapSort(int* a, int n)
{
	// 升序 -- 建大堆
	// 降序 -- 建小堆


	// 建堆--向上调整建堆
	/*for (int i = 1; i < n; i++)
	{
		AdjustUp(a, i);
	}*/

	// 建堆--向下调整建堆
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);
	}

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

		// 调整,选出次小的数
		AdjustDown(a, end, 0);

		--end;
	}
}

2.TOP-K

求数据中前K个最大(或最小)的元素 (例如:世界企业500强,高考排行前200,热门专业前10类)

a.堆排序(示例代码见上)

弊端:

(1)需要先创建一个堆

(2)空间复杂度+拷贝数据

b.采用堆排序的思想对数组直接调整

//时间复杂度 -- logN

void HeapSort(int* a, int 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);
	}*/
    //O(N*logN)--相对于一趟堆排序
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);

		end--;
	}
}

void TestSort()
{
	int a[] = { 3,1,4,1,0,2,5 };
	HeapSort(a, sizeof(a) / sizeof(int));

	for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

c.局限性

对于N个整数找出最大的前K个,正常思路:把这N个整数建成大堆,Pop K次即可

但是,有些场景下,上述思路无法解决问题(例如,当N非常大的时候--10亿个整数)

解决思路

(1)取前K个数建小堆

(2)剩余的数据依次与堆顶元素比较,如果大于堆顶元素,就替换它进堆

(3)最后这个小堆中的数据就是N个整数中最大的前K个

3.优先级队列

代码:

数组实现堆 + 动态内存管理

1.Heap.h

#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}HP;

//向上调整
void AdjustUp(HPDataType* a, int child);
//向下调整
void AdjustDown(int* a, int n, int parent);

void HeapInit(HP* php);
void HeapDestroy(HP* php);

//插入一个结点
void HeapPush(HP* php, HPDataType x);
//删除堆顶的元素
void HeapPop(HP* php);

//取堆顶元素
HPDataType HeapTop(HP* php);
bool HeapEmpty(HP* php);
int HeapSize(HP* php);

2.Heap.c

#include "Heap.h"

void HeapInit(HP* php)
{
	assert(php);
	php->a = NULL;
	php->capacity = 0;
	php->size = 0;
}

void HeapDestroy(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->capacity = 0;
	php->size = 0;
}

void Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}


void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;
	//while(parent >= 0) 行不行?
	while (child > 0)
	{
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);

			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

void AdjustDown(int* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		//选出左右孩子中较小的那一个
		if (child + 1 < n && a[child + 1] > a[child])
		{
			child++;
		}

		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);

			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

//log_2(N)
void HeapPush(HP* php, HPDataType x)
{
	assert(php);
	//如满拓展空间
	if (php->size == php->capacity)
	{
		int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(php->a, newCapacity * sizeof(HPDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}

		php->a = tmp;
		php->capacity = newCapacity;
	}
	php->a[php->size] = x;
	php->size++;

	AdjustUp(php->a, php->size - 1);
}

void HeapPop(HP* php)
{
	assert(php);
	assert(!HeapEmpty(php));

	Swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;

	AdjustDown(php->a, php->size, 0);
}

HPDataType HeapTop(HP* php)
{
	assert(php);
	assert(!HeapEmpty(php));

	return php->a[0];
}

bool HeapEmpty(HP* php)
{
	assert(php);

	return php->size == 0;
}

int HeapSize(HP* php)
{
	assert(php);

	return php->size;
}

图示助解:

a.HeapPush(插入一个元素)——时间复杂度O(logN)

对于完全二叉树,已知一个结点的下标n如何找出它的父母结点和左右孩子的结点呢?

parent = (n - 1) / 2;

leftchild = n * 2 + 1, rightchild = n * 2 + 2;

b.HeapPop(删除堆顶元素)——时间复杂度O(logN)

3.Test.c

#include "Heap.h"

int Test0()
{
	HP hp;
	HeapInit(&hp);
	int a[] = { 65,100,21,32,520,64 };

	for (int i = 0; i < sizeof(a) / sizeof(int); ++i)
	{
		HeapPush(&hp, a[i]);
	}

	while (!HeapEmpty(&hp))
	{
		int top = HeapTop(&hp);
		printf("%d\n", top);
		HeapPop(&hp);
	}

	return 0;
}

//对数组进行堆排序

// 可以这么玩吗?--可以
// 弊端:
// 1、需要先创建一个堆,太麻烦
// 2、空间复杂度+拷贝数据
// 
//void HeapSort(int* a, int n)
//{
//	HP hp;
//	HeapInit(&hp);
//	// N*logN
//	for (int i = 0; i < n; ++i)
//	{
//		HeapPush(&hp, a[i]);
//	}
//
//	// N*logN
//	int j = 0;
//	while (!HeapEmpty(&hp))
//	{
//		int top = HeapTop(&hp);
//		a[j++] = top;
//		HeapPop(&hp);
//	}
//
//	HeapDestroy(&hp);
//}

//O(N*logN)
void HeapSort(int* a, int n)
{
	// 升序 -- 建大堆
	// 降序 -- 建小堆


	// 建堆--向上调整建堆
	/*for (int i = 1; i < n; i++)
	{
		AdjustUp(a, i);
	}*/

	// 建堆--向下调整建堆
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);
	}

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

		// 再调整,选出次小的数
		AdjustDown(a, end, 0);

		--end;
	}
}

void TestSort()
{
	int a[] = { 3,1,8,4,5,2,0 };
	HeapSort(a, sizeof(a) / sizeof(int));

	for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

int main()
{
	//Test0();
	
	TestSort();

	return 0;
}


总结

猜你喜欢

转载自blog.csdn.net/weixin_72501602/article/details/130791043