【数据结构初阶】二叉树(2)

news/2024/11/30 20:40:41/

二叉树顺序结构

    • 1.二叉树的顺序结构及实现
      • 1.1二叉树的顺序结构
    • 1.2 堆的概念及结构
      • 1.3 堆的实现
      • 1.3.1向上调整
      • 1.3.2向下调整
      • 1.3.3交换函数
      • 1.3.4打印
      • 1.3.5初始化
      • 1.3.6销毁
      • 1.3.7插入
      • 1.3.8删除
      • 1.3.9获得堆顶元素
      • 1.3.10判断是否为空
      • 1.3.6 堆的代码实现
    • 1.3.2堆的创建
    • 1.3.3 建堆时间复杂度
    • 1.4 堆的应用
      • 1.4.1 堆排序

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

1.1二叉树的顺序结构

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。

在这里插入图片描述

1.2 堆的概念及结构

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

  • 堆中某个节点的值总是不大于或不小于其父节点的值;
  • 堆总是一棵完全二叉树。
    在这里插入图片描述
    练习题:
    1.下列关键字序列为堆的是:()
    A 100,60,70,50,32,65
    B 60,70,65,50,32,100
    C 65,100,70,32,50,60
    D 70,65,100,32,50,60
    E 32,50,100,70,65,60
    F 50,100,70,65,60,32
    2.已知小根堆为8,15,10,21,34,16,12,删除关键字 8 之后需重建堆,在此过程中,关键字之间的比较次
    数是()。
    A 1
    B 2
    C 3
    D 4
    3.一组记录排序码为(5 11 7 2 3 17),则利用堆排序方法建立的初始堆为
    A(11 5 7 2 3 17)
    B(11 5 7 2 17 3)
    C(17 11 7 2 3 5)
    D(17 11 7 5 3 2)
    E(17 7 11 3 5 2)
    F(17 7 11 3 2 5)
    4.最小堆[0,3,2,5,7,4,6,8],在删除堆顶元素0之后,其结果是()
    A[3,2,5,7,4,6,8]
    B[2,3,5,7,4,6,8]
    C[2,3,4,5,7,8,6]
    D[2,3,4,5,6,7,8]

练习题答案
1.A 2.C 3.C 4.C

1.3 堆的实现

1.3.1向上调整

在这里插入图片描述

向上调整前提:前面数据是堆

void AdjustUp(HPDateType* a, int child)
{int parent = (child - 1) / 2;while (child > 0){if (a[child] > a[parent]){swap(&a[child], &a[parent]);child = parent;parent = (parent - 1) / 2;}else{break;}}
}

1.3.2向下调整

int array[] = {27,15,19,18,28,34,65,49,25,37};

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

在这里插入图片描述

void AdjustDown(HPDateType* a, int n, int parent)
{int child = parent * 2 + 1;while (child > 0){if ((child + 1 > n) && a[child + 1] > a[child]){child++;}if (a[parent] > a[child]){swap(&a[parent], &a[child]);child = parent;parent = parent * 2 + 1;}else{break;}}
}

1.3.3交换函数

void swap(HPDateType* p1, HPDateType* p2)
{HPDateType tmp = *p1;*p1 = *p2;*p2 = tmp;
}

1.3.4打印

void HeapPrint(HP* php)
{assert(php);for(size_t i = 0; i < php->size; i++){printf("%d ", php->a[i]);}printf("\n");
}

1.3.5初始化

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

1.3.6销毁

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

1.3.7插入

先插入一个10到数组的尾上,再进行向上调整算法直到满足堆。
在这里插入图片描述

void HeapPush(HP* php, HPDateType x)
{assert(php);if (php->size == php->capacity){int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDateType* tmp = (HPDateType*)realloc(php->a, sizeof(HPDateType) * newCapacity);php->capacity = newCapacity;php->a = tmp;}php->a[php->size] = x;php->size++;AdjustUp(php->a, php->size - 1);
}

1.3.8删除

删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。
在这里插入图片描述

void HeapPop(HP* php)
{assert(php);assert(php->size > 0);swap(&php->a[0], &php->a[php->size]);php->size--;AdjustDown(php->a, php->size, 0);
}

1.3.9获得堆顶元素

HPDateType HeadTop(HP* php)
{assert(php);assert(php->size > 0);return php->a[0];
}

1.3.10判断是否为空

bool HeapEmpty(HP* php)
{assert(php);return php->size == 0;
}

1.3.6 堆的代码实现

Heap.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>typedef int HPDateType;
typedef struct Heap
{HPDateType* a;int size;int capacity;
}HP;//向上调整
void AdjustUp(HPDateType* a, int child);
//向下调整
void AdjustDown(HPDateType* a, int n, int parent);
//交换函数
void swap(HPDateType* p1, HPDateType* p2);
//打印
void HeapPrint(HP* php);
//初始化
void HeapInit(HP* php);
//销毁
void HeapDestroy(HP* php);
//插入
void HeapPush(HP* php, HPDateType x);
//删除
void HeapPop(HP* php);
//获得堆顶元素
HPDateType HeapTop(HP* php);
//判断是否为空
bool HeapEmpty(HP* php);

Heap.c

#include"Heap.h"void HeapInit(HP* php)
{assert(php);php->a = NULL;php->size = 0;php->capacity = 0;
}void HeapDestroy(HP* php)
{assert(php);free(php->a);php->a = NULL;php->size = php->capacity = 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 (child > 0){if (a[child] > a[parent]){Swap(&a[child], &a[parent]);child = parent;parent = (parent - 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;}if (a[child] > a[parent]){Swap(&a[child], &a[parent]);// 继续往下调整parent = child;child = parent * 2 + 1;}else{break;}}
}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, sizeof(HPDataType) * newCapacity);if (tmp == NULL){perror("realloc fail");exit(-1);}php->a = tmp;php->capacity = newCapacity;}php->a[php->size] = x;php->size++;AdjustUp(php->a, php->size - 1);
}void HeapPrint(HP* php)
{assert(php);for (size_t i = 0; i < php->size; i++){printf("%d ", php->a[i]);}printf("\n");
}void HeapPop(HP* php)
{assert(php);assert(php->size > 0);Swap(&php->a[0], &php->a[php->size - 1]);php->size--;AdjustDown(php->a, php->size, 0);
}HPDataType HeapTop(HP* php)
{assert(php);assert(php->size > 0);return php->a[0];
}bool HeapEmpty(HP* php)
{assert(php);return php->size == 0;
}

test.c

int main()
{int a[] = { 65,100,70,32,50,60 };HP hp;HeapInit(&hp);for(int i = 0; i < sizeof(a) / sizeof(int); i++){HeapPush(&hp, a[i]);}HeapPrint(&hp);return 0;
}

利用堆的代码,我们可以将数组排成有序,如下:

int main()
{int a[] = { 2,3,5,7,4,6,8,65,100,70,32,50,60 };HP hp;HeapInit(&hp);for (int i = 0; i < sizeof(a)/sizeof(int); i++){HeapPush(&hp, a[i]);}HeapPrint(&hp);int k = 5;while (!HeapEmpty(&hp) && k--){printf("%d ", HeapTop(&hp));HeapPop(&hp);}HeapDestroy(&hp);return 0;
}

1.3.2堆的创建

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

int a[] = {1,5,3,8,7,6};

在这里插入图片描述

1.3.3 建堆时间复杂度

因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的就是近似值,多几个节点不影响最终结果):
在这里插入图片描述
因此:建堆的时间复杂度为O(N)。

1.4 堆的应用

1.4.1 堆排序

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

  1. 建堆
    升序:建大堆
    降序:建小堆

建堆有两种方法:
(1)使用向上调整,插入数据的思想建堆。插入数据到新的数组,就是在不断插入的过程中向上调整实现排序

void Swap(HPDataType* pa, HPDataType* pb)
{HPDataType tmp = *pa;*pa = *pb;*pb = tmp;
}
void AdjustUp(HPDataType* a, size_t child)
{size_t parent = (child - 1) / 2;while (child > 0){if (a[child] < a[parent]){Swap(&a[child], &a[parent]);child = parent;parent = (child - 1) / 2;}else{break;//跳出循环}}
}
void HeapSort(int* a, int n)
{//升序,建大堆,向上size_t i = 0;for (i = 1; i < n; ++i){AdjustUp(a, i);}
}int main()
{int a[] = { 4, 3, 10 , 2, 5, 9 };HeapSort(a, sizeof(a) / sizeof(int));for (int i = 0; i < sizeof(a) / sizeof(int); i++){printf("%d ", a[i]);}printf("\n");return 0;
}

(2)使用向下调整,从倒数第一个非叶子节点开始,即最后一个节点的父亲,即[(size-1-1)/ 2 ]【找到这个父亲的节点,向下排序,然后这个父亲节点依次减一【就找到各个小堆,依次向下排序,就成为了一个堆。

void HeapSort(int* a, int n)
{//升序,建堆,向上/*int i = 0;for (i = 1; i < n; ++i){AdjustUp(a, i);}*///向下int i = 0;for (i = (n - 2) / 2; i >= 0; --i){AdjustDown(a, i, n);}
}

💘不知不觉,【数据结构初阶】二叉树(2)以告一段落。通读全文的你肯定收获满满,让我们继续为数据结构学习共同奋进!!!


http://www.ppmy.cn/news/1290554.html

相关文章

SpringBoot学习(三)-员工管理系统开发(重在理解)

注&#xff1a;此为笔者学习狂神说SpringBoot的笔记&#xff0c;其中包含个人的笔记和理解&#xff0c;仅做学习笔记之用&#xff0c;更多详细资讯请出门左拐B站&#xff1a;狂神说!!! 本文是基于狂神老师SpringBoot教程中的员工管理系统从0到1的实践和理解。该系统应用SpringB…

音频筑基:巴克谱和梅尔谱辨析

音频筑基&#xff1a;巴克谱和梅尔谱辨析 是什么深入了解相关参考 在音频信号处理中&#xff0c;巴克谱和梅尔谱是我们经常遇到的概念&#xff0c;也是语音处理中常用到的频域特征&#xff0c;这里谈谈自己对它们的理解。 是什么 巴克谱又称Bark Spectrum&#xff0c;梅尔谱又…

智能化校园:深入探讨云端管理系统设计与实现(二)

系列文章目录 智能化校园&#xff1a;深入探讨云端管理系统设计与实现&#xff08;一&#xff09; 文章目录 系列文章目录功能开发登录功能分析验证码功能实现登录校验功能登录后跳转功能 系统管理器实现验证码响应图片功能实现异步图片上传头像功能实现全局修改密码功能实现 …

28、商城系统(十):ElasticSearch的映射,nginx下载安装,es分词器,springboot整合es

目录 一、Mapping映射 1.es7删除类型 2.es给字段设置字段类型,即映射 (1)创建映射

python弹奏《起风了》

代码是很大的! 其实就是python用ctypes调用Win API import ctypes import threading import time winmm = ctypes.windll.winmmclass Scale:Rest = 0C8 = 108B7 = 107A7s = 106A7 = 105G7s = 104G7 = 103F7s = 102F7 = 101E7 = 100D7s = 99D7 = 98C7s = 97C7 = 96B6 = 95A6s…

4.Unity中向量相关

向量 //三维向量 - Vector3 //Vector3有两种几何意义 //1.位置 -- 代表一个点 print(this.transform.position);//2.方向 -- 代表一个方向 print(this.transform.forward); print(this.transform.up); 两点决定一个向量 //A和B此时 几何意义 是两个点Vector3 A new Vector3(…

elasticsearch操作索引库

目录 一、创建索引库 二、查询索引库 三、删除索引库 四、修改索引库 mapping映射属性 mapping是对索引库中文档的约束&#xff0c;常见的mapping属性包括&#xff1a; type&#xff1a;字段数据类型&#xff0c;常见的简单类型有&#xff1a; 字符串&#xff1a;text&…

【论文阅读】Self-Paced Curriculum Learning

论文下载 代码 Supplementary Materials bib: INPROCEEDINGS{,title {Self-Paced Curriculum Learning},author {Lu Jiang and Deyu Meng and Qian Zhao and Shiguang Shan and Alexander Hauptmann},booktitle {AAAI},year {2015},pages {2694--2700} }1. 摘…