【数据结构】堆排序和top-K问题

堆的实现源码

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
#include <assert.h>
typedef struct Heap
{
	int* a;
	int size;
	int capacity;
}Heap;
void HeapInit(Heap* st)
{
	st->a = NULL;
	st->capacity = 0;
	st->size = 0;
}
void swap(int* str1, int* str2)
{
	int tmp = *str1;
	*str1 = *str2;
	*str2 = tmp;

}
void Adjustup(int* a, int child)
{
	int 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 HeapPush(Heap* st, int x)
{
	if (st->capacity == st->size)
	{
		int newcapcity = st->capacity == 0 ? 4 : st->capacity * 2;
		int* tmp = (int*)realloc(st->a, newcapcity * sizeof(int));
		if (tmp == NULL)
		{
			perror("realloc fail");




		}
		st->a = tmp;
		st->capacity = newcapcity;
	}
	st->a[st->size] = x;
	st->size++;
	Adjustup(st->a, st->size - 1);
}
void Heapprint(Heap* st)
{

	for (int i = 0; i < st->size; i++)
	{
		printf("%d ", st->a[i]);
	}







}
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;
		}













	}
}
bool HeapEmpty(Heap* st)
{
	assert(st);
	if (st->size == 0)
	{
		return true;



	}
	else
	{
		return false;

	}

}
int HeapSize(Heap* st)
{
	assert(st);
	return st->size;
}
void HeapPop(Heap* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));
	swap(&hp->a[0], &hp->a[hp->size - 1]);
	hp->size--;
	AdjustDown(hp->a, hp->size, 0);
}
void HeapDestroy(Heap* st)
{
	assert(st);
	free(st->a);
	st->a = NULL;
	st->size = 0;
	st->capacity = 0;
}
int HeapTop(Heap* st)
{
	assert(st);
	assert(!HeapEmpty(st));
	return st->a[0];

}

使用堆结构实现堆排序

void HeapSort(int* a, int n)
{
	Heap hp;

	HeapInit(&hp);//初始化堆
for (int i = 0; i < 10; i++)
{
	HeapPush(&hp, a[i]);//堆中插入元素,每次插入做向上调整.保证堆是小堆
}

while (!HeapEmpty(&hp))//如果堆不为空的话
{
	int top=HeapTop(&hp);//每次取堆顶数据为最小
	printf("%d ", top);
	HeapPop(&hp);//删除堆顶元素时,重新调整堆,使其还是小堆




}





}

主函数

int main()
{

	
	int arr[10] = { 52,85,74,46,23,14,65,25,32,78 };
	HeapSort(arr, 10);


}

编译运行

在这里插入图片描述

如果用上述方法写堆的话,还必须写一个堆的结构,特别麻烦.

排序方法2

1.排列升序

我们可以直接使用向上调整和向下调整建堆,实质上的堆只是数组,向上调正和向下调整只是操作下标而已.
排列升序我们应该创建大堆还是小堆,如果不仔细想一想的话,可能是创建一个小堆,然后依次取对顶元素,但是如果依次取堆顶元素的话,剩下的元素就不能构成一个堆,堆的关系就全乱了.如下图所示.取出堆顶的小元素.重新排列的话.
在这里插入图片描述
因此我们如果要排升序的话,就创建大堆,我们既可以用向上调整创建大堆,也可以用向下调整创建大堆

向下调整创建大堆

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;
		}













	}
}
void HeapSort(int* a, int n)
{
	Heap hp;


	for (int i = (n - 1 - 1) / 2; i >=0; --i)//从最后一个非子叶节点开始走.直到堆顶的元素
	{
		AdjustDown(a, n, i);


	}
	

	
	
	
	
	
	






}
















int main()
{
	Heap hp;
	//createdata();
	//printtopK(10);
	int arr[10] = { 52,85,74,46,23,14,65,25,32,78 };
	HeapSort(arr, 10);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}















	
}

向上调整创建大堆

void Adjustup(int* a, int child)
{
	int 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)
{
	Heap hp;

	for (int i = 1; i < n; i++)
	{
		Adjustup(a, i);
   }
	//for (int i = (n - 1 - 1) / 2; i >=0; --i)
	//{
	//	AdjustDown(a, n, i);


	//}
	


	
	
	
	
	






}


int main()
{
	Heap hp;
	//createdata();
	//printtopK(10);
	int arr[10] = { 52,85,74,46,23,14,65,25,32,78 };
	HeapSort(arr, 10);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}















	
}

创建的大堆
在这里插入图片描述

在这里插入图片描述

然后将创建好的大堆进行排序
修改后的堆排序

void HeapSort(int* a, int n)
{
	Heap hp;


	for (int i = (n - 1 - 1) / 2; i >=0; --i)
	{
		AdjustDown(a, n, i);


	}
	

		int end = n - 1;
while (end > 0)
{
	swap(&a[0], &a[end]);
	AdjustDown(a, end, 0);
	--end;
}
}

在这里插入图片描述

排降序

由排升序创建大堆,则排降序创建小堆,创建小堆既可以用向上调整法,还可以使用向下调整

向下调整创建小堆

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;
		}













	}
}
void HeapSort(int* a, int n)
{
	Heap hp;


	for (int i = (n - 1 - 1) / 2; i >=0; --i)
	{
		AdjustDown(a, n, i);


	}
	


	
	
	
	
	






}


int main()
{
	Heap hp;
	//createdata();
	//printtopK(10);
	int arr[10] = { 52,85,74,46,23,14,65,25,32,78 };
	HeapSort(arr, 10);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}















	
}

向上调整创建小堆

void Adjustup(int* a, int child)
{
	int 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)
{
	Heap hp;

	for (int i = 1; i < n; i++)
	{
		Adjustup(a, i);
   }
	//for (int i = (n - 1 - 1) / 2; i >=0; --i)
	//{
	//	AdjustDown(a, n, i);


	//}
	


	
	
	
	
	






}


int main()
{
	Heap hp;
	//createdata();
	//printtopK(10);
	int arr[10] = { 52,85,74,46,23,14,65,25,32,78 };
	HeapSort(arr, 10);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}















	
}

将创建好的小堆进行排序

void Adjustup(int* a, int child)
{
	int 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)
{
	Heap hp;

	for (int i = 1; i < n; i++)
	{
		Adjustup(a, i);
   }
	//for (int i = (n - 1 - 1) / 2; i >=0; --i)
	//{
	//	AdjustDown(a, n, i);


	//}
			int end = n - 1;
while (end > 0)
{
	swap(&a[0], &a[end]);
	AdjustDown(a, end, 0);
	--end;
}


	
	
	
	
	






}


int main()
{
	Heap hp;
	//createdata();
	//printtopK(10);
	int arr[10] = { 52,85,74,46,23,14,65,25,32,78 };
	HeapSort(arr, 10);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}















	
}

总结:创建大堆或者小堆都可以用向上调整和向下调整,如果创建大堆对应的调整必须修改成孩子大于父亲进行交换,这样的话就可以把大数放在堆顶上.,如果创建小堆的话,孩子小于父亲的话,在进行交换,值得注意的是,创建大堆时,并且使用向下调整的话,需要将孩子的下标落在左右孩子较大的孩子身上.创建小堆时,需要将孩子的下标落在左右孩子较小的身上.


top-K问题

TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。
比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。
对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决,基本思路如下:

  1. 用数据集合中前K个元素来建堆
    前k个最大的元素,则建小堆
    前k个最小的元素,则建大堆
  2. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素将剩余N-K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素。

找最大的10个

#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include <stdlib.h>//srand头文件
#include <time.h>//time头文件
void swap(int* str1, int* str2)//交换两个数据的值
{
	int tmp = *str1;
	*str1 = *str2;
	*str2 = tmp;

}

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;
		}
	}

}
void createdata()
{
	int n = 10000;
	srand((unsigned int)time(NULL));//产生随机数种子
	FILE* fp = fopen("xa.txt", "w");//以写的方式打开文件
	if (fp == NULL)
	{
		perror("fopen fail");
		return;


	}
	for (int i = 0; i < 10000; i++)//产生10000个随机数,
	{
		int x = rand()%10000;//并且每一个随机数都是小于10000.
		fprintf(fp,"%d\n",x);//将其写入文件中,并且换行	
    }
	fclose(fp);
}
void printtopk(int k)
{
	FILE* fp = fopen("xa.txt", "r");
	if (fp == NULL)
	{
		perror("fopen fail");
		return;


	}
	int* kminheap = (int*)malloc(sizeof(int) * k);//动态申请10个空间大小
	if (kminheap == NULL)
	{
		perror("malloc fail");



	}
	for (int i = 0; i < k; i++)
	{
		fscanf(fp,"%d",&kminheap[i]);//先读取文件前10个数据
		
	} 
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)//向下调整为小堆,因为选最大的10个要排小堆
	{
		AdjustDown(kminheap, k, i);
		
    }
	int val = 0;
	while (!feof(fp))//文件中剩下的数据需要与小堆中堆顶元素比较,如果大于堆顶元素,就把堆顶元素更新
	{
		fscanf(fp,"%d", &val);//读取文件中的数据到val中
		if (val > kminheap[0])
		{
			kminheap[0] = val;
			AdjustDown(kminheap, k,0);//比堆顶最小的大,就入堆,然后将堆顶元素向下调整,调整后,还需要保证是小堆。



		}






	}
	for (int i = 0; i < k; i++)
	{
		printf("%d ", kminheap[i]);




	}

}

int main()
{
	createdata();
	printtopk(10);



}

在这里插入图片描述

找最小的10个

需要创建大堆

#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include <stdlib.h>
#include <time.h>
void swap(int* str1, int* str2)
{
	int tmp = *str1;
	*str1 = *str2;
	*str2 = tmp;

}

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;
		}
	}

}
void createdata()
{
	int n = 10000;
	srand((unsigned int)time(NULL));
	FILE* fp = fopen("xa.txt", "w");
	if (fp == NULL)
	{
		perror("fopen fail");
		return;


	}
	for (int i = 0; i < 10000; i++)
	{
		int x = rand()%10000;
		fprintf(fp,"%d\n",x);
    }
	fclose(fp);
}
void printtopk(int k)
{
	FILE* fp = fopen("xa.txt", "r");
	if (fp == NULL)
	{
		perror("fopen fail");
		return;


	}
	int* kminheap = (int*)malloc(sizeof(int) * k);
	if (kminheap == NULL)
	{
		perror("malloc fail");



	}
	for (int i = 0; i < k; i++)
	{
		fscanf(fp,"%d",&kminheap[i]);
		
	} 
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(kminheap, k, i);
		
    }
	int val = 0;
	while (!feof(fp))
	{
		fscanf(fp,"%d", &val);
		if (val <kminheap[0])
		{
			kminheap[0] = val;
			AdjustDown(kminheap, k,0);



		}






	}
	for (int i = 0; i < k; i++)
	{
		printf("%d ", kminheap[i]);




	}

}

int main()
{
	createdata();
	printtopk(10);



}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/123399.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

nacos做服务配置和服务器发现

一、创建项目 1、创建一个spring-boot的项目 2、创建三个模块file、system、gateway模块 3、file和system分别配置启动信息,并且创建一个简单的控制器 server.port9000 spring.application.namefile server.servlet.context-path/file4、在根目录下引入依赖 <properties&g…

《UML和模式应用(原书第3版)》2024新修订译本部分截图

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 机械工业出版社即将在2024春节前后推出《UML和模式应用&#xff08;原书第3版&#xff09;》的典藏版。 受出版社委托&#xff0c;UMLChina审校了原中译本并做了一些修订。同比来说&a…

工业镜头接口类型

现有产品主要有以下接口 1、C:最常见的工业相机接口&#xff0c;受限于接口物理尺寸大小&#xff0c;最大靶面目前是4/3” 2、M42:M421.0,2k和4k线阵相机使用 3、M58S:M580.75,大靶面相机使用&#xff0c;可以转C(限于CH080相机&#xff0c;靶面4/3”)&#xff0c;可以转F,可以…

数据分析实战 | 多元回归——广告收入数据分析

目录 一、数据及分析对象 二、目的及分析任务 三、方法及工具 四、数据读入 五、数据理解 六、数据准备 七、模型构建 八、模型预测 九、模型评价 一、数据及分析对象 CSV格式的数据文件——“Advertising.csv” 数据集链接&#xff1a;https://download.csdn.net/d…

数据结构-双向链表

目录 1.带头双向循环链表&#xff1a; 2. 带头双向循环链表的实现&#xff1a; 双向链表初始化&#xff1a; 双向链表打印&#xff1a; 开辟节点函数&#xff1a; 双向链表头插&#xff1a; 双向链表尾插&#xff1a; 双向链表头删&#xff1a; 双向链表尾删&#xff…

C语言学习笔记之结构篇

C语言是一门结构化程序设计语言。在C语言看来&#xff0c;现实生活中的任何事情都可看作是三大结构或者三大结构的组合的抽象&#xff0c;即顺序&#xff0c;分支&#xff08;选择&#xff09;&#xff0c;循环。 所谓顺序就是一条路走到黑&#xff1b;生活中在很多事情上我们都…

景联文科技提供高质量人像采集服务,助力3D虚拟人提升逼真度

人像采集是一种通过特定设备或技术&#xff0c;对人的相貌、身材等特征信息进行收集和处理的过程&#xff0c;可应用于3D虚拟人领域。通过采集大量的人像数据&#xff0c;可以训练和优化人像识别算法&#xff0c;提高其准确性。 人像采集对于提高3D虚拟人的逼真度、个性化定制以…

【手把手教你】将python程序打包成exe可执行文件

1. 安装环境 pip install pyinstaller6.0.02. 打包文件 pyinstaller -D “要启动的文件名“.py比如我的命令就是&#xff1a;pyinstaller -D eval.py 执行完后&#xff0c;会生两个文件夹dist和bulib两个文件和一个xxx.spec文件 3. 删除生成的文件 删除生成的bulid和dist文…

Java学习 8.Java-递归

一、递归的概念 引例&#xff1a; 一个方法在执行过程中调用自身&#xff0c;就称为递归&#xff08;函数自己调用自己&#xff09; 递归相当于数学的数学归纳法&#xff0c;有一个起始条件&#xff0c;有一个递推公式 递归的必要条件 1.将原问题划分为子问题&#xff0c;…

使用数据分析,识别设备异常

设备健康监测系统在工业领域中扮演着至关重要的角色&#xff0c;它能够帮助企业及时发现设备异常&#xff0c;预防故障&#xff0c;提高设备使用寿命和生产效率。而异常诊断技术则是设备健康监测系统中的核心部分&#xff0c;能够实现对设备异常情况的准确判断。根据设备状态数…

AI:69-基于深度学习的音乐推荐

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…

命令行远程操作windows

如遇安装python模块问题&#xff0c;请参考此连接处理&#xff1a;http://t.csdnimg.cn/l9W6f 一、命令行中使用ssh连接 1、安装 OpenSSH 客户端&#xff1a; 在 Windows 10 中&#xff0c;打开“设置”应用&#xff0c;选择“应用” > “可选功能” > “添加功能”。…

python- os模块

一、文件与目录相关的操作 1、删除文件/文件夹 2、os.stat("path/filename"): 获取文件/目录信息的结构说明 3、os.listdir 和 os.path.join 在Python中&#xff0c;os.listdir()和os.path.join()是操作系统模块&#xff08;os模块&#xff09;的两个函数&#x…

机器学习——奇异值分解案例(图片压缩未开始)

本想大迈步进入前馈神经网络 但是…唉…瞅了几眼&#xff0c;头晕 然后想到之前梳理的奇异值分解、主成分分析、CBOW都没有实战 如果没有实际操作&#xff0c;会有一种浮在云端的虚无感 但是如果要实际操作&#xff0c;我又不想直接调用库包 可是…如果不直接调包&#xff0c;感…

Python Opencv实践 - 车牌定位(纯练手,存在失败场景,可以继续优化)

使用传统的计算机视觉方法定位图像中的车牌&#xff0c;参考了部分网上的文章&#xff0c;实际定位效果对于我目前使用的网上的图片来说还可以。实测发现对于车身本身是蓝色、或是车牌本身上方有明显边缘的情况这类图片定位效果较差。纯练手项目&#xff0c;仅供参考。代码中im…

学之思开源考试系统部署至Centos7

学之思开源考试系统部署至Centos7 1、下载源码 源码下载&#xff1a; https://gitee.com/mindskip/xzs-mysql 数据库脚本下载&#xff1a; https://www.mindskip.net:999/ 2、项目打包 分别在\source\vue\xzs-student目录和source\vue\xzs-admin目录&#xff0c;执行前端打…

【遮天】叶凡首次高燃时刻,暴打姜峰逼其下跪,故事逐渐燃情

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析国漫资讯。 深度爆料&#xff0c;《遮天》国漫30集剧情最新内容解析&#xff0c;前面剧情中&#xff0c;叶凡被姜峰如疯狗一般追杀&#xff0c;他像一只被狼群追逐的鹿&#xff0c;在山林中亡命逃窜。身后是姜峰那歇斯底…

KaiOS APN配置文件apn.json调试验证方法(无需项目全编)

1、KaiOS 的应用就类似web应用&#xff0c;结合文件夹路径webapp字面意思理解。 2、KaiOS APN配置文件源代码在apn.json&#xff0c; &#xff08;1&#xff09;apn.json可以自定义路径&#xff0c;通过配置脚本实现拷贝APN在编译时动态选择路径在机器中生效。 &#xff08;…

Ubuntu22.04安装MySql

在Ubuntu上安装mysql就比较简单了 1、常规操作&#xff0c;更新软件包列表 apt update 至少安装之前看一眼版本吧 apt list mysql-server 嗯&#xff0c;是8.0.35版本的 2、安装mysql apt install mysql-server 3、给root用户设置密码 # 第一次安装完无需密码,让你输入…

【C语言】嵌套结构体初始化 - 一个有趣的结论

0. 前言 A. 嵌套结构体&#xff08;比如双链表&#xff09;的初始化一般是什么流程&#xff1f; B. 嵌套结构体的内存是如何分布的&#xff1f; C. 结构体中的结构体指针是否需要再次分配内存&#xff1f;不分配会怎么样&#xff1f; 关于嵌套结构体的初始化问题&#xff0c;我…