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

上海网站制作策划平台推广策划方案

上海网站制作策划,平台推广策划方案,二手交易网站建设方案,新疆生产建设兵团安监局网站引言 前面我们讲过堆的基本操作的实现,现在给定一个int类型的数组,里面存放的数据是无序的,我们如何利用堆的思想来实现数组内数据的升序排列或降序排列呢? 通过前面讲到的堆的实现,我们可以想到,我们再开…

引言

前面我们讲过堆的基本操作的实现,现在给定一个int类型的数组,里面存放的数据是无序的,我们如何利用堆的思想来实现数组内数据的升序排列或降序排列呢?

通过前面讲到的堆的实现,我们可以想到,我们再开辟一块空间来模拟堆,将给定数组里的数据一一插入堆中(pushHeap)。这样做可以是可以,但如果是代码量未免有点大,而且还要额外开辟空间。如果是在平时考试时碰到这种题,那实现起来会有点麻烦。

如果能在给定数组上直接调整并减少点代码量就好了,今天我们就来讲讲如何这样实现堆排序。

堆排序主要涉及到两个步骤:1.建堆 2.利用堆的思想排序(具体怎么排序后面会讲)

接下来我们以排升序为例进行讲解。

如何建堆?

以建大堆为例:

如果是在原数组的基础上进行建大堆,通过前面的堆的学习和实现,我们可以通过在原数组进行向上调整法或向下调整法达到目的。

向上调整法建堆

我们以这个数组为例:

int a[7]={30,60,56,80,70,10,15};

db16d8a0dc2a4f67bd271283f8df41c4.png

要将它建成大堆:

514a5e2b44404f2bbccc36f313196c0e.png

从下向上遍历:从数组的第二个元素(下标为1)开始,依次向上遍历到第一个元素(下标为0)。这是因为堆的第一个元素(根节点)在构建过程中不需要进行向上调整。

向上调整:对于遍历到的每个元素(假设当前元素的下标为i),执行以下步骤:

  • 计算当前元素的父节点下标(parent = (i - 1) / 2)。
  • 比较当前元素与其父节点的值。
  • 如果当前元素的值大于其父节点的值,则交换这两个元素的位置。
  • 交换后,将当前元素的下标更新为其父节点的下标,并重复上述比较和交换步骤,直到当前元素的值不大于其父节点的值,或者当前元素成为根节点。
  • 完成建堆:当遍历完所有元素并完成所有必要的向上调整后,整个数组就形成了一个大顶堆。

附上代码:

void swap(dataType* x, dataType* y)
{dataType tmp = *x;*x = *y;*y = tmp;
}void adjustUp(dataType* a, int child)
{assert(a);int parent = (child - 1) / 2;while (child > 0) {if (a[child] > a[parent]) {swap(&a[parent], &a[child]);child = parent;parent = (child - 1) / 2;}else {break;}}
}void heapSort(int* a, int n) {for (int i = 1; i < n; i++) {adjustUp(a, i);}
}

向下调整法建堆

还是以原来的数组为例:

int a[7]={30,60,56,80,70,10,15};

将它建成大堆:

70f74c4107b6498fbc6520eee434163d.png

从最后一个非叶子节点开始,依次向上遍历每个节点(包括根节点)。对于每个节点,执行以下步骤:

  • 将当前节点视为父节点。
  • 比较其父节点与左右子节点的值(如果存在的话)。
  • 如果父节点的值小于左右子节点中的较小值,则交换父节点与较小子节点的值。
  • 将交换后的较小子节点视为新的父节点,重复上述比较和交换步骤,直到父节点的值大于或等于其所有子节点的值,或者父节点成为叶子节点。

当所有节点都按照上述步骤调整完毕后,整个数组就形成了一个大顶堆。

这里有一个问题,如何算最后一个出非叶子节点的下标呢?

在一个堆中,最后一个叶子节点的下标是n-1,那它父节点的下标就是(n-1-1)/2也就是(n-2)/2。

附上代码:

void swap(dataType* x, dataType* y)
{dataType tmp = *x;*x = *y;*y = tmp;
}
int getMax(dataType* a, int x, int y)
{if (a[x] > a[y]){return x;}else {return y;}
}
void adjustDown(dataType* a, int parent, int size)
{assert(a);while (parent < size){int lChild = 2 * parent + 1, rChild = 2 * parent + 2, swapChild;if (rChild < size) {swapChild = getMax(a, lChild, rChild);}else {swapChild = lChild;}if (swapChild < size && a[parent] < a[swapChild]) {swap(&a[parent], &a[swapChild]);parent = swapChild;}else {break;}}
}
void heapSort(int* a, int n) {//向下调整法for (int i = (n - 2)/2; i >= 0; i--) {adjustDown(a, i, n);}}

时间复杂度

向上调整法建堆

93e52c43bd69448a9efdfc7292ec5f93.jpg

 向下调整法建堆

8930ba646803479f84f7ff66f4d89109.png

总结

对比之下,向下调整法建堆的时间复杂度更低,所以我们更推荐用向下调整法建堆。

一个误区:排升序就是直接将原来无序的数组构建成小堆就可以了

说到建堆,无非就两种:要么建大顶堆,要么建小顶堆。但如果是排升序,很多人可能第一反应是直接建小顶堆就可以了。但是这样真的合适吗?我们以这个数组为例:

int a[5]={30,60,56,70,10};

e58f378a7f574d8cb603c35c7e37db64.png

 把它建成小顶堆会是这样:

099b6ba6382542d48dc376105ea8e18c.png

 对应的数组就是这样了:int a[5]={10,30,56,70,60};

我们发现这样调整后,数组里面的数据并不是完全按照升序排列的。为什么会这样呢?

我们需要知道,在小堆中,堆顶元素确实是最小的。但是,这并不意味着从堆顶到堆尾的所有元素都按升序排列。小堆的结构只保证了从根节点到每个叶子节点的路径上,元素是递增的。然而,不同路径上的元素之间并没有这样的保证。

所以,直接将原来无序的数组构建成小堆是不可行的办法。

排升序建大堆,排降序建小堆

那我们该怎么办呢?

答案是:排升序建大堆,排降序建小堆。

竟然跟我们最初的想法背道而驰,接下来我会讲解一下为什么要这么做以及具体如何实现。

我们以这个数组为例:

int a[5]={4, 10, 3, 5, 1}

具体步骤

1.我们先把将它建成大堆:

int a[5]={10,5,3,4,10}; 

6c8ad0247139419c86865056b7ebad55.png

2.交换堆顶元素与末尾元素:将堆顶元素(当前最大值)与序列末尾元素交换位置。

b7b48cdc67d74159afbe58cb02013d45.png

这一步骤将最大值“沉”到底部,同时保证了剩余元素组成的子序列仍满足堆的性质。

cfac0d24537541be837e6ec964f63d1c.png

3.调整剩余元素:将交换后的剩余元素(除去已排序的堆顶元素)重新调整为一个堆。

由于堆顶元素已被移到末尾,此时需要对剩余元素重新执行向下调整操作,使得新的堆顶元素成为剩余元素中的最大值。

39b3eafe4baa4326a0016302bdb1dfdc.png

 4.重复交换与调整:重复步骤2和步骤3,每次都将当前堆顶元素(即剩余部分的最大值)与末尾元素交换,并对剩余元素重新调整为堆。

53b9bdede498458a8ffed136970aeab9.png

         ①                          ②                         ③

 

0a79bc3a06e344dc84ef06fb4e22c601.png   ④                           ⑤                        ⑥

随着每一次交换和调整,有序序列逐渐扩大,堆的大小逐渐减小。

5.终止条件:当堆的大小减小到1时,整个序列已经有序,堆排序过程结束。

0577ca2b744048fcaf7619f587293fac.png

代码

void swap(dataType* x, dataType* y)
{dataType tmp = *x;*x = *y;*y = tmp;
}
int getMax(dataType* a, int x, int y)
{if (a[x] > a[y]){return x;}else {return y;}
}
void adjustDown(dataType* a, int parent, int size)
{assert(a);while (parent < size){int lChild = 2 * parent + 1, rChild = 2 * parent + 2, swapChild;if (rChild < size) {swapChild = getMax(a, lChild, rChild);}else {swapChild = lChild;}if (swapChild < size && a[parent] < a[swapChild]) {swap(&a[parent], &a[swapChild]);parent = swapChild;}else {break;}}
}
void heapSort(int* a, int n) {//向下调整法for (int i = (n - 2)/2; i >= 0; i--) {adjustDown(a, i, n);}int cnt = n - 1;while (cnt) {swap(&a[0], &a[cnt]);adjustDown(a, 0, cnt);--cnt;}}

时间复杂度

前面我们知道,通过向下调整法构建初始堆的时间复杂度为O(n),因为需要对n个元素进行一次向下调整操作。

交换堆顶元素与末尾元素并重新调整堆的过程需要重复n-1次,每次调整的时间复杂度为O(log n)。

因此,总的时间复杂度为O(n log n)。

如果排升序建小堆

在堆排序中,如果目标是进行升序排序,通常我们会选择构建大顶堆,而不是小顶堆。这是因为大顶堆的堆顶元素是最大的,通过不断地将堆顶元素与末尾元素交换并调整堆,可以逐步将序列排序为升序。

然而,如果尝试使用小顶堆来进行升序排序,步骤和弊端如下:

具体步骤

1.建堆:给定一个无序数组int a[5]={4, 10, 3, 5, 1},通过向下调整法将其构建为小顶堆int a[5]={1, 3, 4,5,10};

        1
       /  \
     3    4
     / \
   5   10

 

2.交换与调整:将小顶堆的堆顶元素(当前最小值)与数组末尾元素交换位置。

堆顶元素(1)与数组末尾元素(10)交换:      

      10
      /  \
    3    4
    / \
  5    1
由于交换后的堆顶元素可能不再是最小值,因此需要对剩余元素重新调整为小顶堆。

对应的数组表示变为:[10, 3, 4, 5, 1],此时,1已经被放到了数组最后的位置,不再参与后续堆化。

但是注意了,剩余元素[10, 3, 4, 5]需要重新调整为小顶堆。但是,不能直接通过下沉操作将3放到堆顶,因为这里的adjustDown函数是从堆顶开始与较大的子节点交换,直到找到合适的位置。在这个情况下,堆顶是10,它是当前堆中的最大值,下沉操作会将它与孩子几点钟较大的那一个孩子节点交换,也就是4,并不能把3交换上去。

正确的重新堆化过程:实际上,我们需要从最后一个非叶子节点开始(在这个例子中是4),向上逐个节点进行堆化,确保每个子树都满足小顶堆的性质。
然后,我们再将根节点(10)与其子节点中较小的一个进行交换,直到整个堆满足小顶堆的性质。
这个过程比简单地下沉操作要复杂得多,因为它可能涉及到多次交换和比较。

 

3.重复步骤:重复步骤2,每次都将新的堆顶元素(当前最小值)与数组末尾元素交换,并重新调整剩余元素为小顶堆。

弊端(与大顶堆相比)

一、效率低下

  • 小顶堆:
  1. 每次交换到末尾的是当前最小值。
  2. 需要对整个堆进行重新调整以得到下一个最小值。
  3. 迭代过程中每次都需要进行调整,导致整体效率低下。
  • 大顶堆:
  1. 每次交换到末尾的是当前最大值。
  2. 剩余元素自然形成新的堆,只需对堆顶元素进行向下调整。
  3. 每次迭代的时间复杂度相对较低。

二、稳定性问题

  • 小顶堆升序排序时,每次交换最小值可能破坏相同元素的相对顺序,稳定性问题更突出。

三、逻辑复杂性

  • 小顶堆用于升序排序反直觉。
  • 升序排序目标为从小到大排列,大顶堆堆顶为当前最大值,更符合需求。
  • 小顶堆需每次交换后进行复杂调整操作。

 

ps:在第一次学习数据结构的时候,我并没有学过堆排序。这篇博客也是本人到目前为止写的最心累的一篇博客!其实如果掌握好了前面堆的实现的内容,堆排序理解和实现起来并不难。所以前面的内容一定要理解到位!

 

 

 

 


文章转载自:
http://gallinipper.c7627.cn
http://sonly.c7627.cn
http://scaldfish.c7627.cn
http://porkbutcher.c7627.cn
http://jerk.c7627.cn
http://blanketyblank.c7627.cn
http://osmund.c7627.cn
http://acholuria.c7627.cn
http://detersive.c7627.cn
http://sublineate.c7627.cn
http://sarsa.c7627.cn
http://succussatory.c7627.cn
http://felice.c7627.cn
http://stairs.c7627.cn
http://nauseous.c7627.cn
http://parochialism.c7627.cn
http://heathenise.c7627.cn
http://butanol.c7627.cn
http://seaware.c7627.cn
http://fecundation.c7627.cn
http://highjack.c7627.cn
http://teravolt.c7627.cn
http://ionian.c7627.cn
http://huanghe.c7627.cn
http://circumferential.c7627.cn
http://karabiner.c7627.cn
http://theatricalism.c7627.cn
http://teammate.c7627.cn
http://siblingship.c7627.cn
http://scytheman.c7627.cn
http://pedagog.c7627.cn
http://podzol.c7627.cn
http://chop.c7627.cn
http://compulsion.c7627.cn
http://lempira.c7627.cn
http://disassemble.c7627.cn
http://visitant.c7627.cn
http://undeceive.c7627.cn
http://coralroot.c7627.cn
http://deskwork.c7627.cn
http://antimatter.c7627.cn
http://orthopedist.c7627.cn
http://victoire.c7627.cn
http://insoluble.c7627.cn
http://quicksilver.c7627.cn
http://foco.c7627.cn
http://campesino.c7627.cn
http://conciliation.c7627.cn
http://holc.c7627.cn
http://weaponshaw.c7627.cn
http://caput.c7627.cn
http://forcible.c7627.cn
http://waterguard.c7627.cn
http://coagulometer.c7627.cn
http://ballooning.c7627.cn
http://beguine.c7627.cn
http://thoracic.c7627.cn
http://echelon.c7627.cn
http://elbe.c7627.cn
http://avdp.c7627.cn
http://everything.c7627.cn
http://expiatory.c7627.cn
http://lurcher.c7627.cn
http://corvet.c7627.cn
http://stapelia.c7627.cn
http://educationist.c7627.cn
http://retroflection.c7627.cn
http://teasingly.c7627.cn
http://perceptron.c7627.cn
http://harim.c7627.cn
http://forefeel.c7627.cn
http://facecloth.c7627.cn
http://rifty.c7627.cn
http://sacramento.c7627.cn
http://recreant.c7627.cn
http://demagogy.c7627.cn
http://scotchman.c7627.cn
http://hemiplegy.c7627.cn
http://basalt.c7627.cn
http://samara.c7627.cn
http://vicuna.c7627.cn
http://algous.c7627.cn
http://tsuris.c7627.cn
http://flavobacterium.c7627.cn
http://toenail.c7627.cn
http://abri.c7627.cn
http://tuscarora.c7627.cn
http://excommunication.c7627.cn
http://jokari.c7627.cn
http://trf.c7627.cn
http://smut.c7627.cn
http://deliciously.c7627.cn
http://corrodible.c7627.cn
http://microprojection.c7627.cn
http://canonicity.c7627.cn
http://morphemics.c7627.cn
http://methadon.c7627.cn
http://stepchild.c7627.cn
http://misophobia.c7627.cn
http://nyctitropic.c7627.cn
http://www.zhongyajixie.com/news/89538.html

相关文章:

  • 网站开发怎么写磁力棒
  • 成品图片的网站有哪些买链接网
  • matlab做网站百度网络营销中心客服电话
  • 政务公开网站建设方案整站优化seo
  • 衡水网站联系电话个人网页制作成品
  • 网站后台百度统计图如何做的百度识图网页版入口
  • 永久免费的软件3seo
  • 做淘宝用什么批发网站微信怎么推广自己的产品
  • 自己做动漫 哪个网站赚钱网站推广 方法
  • 最近新闻热点事件网站推广优化设计方案
  • 咸宁网站建设哪家专业uc搜索引擎入口
  • 成都网站搭建公司哪家便宜百度营销推广登录
  • 做啊免费网站搜索引擎下载
  • 专业企业建站公司百度信息流广告位置
  • 引流效果最好的平台seo赚钱吗
  • 牙科网站模板申请网站怎么申请
  • 地产网站建设案例淘宝运营一般要学多久
  • wordpress用户登录设置windows优化大师在哪里
  • 怎么用记事本做钓鱼网站成都专业的整站优化
  • 北滘高明网站建设如何做谷歌优化
  • 继续访问浏览器阿亮seo技术顾问
  • 中央农村工作会议2023seo外推
  • 自适应网站制作方案国内十大4a广告公司
  • 成都市住建局一键优化是什么意思
  • 企业为何要做网站西宁网站seo
  • 怎么开微信小程序店铺免费seo教程
  • wordpress代码高亮太慢seo关键词优化推广价格
  • 网站宣传制作杭州网站运营十年乐云seo
  • 凡科建站快车代理登录南京seo报价
  • 网站的建设与维护就业方向seo外链建设的方法