树的简介

树是一种非线性结构, 树状结构是结点间有分支的, 层次的结构.

一些基本术语
度数: 一个结点的子树个数
树叶: 没有子树的结点称为树叶或终端结点
双亲结点或父节点: 若一个结点含有子节点, 则这个结点称为子节点的父节点
孩子节点或子节点: 一个结点含有的子树的根结点称为该节点的子节点
层数: 定义树根的层数为1, 其他结点的层数等于其父母结点的层数加1
高度或深度: 书中结点的最大层数, 规定空树的高度为0

树的表示

树结构相对线性表就比较复杂了, 要存储表示起来就比较麻烦了, 实际中树有很多种表示方式, 如: 双亲表示法, 孩子表示法, 孩子兄弟表示法等等. 我们这里就简单的了解其中最常用的孩子兄弟表示法

typedef int ElementType;

typedef struct Node {
	/*---数据域---*/
	ElementType value;

	/*---第一个孩子结点---*/
	struct Node* firstchild;

	/*---下一个兄弟结点---*/
	struct Node* nextchild;
} Node;

数据结构_二叉树和堆

二叉树概念及结构

二叉树是树状结构的另一个重要类型. 二叉树的每个结点至多有两个孩子结点, 而且孩子结点有左右之分. 二叉树的存储结构简单, 存储效率较高, 树运算的算法实现也相对简单. 二叉树还可用来表示树, 二叉树在数据结构中有着重要的地位

二叉树的五种基本形态
数据结构_二叉树和堆
特殊的二叉树
满二叉树: 一个二叉树, 如果每一个层的结点数都达到最大值, 则这个二叉树就是满二叉树. 也就是说, 如果一个二叉树的层数为K, 且结点总数是2^K - 1, 则它就是满二叉树.
数据结构_二叉树和堆
完全二叉树: 完全二叉树是效率很高的数据结构, 完全二叉树是由满二叉树而引出来的. 对于深度为K的, 有N个结点的二叉树, 当且仅当其每一个结点都与深度为K的满二叉树中编号从1至N的结点一一对应时称之为完全二叉树. 要注意的是满二叉树是一种特殊的二叉树.
数据结构_二叉树和堆
二叉树的存储结构
二叉树一般可以使用两种结构存储, 一种顺序结构, 一种链式结构.

顺序存储: 顺序结构存储就是使用数组来存储, 一般使用数组只适合表示完全二叉树, 因为不是完全二叉树会有空间的浪费. 而现实使用中只有堆才会使用数组来存储, 二叉树顺序存储在物理上是一个数组, 在逻辑上是一棵二叉树.

链式存储: 二叉树的链式存储结构是指, 用链表来表示一棵二叉树, 即用链来指示元素的逻辑关系. 通常的方法是链表中每个结点由三个域组成, 数据域和左右指针域, 左右指针分别用来给出该节点左右孩子所在的结点的存储地址. 链式结构分为二叉链和三叉链.
数据结构_二叉树和堆

typedef int ElementType;

/*---二叉链表---*/
typedef struct Node {
	struct Node* left;
	struct Node* right;
	ElementType value;
} Node;

/*---链表---*/
typedef struct Node {
	struct Node* parent;
	struct Node* left;
	struct Node* right;
	ElementType value;
} Node;

二叉树接口实现
binary_tree.h

#pragma once

typedef char ElementType;

/*---结点---*/
typedef struct Node {
	ElementType value;
	struct Node* left;
	struct Node* right;
} Node;

/*---前序创建树---*/
Node* btCreatePre(ElementType preorder[],
	int size, int* used);

/*---前序和中序创建树---*/
Node* btCreatePreAndIn(ElementType preorder[],
	ElementType inorder[], int size);

/*---中序和后序创建树---*/
Node* btCreateInAndPost(ElementType inorder[], 
	ElementType postorder[], int size);

/*---前序遍历---*/
void btPreOrder(Node* root);

/*---中序遍历---*/
void btInOrder(Node* root);

/*---后序遍历---*/
void btPostOrder(Node* root);

/*---结点数---*/
size_t btSize(const Node* root);

/*---叶子数---*/
size_t btLeafSize(const Node* root);

/*---树高---*/
size_t btHeight(const Node* root);

/*---K层数量---*/
size_t btKthLevelSize(const Node* root, size_t k);

/*---查找---*/
const Node* btFind(const Node* root, ElementType v);

/*---相同---*/
bool btSame(const Node* root1, const Node* root2);

/*---镜像---*/
bool btMirror(const Node* root1, const Node* root2);

/*---测试---*/
void btTest();

binary_tree.c

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdbool.h>
#include "binary_tree.h"

Node* btCreatePre(ElementType preorder[], 
	int size, int* used){
	if (size == 0){
		*used = 0;
		return NULL;
	}
	if (preorder[0] == '#'){
		*used = 1;
		return NULL;
	}

	Node* root = (Node*)malloc(sizeof(Node));
	assert(root != NULL);
	root->value = preorder[0];

	int left_used;
	root->left = btCreatePre(preorder + 1, size - 1, &left_used);

	int right_used;
	root->right = btCreatePre(preorder + 1 + left_used,
		size - 1 - left_used, &right_used);

	*used = left_used + right_used + 1;
	return root;
}

static int eleFind(ElementType inorder[], ElementType ele, int size){
	int i = 0;
	for (i = 0; i < size; ++i){
		if (inorder[i] == ele){
			return i;
		}
	}
	return -1;
}

Node* btCreatePreAndIn(ElementType preorder[], 
	ElementType inorder[], int size){
	if (size == 0){
		return NULL;
	}

	ElementType root_value = preorder[0];
	int left_size = eleFind(inorder, root_value, size);

	Node* root = (Node*)malloc(sizeof(Node));
	root->value = root_value;

	root->left = btCreatePreAndIn(preorder + 1, inorder, left_size);

	root->right = btCreatePreAndIn(preorder + 1 + left_size,
		inorder + 1 + left_size, size - 1 - left_size);

	return root;
}

Node* btCreateInAndPost(ElementType inorder[], 
	ElementType postorder[], int size){
	if (size == 0){
		return NULL;
	}

	ElementType root_value = postorder[size - 1];
	int left_size = eleFind(inorder, root_value, size);

	Node* root = (Node*)malloc(sizeof(Node));
	assert(root != NULL);
	root->value = root_value;
	
	root->left = btCreateInAndPost(inorder, postorder, left_size);

	root->right = btCreateInAndPost(inorder + left_size + 1, 
		postorder + left_size, size - left_size - 1);

	return root;
}

void btPreOrder(Node* root){
	if (root == NULL){
		return;
	}

	printf("%c ", root->value);

	btPreOrder(root->left);

	btPreOrder(root->right);
}

void btInOrder(Node* root){
	if (root == NULL){
		return;
	}

	btInOrder(root->left);

	printf("%c ", root->value);

	btInOrder(root->right);
}

void btPostOrder(Node* root){
	if (root == NULL){
		return;
	}

	btPostOrder(root->left);

	btPostOrder(root->right);

	printf("%c ", root->value);
}

size_t btSize(const Node* root){
	if (root == NULL){
		return 0;
	}

	size_t left = btSize(root->left);

	size_t right = btSize(root->right);

	return left + right + 1;
}

size_t btLeafSize(const Node* root){
	if (root == NULL){
		return 0;
	}

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

	size_t left = btLeafSize(root->left);

	size_t right = btLeafSize(root->right);

	return left + right;
}

size_t btHeight(const Node* root){
	if (root == NULL){
		return 0;
	}

	size_t left = btHeight(root->left) + 1;

	size_t right = btHeight(root->right) + 1;

	return left > right ? left : right;
}

size_t btKthLevelSize(const Node* root, size_t k){
	if (root == NULL){
		return 0;
	}

	if (k == 1){
		return 1;
	}

	size_t left = btKthLevelSize(root->left, k - 1);

	size_t right = btKthLevelSize(root->right, k - 1);

	return left + right;
}

const Node* btFind(const Node* root, ElementType v){
	if (root == NULL){
		return NULL;
	}

	if (root->value == v){
		return root;
	}

	const Node* temp = btFind(root->left, v);
	if (temp != NULL){
		return temp;
	}

	temp = btFind(root->right, v);
	if (temp != NULL){
		return temp;
	}

	return NULL;
}

bool btSame(const Node* root1, const Node* root2){
	if (root1 == NULL && root2 == NULL){
		return true;
	}

	if (root1 == NULL || root2 == NULL){
		return false;
	}

	return root1->value == root2->value &&
		btSame(root1->left, root2->left) &&
		btSame(root1->right, root2->right);
}

bool btMirror(const Node* root1, const Node* root2){
	if (root1 == NULL && root2 == NULL){
		return true;
	}

	if (root1 == NULL || root2 == NULL){
		return false;
	}
	
	return root1->value == root2->value &&
		btMirror(root1->left, root2->right) &&
		btMirror(root1->right, root2->left);
}

void btTest(){
	char* preorder = "ABDF####C#E#G";
	int size = strlen(preorder);
	int used;

	Node* root = btCreatePre(preorder, size, &used);

	printf("The Binary Tree's PreOrder is: ");
	btPreOrder(root);
	printf("\n");

	size_t num = btSize(root);
	printf("The Binary Tree has %lu nodes!\n", num);

	size_t leaf_num = btLeafSize(root);
	printf("The Binary Tree has %lu leaf nodes!\n", leaf_num);

	size_t height = btHeight(root);
	printf("The Binary Tree's Height is: %lu!\n", height);

	const Node* node = btFind(root, 'A');
	printf("root->value: %c, root->left: %c, root->right: %c\n", 
		node->value, node->left->value, node->right->value);
}

堆的概念及结构

堆, 又叫二叉堆, 采用顺序存储的树结构, 逻辑上是一棵完全二叉树, 存在数组中

大堆和小堆
大堆: 任取一个结点, 要求根的值大于或等于左右孩子的值
小堆: 任取一个结点, 要求根的值小于或等于左右孩子的值

堆的用途
找最值, 尤其是在动态找最值, 多次有增减的情况下

主要操作
向下调整
前提: 只有一个位置不满足堆性质
调整: ①找到根, 左孩子, 右孩子三个中最小的一个, 和根进行交换, 只要不是叶子, 一定有左孩子, 但不一定有右孩子 ②停止条件, 走到叶子位置, 已经满足堆的性质, 刚才三个中最小的是根

建堆
把一个完全无序的随机分配的数组变成满足堆性质, 从最后一个非叶子结点一直到0, 进行向下调整, 最后一个结点的双亲结点就是最后一个非叶子结点, (size - 1 - 1) / 2, 为什么要倒着调整, 向下调整的前提有限制, 必须左右子树已经是堆, 才可以进行向下调整, 所以需要倒着调整

向上调整

堆的应用
① 优先级队列
② 排序
堆排序, 排升序, 建大堆, 建小堆破坏堆结构
排升序, 建小堆, 建大堆破坏堆结构
③ TopK问题
给海量数据(个数N个), 找到其中最大的K个, 建小堆

接口实现
heap.h

#pragma once

typedef int ElementType;

/*---堆---*/
typedef struct Heap {
	ElementType* heap;
	size_t size;
	size_t capacity;
} Heap;

/*---初始化---*/
void heapInit(Heap* h);

/*---释放---*/
void heapDestory(Heap* h);

/*---入堆---*/
void heapPush(Heap* h, ElementType x);

/*---出堆---*/
void heapPop(Heap* h);

/*---堆顶元素---*/
ElementType heapTop(const Heap* h);

/*---堆大小---*/
size_t heapSize(const Heap* h);

/*---空堆---*/
bool heapEmpty(const Heap* h);

/*---堆排序---*/
void heapSort(Heap* h);

/*---打印---*/
void heapDisplay(const Heap* h);

/*---堆测试---*/
void heapTest();

heap.c

#define _CRT_SECURE_NO_WARNINGS

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

void heapInit(Heap* h){
	if (h == NULL){
		return;
	}

	h->capacity = 1;
	h->size = 0;
	h->heap = (ElementType*)malloc(sizeof(ElementType) * h->capacity);
	assert(h->heap != NULL);
}

void heapDestory(Heap* h){
	if (h == NULL){
		return;
	}

	h->size = 0;
	h->capacity = 0;
	free(h->heap);
	h->heap = NULL;
}

static void checkCapacity(Heap* h){
	if (h == NULL){
		return;
	}

	if (h->size >= h->capacity){
		h->capacity *= 2;
		h->heap = (ElementType*)realloc(h->heap,
			sizeof(ElementType) * h->capacity);
	}
}

static void swap(int* a, int* b){
	assert(a != NULL);
	assert(b != NULL);
	int temp = *a;
	*a = *b;
	*b = temp;
}

static void upwardAdjustment(Heap* h, int child){
	if (h == NULL){
		return;
	}
	
	if (child == 0){
		return;
	}

	int parent = (child - 1) / 2;
	if (parent < 0 || h->heap[child] <= h->heap[parent]){
		return;
	}

	swap(&h->heap[child], &h->heap[parent]);

	upwardAdjustment(h, parent);
}

void heapPush(Heap* h, ElementType x){
	if (h == NULL){
		return;
	}

	checkCapacity(h);
	++h->size;
	h->heap[h->size - 1] = x;

	upwardAdjustment(h, h->size - 1);
}

static void downwardAdjustment(ElementType heap[], size_t size, size_t root_index){
	if (heap == NULL){
		return;
	}

	size_t left_index = 2 * root_index + 1;
	if (2 * root_index + 1 >= size){
		return;
	}

	size_t max_index = left_index;
	size_t right_index = 2 * root_index + 2;
	if (right_index < size && heap[right_index] > heap[left_index]){
		max_index = right_index;
	}

	if (heap[root_index] < heap[max_index]){
		swap(&heap[root_index], &heap[max_index]);
	}
	else{
		return;
	}

	downwardAdjustment(heap, size, max_index);
}

void heapPop(Heap* h){
	if (h == NULL){
		return;
	}

	swap(&h->heap[0], &h->heap[h->size - 1]);

	--h->size;

	downwardAdjustment(h->heap, h->size, 0);
}

ElementType heapTop(const Heap* h){
	assert(h != NULL);

	return h->heap[0];
}

size_t heapSize(const Heap* h){
	if (h == NULL){
		return 0;
	}

	return h->size;
}

bool heapEmpty(const Heap* h){
	if (h == NULL){
		return true;
	}

	return h->size == 0 ? 1 : 0;
}

void heapSort(Heap* h){
	if (h == NULL){
		return;
	}

	size_t i = 0;
	for (i = 0; i < h->size; ++i){
		swap(&h->heap[0], &h->heap[h->size - 1 - i]);
		downwardAdjustment(h->heap, h->size - 1 - i, 0);
	}

}

void heapDisplay(const Heap* h){
	if (h == NULL){
		return;
	}

	size_t i = 0;
	printf("The Heap is below: \n");
	for (i = 0; i < h->size; ++i){
		printf("%d ", h->heap[i]);
	}
	printf("\n");
}

void heapTest(){
	Heap h;

	heapInit(&h);

	heapPush(&h, 1);
	heapPush(&h, 2);
	heapPush(&h, 3);
	heapPush(&h, 5);
	heapPush(&h, 0);
	heapPush(&h, 8);

	heapPop(&h);

	ElementType top = heapTop(&h);
	printf("Heap Top: %lu\n", top);

	size_t size = heapSize(&h);
	printf("Heap Size: %lu\n", size);

	if (heapEmpty(&h)){
		printf("Heap is Empty!\n");
	}
	else{
		printf("Heap isn't Empty!\n");
	}

	heapDisplay(&h);

	heapSort(&h);

	heapDisplay(&h);
}

相关文章: