首页 > 其他分享 >堆结构和堆排序

堆结构和堆排序

时间:2023-12-17 09:44:17浏览次数:27  
标签:index int 堆排序 heapSize largest 结构 节点 left

堆是一种特殊的完全二叉树,其他语言中的优先级队列就是堆。堆分为大根堆和小根堆,大根堆即对于每一颗树,它的父亲节点的值,一定大于它的孩子节点的值,左右节点的值不用管它的顺序。小根堆同理。

堆的实现通常是用数组实现的,那么对于每一个节点在数组中怎么找到它的父节点和它的左右孩子就成了一个问题。

那么对于任意一个节点i,它的父节点在数组中的表示为(i - 1) / 2,它的左孩子为2 * i + 1,它的右孩子为2 * i + 2。

那么堆最主要的就两个操作,在堆中插入一个节点和在堆中删掉一个节点。

在堆中插入一个节点,也就是把要插入的元素放在数组的末尾,再往上调整。

代码实现

void heapinsert(ELEMENT *a, int index)//把下标index的值插入到堆中 
{
	while(a[index] > a[(index - 1) / 2])//如果下标index的值大于它的父节点 
	{
		swap(a, index, (index - 1) / 2);//下标index的值和它的父节点的值交换 
		index = (index - 1) / 2;//更新index的位置 
	}
}

在堆中删除一个节点,就是把树根的值取出,再把堆中最后一个元素放在树根,再向下调整。

代码实现

void heapify(ELEMENT *a, int index, int heapSize)//index位置的数变小了,又想维持大根堆结构,向下调整大根堆 
{
	int left = 2 *index + 1;//下标index的左孩子 
	
	while(left < heapSize)//只要还有孩子 
	{
		int largest = left + 1 < heapSize && a[left + 1] > a[left]? left + 1: left;//如果有右孩子并且右孩子的值大于左孩子的值,那么右孩子就是最大的,否则就是左孩子最大 
		
		largest = a[largest] > a[index]? largest:index;//如果左右孩子中最大的比父节点大,那么最大的还是它,没有父节点大,那么最大的是父节点 
		
		if (largest == index) {//如果最大的是父节点,那么不用调整了,是大根堆了 
			break;
		}
		
		swap(a, largest, index);//父节点与左右孩子中最大的做交换 
		index = largest;//更新index 
		left = index * 2 + 1;//重新计算左孩子的位置 
	}
}

堆排序

堆排序的时间复杂度是O(N * logN),它就是把数组转化成一个堆之后,再每次把堆的第一个元素放在一个需要放的位置,然后调整堆。当堆中只有一个元素之后就已经排好序了。

也就是分为了两个步骤,第一步建立一个堆,第二步在删除堆顶元素之后调整堆。

调整过程的时间复杂度是O(N * logN),这是无法再改变的。

但建立堆的是时间复杂度可以做到O(N),建立堆分为从顶建堆和从底建堆。

从顶建堆的时间复杂度是O(N * logN)。

//从顶建堆
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#define N 100000

typedef int ELEMENT;

void heapSort(int *a, int size);
void heapinsert(int *a, int index);
void heapify(int *a, int index, int heapSize);
void swap(int *a, int i, int j);

int main() {
    int a[N];
    
    srand(time(NULL));
    
    int n;
	
	scanf("%d", &n);
    
    for (int i = 0; i < N; i++){
		scanf("%d", a + i);
	}
	
	heapSort(a, n);

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

    return 0;
}
void heapSort(ELEMENT *a, int size)
{
	if (a == NULL || size == 2){
		return ;
	}
	
	int heapSize = size;
	
	for (int i = 0; i < size; i++)//从顶建立一个大根堆 
	{
		heapinsert(a, i);
	}
	
	swap(a, 0, --heapSize);//把堆的第一个元素与堆的最后一个元素交换,然后堆的大小减一 
	
	while(heapSize > 1){//只要堆中的元素大于1 
		heapify(a, 0, heapSize);//因为0位置的数变小了,向下调整大根堆 
		swap(a, 0, --heapSize);//把堆的第一个元素与堆的最后一个元素交换,然后堆的大小减一 
	}
}
void heapinsert(ELEMENT *a, int index)//index位置的数向上调整大根堆 
{
	while(a[index] > a[(index - 1) / 2])//如果下标index的值大于它的父节点 
	{
		swap(a, index, (index - 1) / 2);//下标index的值和它的父节点的值交换 
		index = (index - 1) / 2;//更新index的位置 
	}
}
void heapify(ELEMENT *a, int index, int heapSize)//index位置的数变小了,又想维持大根堆结构,向下调整大根堆 
{
	int left = 2 *index + 1;//下标index的左孩子 
	
	while(left < heapSize)//只要还有孩子 
	{
		int largest = left + 1 < heapSize && a[left + 1] > a[left]? left + 1: left;//如果有右孩子并且右孩子的值大于左孩子的值,那么右孩子就是最大的,否则就是左孩子最大 
		
		largest = a[largest] > a[index]? largest:index;//如果左右孩子中最大的比父节点大,那么最大的还是它,没有父节点大,那么最大的是父节点 
		
		if (largest == index) {//如果最大的是父节点,那么不用调整了,是大根堆了 
			break;
		}
		
		swap(a, largest, index);//父节点与左右孩子中最大的做交换 
		index = largest;//更新index 
		left = index * 2 + 1;//重新计算左孩子的位置 
	}
}
void swap(ELEMENT *a, int i, int j)
{
	ELEMENT t = a[i];
	a[i] = a[j];
	a[j] = t;
}

从底建堆的时间复杂度是O(N),因为它调整的次数变少了。

代码实现

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#define N 100000

typedef int ELEMENT;

void heapSort(int *a, int size);
void heapinsert(int *a, int index);
void heapify(int *a, int index, int heapSize);
void swap(int *a, int i, int j);

int main() {
    int a[N];
    
    srand(time(NULL));
    
    int n;
	
	scanf("%d", &n);
    
    for (int i = 0; i < N; i++){
		scanf("%d", a + i);
	}
	
	heapSort(a, n);

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

    return 0;
}
void heapSort(ELEMENT *a, int size)
{
	if (a == NULL || size == 2){
		return ;
	}
	
	int heapSize = size;
	
	for (int i = size - 1; i >= 0; i--)//从底建立一个大根堆 
	{
		heapify(a, i, size);
	}
	
	swap(a, 0, --heapSize);//把堆的第一个元素与堆的最后一个元素交换,然后堆的大小减一 
	
	while(heapSize > 1){//只要堆中的元素大于1 
		heapify(a, 0, heapSize);//因为0位置的数变小了,向下调整大根堆 
		swap(a, 0, --heapSize);//把堆的第一个元素与堆的最后一个元素交换,然后堆的大小减一 
	}
}
void heapinsert(ELEMENT *a, int index)//index位置的数向上调整大根堆 
{
	while(a[index] > a[(index - 1) / 2])//如果下标index的值大于它的父节点 
	{
		swap(a, index, (index - 1) / 2);//下标index的值和它的父节点的值交换 
		index = (index - 1) / 2;//更新index的位置 
	}
}
void heapify(ELEMENT *a, int index, int heapSize)//index位置的数变小了,又想维持大根堆结构,向下调整大根堆 
{
	int left = 2 *index + 1;//下标index的左孩子 
	
	while(left < heapSize)//只要还有孩子 
	{
		int largest = left + 1 < heapSize && a[left + 1] > a[left]? left + 1: left;//如果有右孩子并且右孩子的值大于左孩子的值,那么右孩子就是最大的,否则就是左孩子最大 
		
		largest = a[largest] > a[index]? largest:index;//如果左右孩子中最大的比父节点大,那么最大的还是它,没有父节点大,那么最大的是父节点 
		
		if (largest == index) {//如果最大的是父节点,那么不用调整了,是大根堆了 
			break;
		}
		
		swap(a, largest, index);//父节点与左右孩子中最大的做交换 
		index = largest;//更新index 
		left = index * 2 + 1;//重新计算左孩子的位置 
	}
}
void swap(ELEMENT *a, int i, int j)
{
	ELEMENT t = a[i];
	a[i] = a[j];
	a[j] = t;
}

 

标签:index,int,堆排序,heapSize,largest,结构,节点,left
From: https://www.cnblogs.com/lwj1239/p/17908645.html

相关文章

  • 数据结构绪论
    数据定义:数据是信息的载体,所有能被输入到计算机中,且能被计算机处理的符号的集合。例:在生活中的各种信息都可以作为数据来进行输入和处理。eg:图片·身份信息等。数据元素定义:数据元素是数据的基本单位,常被作为一个整体来考虑。例如:每个学生信息就是数据元素数据项定义:数据......
  • 数据结构与算法 第一章(48课时课程笔记)Data Structure and Algorithms
    数据结构基础知识 数据(Data):是对信息的一种符号表示。在计算机科学中是指所有能输入到计算机中并被计算机程序处理的符号的总称。数据元素(DataElement):是数据的基本单位,在计算机程序中通常作为一个整体进行考虑和处理。一个数据元素可由若干个数据项(DataItem)组成。数据......
  • 结构化开发方法——03
    抽象化定义:从概要设计到详细设计的抽象化层次逐次降低。在最高的抽象层次上,可以使用问题所处环境的语言描述问题的解法;在较低的抽象层次上,则采用过程化的方法,产生源程序时到达最低的抽象层次。分为3种:过程的抽象:在从概要设计到详细设计的过程中,抽象化的层次逐次降低,当产生源程序时......
  • window10下生成目录结构树
    大家看博客的时候应该看到过这种目录结构展示可以手敲出来,但是麻烦,我们可以命令生成 cmd,进入要生成目录结构树的目录 预览目录结构:tree 目录结构写到文件:tree>dir.txt dir.txt内容 如果要显示文件名,加个参数即可:tree/f,tree/f>dir.txt ......
  • 结构性设计模式-适配器模式的优缺点
    把一个类的接口变换成客户端所期待的另一种接口,从而使原本接口不匹配而无法一起工作的两个类能够在一起工作。类的适配器模式对象的适配器模式对象适配器在上图中可以看出:冲突:Target期待调用Request方法,而Adaptee并没有(这就是所谓的不兼容了)。解决方案:为使Target能够使用Ad......
  • Redis数据结构8:REDIS_HASH
    REDIS_HASHHash本质上就是一个保存若干键值对的数据结构,类似于Java中的HashMap。同样的,hash中只能存在一个独一无二的key,所有的操作都围绕key展开。hash的最大优点在于其可以提供最佳O(1)的查询时间复杂度。通过一段原始数据key,通过特定算法将其哈希值转化为数组下标,通过相同的......
  • 入门篇-其之十-流程控制之循环结构
    本文中使用到的工具是IntellijIDEA和JDK8,需要安装两款工具的请查看这两篇教程:点我查看安装JDK8/11/17教程、点我查看安装IntellijIDEA教程。假设输出1~100之间的所有整数,正常情况下我们需要写100行代码才能对所有数字输出。System.out.println(1);System.out.println(2);......
  • Java中常见的数据结构
    一、数组二、链表三、栈四、队列五、List类1.ArrayList:底层是数组结构。2.LinkedList:底层结构是链表。六、LinkedList类七、Vector八、HashSet集合九、LinkedHashSet集合......
  • Oracle内核技术揭秘 -- 存储结构
    区:表空间中的基本单位在Oracle11.2.0.3以上的版本中,创建新表默认不会分配区给这个表的,只有在插入了数据之后才会分配一个区给这个表空间。区是表空间中空间分配的基本单位,如果一个区的空间用完了,Oracle就会默认再分配一个区。Oracle专门设定了两种类型的表空间:统一大小表空间和......
  • 实验6 c语言结构体,枚举应用编程
    task4源代码1#include<stdio.h>2#include<string.h>3#defineN1045typedefstruct{6charisbn[20];//isbn号7charname[80];//书名8charauthor[80];//作者9doublesales_price;//......