树的简介
树是一种非线性结构, 树状结构是结点间有分支的, 层次的结构.
一些基本术语
度数: 一个结点的子树个数
树叶: 没有子树的结点称为树叶或终端结点
双亲结点或父节点: 若一个结点含有子节点, 则这个结点称为子节点的父节点
孩子节点或子节点: 一个结点含有的子树的根结点称为该节点的子节点
层数: 定义树根的层数为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);
}