当前位置: 首页 > news >正文

手机制作网站的软件四川房产信息网官网

手机制作网站的软件,四川房产信息网官网,全国企业名录大全,设计网站定制公司文章目录 堆的概念性质图解 向上调整算法算法分析代码整体实现 向下调整算法算法分析整体代码实现 堆的接口实现初始化堆销毁堆插入元素删除元素打印元素判断是否为空取首元素实现堆 堆排序创建堆调整堆整合步骤 TopK问题 堆的概念 堆就是将一组数据所有元素按完全二叉树的顺序… 文章目录 堆的概念性质图解 向上调整算法算法分析代码整体实现 向下调整算法算法分析整体代码实现 堆的接口实现初始化堆销毁堆插入元素删除元素打印元素判断是否为空取首元素实现堆 堆排序创建堆调整堆整合步骤 TopK问题 堆的概念 堆就是将一组数据所有元素按完全二叉树的顺序存储方式存储在一个一维数组中并满足树中每一个父亲节点都要大于其子节点称为大堆(树中每一个父亲节点都要大于其子节点称为小堆)。 性质 ①对于大堆(大根堆)来说,堆的顶部也就是数组首元素一定是最大的元素 ②对于小堆(小根堆)来说,堆的顶部也就是数组首元素一定是最小的元素 (这两点对于下面的堆排序来说十分重要) 此外,堆总是一棵完全二叉树,因为堆本身就是二叉树的一种顺序存储结构的实现模式 注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事一个是数据结构一个是操作系统中管理内存的一块区域分段 图解 通过图再去对比上面的概念和性质,理解会更加清晰 所谓的存储结构也就数据在内存中真实的存储情况,在一维数组中 而逻辑结构就是我们想象出来的,能够帮助我们理解并且通过这个也是根据二叉树中父节点和子节点之间的下标关系来确定的 ①已知父亲节点求子节点 LeftChild Parent * 2 1; //左孩子的节点下标 RightChild Parent * 2 2; //右孩子的节点下标②已知子节点求父节点 Parent (Child - 1) / 2; //切记是减1之后再除以2向上调整算法 向上调整算法主要在堆的插入和堆排序中应用最为广泛 算法分析 对于堆的插入,就是在数组的末尾进行数字的插入,并且在插入数据之后,我们仍要保证现有的结构仍然是一个堆! 如上图,是一个小堆 然后在数组的末尾插入了一个数字,即最后一个孩子节点,但是在插入之后,我们自身的堆结构发生了变化,所以我们必须对堆的结构进行调整. 不难发现,在最后插入一个数之后,其他子树仍然保持了小堆的性质(即父节点的值小于子节点),而正在需要调整的就是该子节点的’祖宗’这条线路,如上图红色箭头一步一步指向的位置, 而利用的公式就是Parent (Child - 1) / 2;把新插入的数和它的父节点作比较如果这个新插入的数小于于父节点那么就和父节点交换位置 在向上调整代码中,我们需要传入的参数是数组和插入的那个子孩子的节点的下标 void AdjustUp(HeapDataType* a, int child) //child是下标在实际的不断向上调整中,我们需要用循环来实现代码,并且要合理的设置循环 while (child 0) //不能设置为parent 0 { //Parent (Child - 1) / 2, 通过这个公式因为parent永远都不可能小于零...}时间复杂度 -------O(logN) 根据最坏情况来看(比如上图),数据多少层,我们就需要调整多少次,所以次数高度h 再根据二叉树节点数量和高度的关系可知: 所以可以得到关系: 次数 h logN 所以时间复杂度就为:O(logN) 代码整体实现 算法既可以实现小堆也可以大堆,具体看你函数内部符号的控制 整体实现如下: typedef struct HeapNode {HeapDataType* a;int size;int capacity; }HP; void Swap(HeapDataType* p1, HeapDataType* p2) {HeapDataType tmp *p1;*p1 *p2;*p2 tmp; } //循环写法 void AdjustUp(HeapDataType* a, int child) //child,parent是下标 {int parent (child - 1) / 2;while (child 0){//小堆:判断子节点和父亲结点的大小if (a[child] a[parent])//大堆if (a[child] a[parent]){Swap(a[child], a[parent]);//交换孩子和父亲child parent;parent (child - 1) / 2;}else{break;}} }//递归写法 void AdjustUp(HeapDataType* a, int child) {int parent (child - 1) / 2;if (child 0){//小堆if (a[child] a[parent]){Swap(a[child], a[parent]);child parent;AdjustUp(a, child); //递归}else{return;}}else{return;} }向下调整算法 向上调整算法主要在堆的数据删除和堆排序中应用最为广泛 算法分析 对于上图根节点27来说,它的左右子树都是小堆,所以就需要将27不断向下调整,保证其整体还是一个小堆 由此可见,向下调整的前提是左右子树必须是堆 利用的公式就是 LeftChild Parent * 2 1; RightChild Parent * 2 2; 在每一轮的调整中你都需要比较左右子节点的大小,比如上图就是对于27来说,15和17两个节点,15更小,所以就将15和27进行交换,然后对于19这个子树来说本身就是一个小堆,就可以不用管了,并且15本身也小于19,所以也符合小堆性质,然后继续对左边的子树进行如此的调整 在向下调整代码中,我们需要传入的参数是数组,数组大小和整棵树根节点的下标 void AdjustDown(HeapDataType* a, int size, int parent)时间复杂度 -------O(logN) 根据最坏情况来看(比如上图),数据多少层,最坏的情况我们就需要向下调整多少次,所以次数高度h, 再根据二叉树节点数量和高度的关系可知: 所以可以得到关系: 次数 h logN 所以时间复杂度就为:O(logN) 整体代码实现 typedef int HeapDataType;typedef struct HeapNode {HeapDataType* a;int size;int capacity; }HP; //转换 void Swap(HeapDataType* p1, HeapDataType* p2) {HeapDataType tmp *p1;*p1 *p2;*p2 tmp; } //循环写法 void AdjustDown(HeapDataType* a, int size, int parent) {int child parent * 2 1;while (child size)//这里的child是左孩子下标,之所以不是child1size,是因为{ //如果没有右孩子的话,这次循环将会终止,调整就会进行不彻底//小堆if (child 1 size 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 AdjustDown(HeapDataType* a, int size, int parent) {int child parent * 2 1;if (child size) {//小堆if (child 1 size a[child 1] a[child]){child; //如果右孩子小,那么下标就换成右孩子的下标}//小堆if (a[child] a[parent]){Swap(a[child], a[parent]);parent child;AdjustDown(a, size, parent); //递归}else{return;}}else{return;} }堆的接口实现 接下里,我将把堆的实现过程一步一步实现出来 初始化堆 void HeapInit(HP* hp) {assert(hp);hp-a NULL;hp-size 0;hp-capacity 0; }销毁堆 void HeapDestroy(HP* hp) {assert(hp);free(hp-a);hp-a NULL;hp-size hp-capacity 0; }插入元素 在尾部插入之后,要用AdjustUp函数向上调整形成堆 void HeapPush(HP* hp, HPDataType x) {assert(hp);// 扩容if (hp-size hp-capacity){int new hp-capacity 0 ? 4 : hp-capacity * 2;HPDataType*tmp(HPDataType*)realloc(hp-a, sizeof(HPDataType) * new);if (tmp NULL){perror(realloc fail);exit(-1);}hp-a tmp;hp-capacity new;}hp-a[hp-size] x;hp-size;AdjustUp(hp-a, hp-size - 1);//插入之后向上调整堆 }删除元素 一般指删除首元素,至于为什么HeapPop是删除首元素 根本就是因为要弹出尾元素很简单,直接size–不就完了 void HeapPop(HP* php) {assert(php);assert(php-size 0);Swap(php-a[0], php-a[php-size - 1]);//首元素换到尾部来,然后再size----php-size;AdjustDown(php-a, php-size, 0);//再用AdjustDown函数再来调整堆 }打印元素 void HeapPrint(HP* php) {assert(php);for (size_t i 0; i php-size; i){printf(%d , php-a[i]);}printf(\n); }判断是否为空 bool HeapEmpty(HP* php) {assert(php);return php-size 0; } 取首元素 HPDataType HeapTop(HP* php) {assert(php);assert(php-size 0);return php-a[0]; }实现堆 int main() {HP hp;HeapInit(hp);int a[] { 20, 11, 28, 31, 111, 52, 34, 16, 7, 9 };for (int i 0; i sizeof(a) / sizeof(int); i){HeapPush(hp, a[i]);//插入}HeapPop(hp);//把堆的首元素7删除删除HeapPrint(hp);printf(堆顶元素%d\n, HeapTop(hp));HeapDestroy(hp);return 0; }运行出来的结果: 堆排序 首先堆排有几个关键的步骤 ①创建堆 ②调整堆 创建堆 创建堆的方式有两种①向上调整建堆 ②向下调整建堆 ①首先我们来看第一种:向上调整建堆 这种方式的原理就是看作最开始堆中只有一个元素,从第一个元素开始就已经在向上调整,然后逐渐像堆中加入元素,随着一个一个元素的加入,也就形成了堆 图解如下: 而代码就是通过AdjustUp函数和一个for循环就可以完成上面步骤 //以前n个数建小堆 for (int i 0; i n; i) {AdjustUp(a, i); //a为数组的指针 }时间复杂度: O(n*logn) 分析:首先我们上面详细分析了AdjustUp()的时间复杂度为O(logn),然后循环了n此每次建堆,所以两者相乘,时间复杂度也就是 n*logn 了 ②我们来看第一种:向下调整建堆 -----堆排序中最主要用到的方法 这种建堆的关键就是从倒数第一个非叶子节点开始调(也就是树中最后一个父节点),然后逐渐1,就可以调整从最后一个父节点开始的每一棵树. 不难发现这样也符合向下调整的前提,即左右子树都是堆 那么我们如何找到最后一个节点的父亲? 就需要用到公式:Parent (Child - 1) / 2; 图解如下 而代码就是通过AdjustDown函数和一个for循环就可以完成上面步骤 for (int i ((n-1)-1)/2; i 0; --i) //(n-1)是拿到树最后一个节点,然后再根据公式Parent (Child - 1) / 2; {AdjustDown(a, size, i); }时间复杂度:O(n) 根据下面的思路 因此建堆的时间复杂度为O(n) 总结:其实两种方式建堆之所以时间复杂度有差距,就是因为向下调整建堆可以看作忽略了最后一排的节点,直接从倒数第二排节点开始调整的,而在一棵满二叉树中最后一排的节点其实就占据了整棵树的二分之一,所以相当于向下调整比向上调整少经历了很多的节点 所以实际堆排序中我们更多的使用的是向下调整建堆,因此时间复杂度为O(n) 还有一点需要注意的是:如果你想要升序,即从小打大,需要建大堆. 建了大堆之后,再交换首元素(最大的)和末尾元素,然后把最大的元素不算入堆中的元素, 再进行向下调整 如果你建小堆,当你拿到首元素(最小的元素之后),需要将数组依次前移然后重新建堆,每次都前移然后每次都建堆,时间复杂度直接拉满!!! 同理 如果你想要降序,即从大打小,需要建小堆. 调整堆 在堆建好之后,就可以开始调整堆了,比如你是升序,即从小打大,需要建大堆. 建了大堆之后,循环N次 进行N次调整堆操作每一次调整 堆得到的最大值将此值和数组的最后一个元素进行交换交换减小数组的长度最后被减小的那几个值不参与堆的调整直到最后一个元素就完成了堆的排序. 如下图,降序—小堆, 展示了其中一个调整过程 整合步骤 综合建堆和调整,完整的堆排序代码就出来了 void HeapSort(int* a, int n) {// 建堆 大堆or 小堆for (int i 1; i n; i){AdjustUp(a, i);} int end n - 1; while (end 0) {Swap(a[0], a[end]); //交换AdjustDown(a, end, 0); //向下调整--end; //换下来的最后一个数不计入堆中 }升序建大堆,降序建小堆很重要! TopK问题 最后我们再来解决一个堆在实际应用中很重要的Topk问题 通常这是在数据很大的情况下才会使用到的,如世界前500强,全省高考前十等等… 因为如果数据很大,你不可能在内存中创建一个这么大的数组来装下这么多数据,所以就要用topk问题的思路 举个简单的例子: 比如你有1000个数据,你要找前100个大的数据,那么你先随便拿100个数据(无论其大小多少)建小堆,然后另外900个数据依次与堆顶的最小数据进行比较,比它大就替换,然后再调整堆,这样1000个数据都参与了对比,对比了900次,900个最小的被拿走,剩下的100个一定是最大的,再进行堆排序 接下来用文件传输数据的形式进行举例 void CreateNDate() {// 造数据int n 10000000;srand(time(0));const char* file data.txt;FILE* fin fopen(file, w);if (fin NULL){perror(fopen error);return;}for (int i 0; i n; i){int x (rand() i) % 10000000;fprintf(fin, %d\n, x);}fclose(fin); }void TestTopK(const char* filename, int k) {// 1. 建堆--用a中前k个元素建堆FILE* fout fopen(filename, r);if (fout NULL){perror(fopen fail);return;}int* minheap (int*)malloc(sizeof(int) * k);if (minheap NULL){perror(malloc fail);return;}for (int i 0; i k; i){fscanf(fout, %d, minheap[i]);}// 前k个数建小堆for (int i (k-2)/2; i 0 ; --i){AdjustDown(minheap, k, i);}// 2. 将剩余n-k个元素依次与堆顶元素交换不满则则替换int x 0;while (fscanf(fout, %d, x) ! EOF){if (x minheap[0]){// 替换你进堆minheap[0] x;AdjustDown(minheap, k, 0);}}for (int i 0; i k; i){printf(%d , minheap[i]);}printf(\n);fclose(fout); }int main() {CreateNDate();TestTopK(data.txt, 5);return 0; }
http://www.hkea.cn/news/14374481/

相关文章:

  • 网站开发问卷调查厦门网站建设找维品
  • 建立一个个人网站做360网站优化快
  • 特色食品网站建设策划书免费项目管理软件app
  • 做直播网站软件设计公司需要什么资质
  • 建设网站需要的资源小程序后台管理系统
  • 电子商务网站建设考试试题网页制作素材免费网站
  • 没有网站怎么做淘客家用电脑可以做网站服务器
  • 成都专业手机网站建设服务种子搜索引擎
  • 电子商务网站建设模块1免费做网站
  • 怎么样做网站代建站之星凡客
  • html 网站模板简单近期新闻
  • 网站开发后端论文django网站开发源码
  • 免费优化网站建设网站交互怎么做的
  • 网站首次备案 多久网站优化费用
  • 手机怎么做网站服务器网站图片切换怎么做
  • 京东网站建设的意义网站建设用模板
  • 网页制作相关网站艾奇视觉网站建设
  • 保定网站建设工作怎样在凡科免费做网站
  • 怎么在虚拟主机上发布网站wordpress需要伪静态吗
  • 山东城乡住房建设厅网站唐山做网站公司费用
  • 长春网站制作报价志愿者协会网站建设
  • 接单做网站查网站权重
  • 网站制作电话shopify不如wordpress
  • 免费可商用素材网站wordpress新浪jquery
  • dede换网站自己有网站 做app
  • 广西建设厅关公网站如何在网站上做标记圈信息
  • 怎么做企业网站网站开发基础课程
  • 有没有专门发布毕业设计代做网站网站的数据库在哪里
  • 网站制作开发教程wordpress tml
  • 网站后台怎么上传表格2022网络游戏排行榜前十名