更新2019年11月4日 04:26:35

睡不着觉起来寻思寻思干点啥吧,好像好久没写堆排了。于是写了个索引从0开始的堆排,这次把建堆函数略了并在heapsort主函数里,索引从0开始到size-1结束,长度size。

这个堆排和索引从1开始的堆排区别就是对于节点i,两个子节点分别为2i+1和2i+2。另外建堆时从索引size/2-1开始倒序维护大顶堆。下面证明下这个起始索引的节点一定对应着二叉树的最后的一个或两个叶子节点。

1.siz是偶数,那么最后一个内部节点只有左子树。siz/2-1乘2等于siz-2,siz-2加1为最后一个节点的索引,是siz-1末尾没问题。

2.siz是奇数,最后一个内部节点有左右子树。siz/2-1乘2等于((siz-1)/2-1)乘2等于siz-1-2等于siz-3,那么左子节点siz-3+1=siz-2,右子节点siz-3+2=siz-1是末尾也没问题。

void max_heap(vector<int>& nums, int i, int limit);
void heap_sort(vector<int>& nums)
{
for (int i = nums.size() / 2 - 1; i >= 0; --i) //建最大堆
{
max_heap(nums, i, nums.size());
}
for (int i = 1; i < nums.size(); ++i)
{
int temp = nums[0];
nums[0] = nums[nums.size() - i];
nums[nums.size() - i] = temp;
max_heap(nums, 0, nums.size() - i);
}
}
void max_heap(vector<int>& nums, int i, int limit)
{
int max_index = i;
if (2 * i + 1 < limit and nums[2 * i + 1] > nums[max_index])
{
max_index = 2 * i + 1;
}
if (2 * i + 2 < limit and nums[2 * i + 2] > nums[max_index])
{
max_index = 2 * i + 2;
}
if (i != max_index)
{
int temp = nums[i];
nums[i] = nums[max_index];
nums[max_index] = temp;
max_heap(nums, max_index, limit);
}
}

分割线

最近算法群里有人问堆排序的问题,我一想没想出来,就又看看算法导论堆排那章,跟着敲了一遍,感觉印象算是深刻了一些。

堆排序的几个函数:

1.maxheap(nums[ ],int i)

首先我们数组从1开始编号,为了方便。即i为父节点,nums[i]的左右孩子分别为2i和2i+1,算算对不?这个函数假设i的左右子树都已经为最大堆,只有nums[i]这个根节点可能有问题,下面上代码:

void maxheap(vector<int>& nums,int i,int limit)
//把以ri为根的子树调整为最大堆,假设其左右子树都已是最大堆
{
int largest=i;
if(2*i<=limit&&nums[i]<nums[2*i])
{
largest=2*i;
}
if(2*i+1<=limit&&nums[largest]<nums[2*i+1])
{
largest=2*i+1;
}
if(i!=largest)
{
swap(nums[i],nums[largest]);
maxheap(nums,largest,limit);
}
}

limit是数组最后一个元素的编号,看名字也看得出。函数做的事情就是先找i和他的俩孩子里最大的是哪个,如果最大的是i的其中一个孩子,那么就将其和i的值交换,这样大的就上去了,原来的nums[i]下来了,但是这个新下来的值可能与下面的左右子树构不成最大堆,此时下来的nums[i]和其新左右子树依然符合我们上面假设的条件这个函数假设i的左右子树都已经为最大堆,只有nums[i]这个根节点可能有问题,所以对其递归调用maxheap

2.createmaxheap(nums[ ]):

排序刚开始数组为无序,把它变成最大堆的函数。

代码:

void createmaxheap(vector<int>& nums)
{
int siz=nums.size();
nums.insert(nums.begin(),0);
//排序1~n
for(int i=siz/2;i>1;--i)
{
maxheap(nums,i,siz);
}
}

C的数组下标0开始的,那么我们前面插个0(插几都行),然后对1~n进行堆排序。

siz/2是最后一个非叶节点(就是编号最大的非叶节点),画个图好理解的,这就不提了。然后从这个节点开始依次往1靠,每次都把这个节点作为根节点的树变成最大堆,最后整个1~n就是最大堆了。

3.heapsort(nums[ ]):

这个更简单了:

void heapsort(vector<int>& nums)
{
createmaxheap(nums);
for(int i=nums.size()-1;i>1;--i)
{
swap(nums[1],nums[i]);
maxheap(nums,1,i-1);
}
}

先建最大堆,然后把nums[1]和nums[n]交换,然后对nums[1,n-1]继续maxheap,就是每次调整为最大堆,最大的就是第一个元素,把它和当前堆排序区间的末尾元素交换,这样循环n-1次就排好了(剩一个最小的就不用排了)。

全部代码:

C++:

#include<vector>
#include<time.h>
#include<iostream>
using namespace std; void maxheap(vector<int>& nums,int i,int limit)
//把以ri为根的子树调整为最大堆,假设其左右子树都已是最大堆
{
int largest=i;
if(2*i<=limit&&nums[i]<nums[2*i])
{
largest=2*i;
}
if(2*i+1<=limit&&nums[largest]<nums[2*i+1])
{
largest=2*i+1;
}
if(i!=largest)
{
swap(nums[i],nums[largest]);
maxheap(nums,largest,limit);
}
} void createmaxheap(vector<int>& nums)
{
int siz=nums.size();
nums.insert(nums.begin(),0);
//排序1~n
for(int i=siz/2;i>1;--i)
{
maxheap(nums,i,siz);
}
} void heapsort(vector<int>& nums)
{
createmaxheap(nums);
for(int i=nums.size()-1;i>1;--i)
{
swap(nums[1],nums[i]);
maxheap(nums,1,i-1);
}
}
int main()
{
int l=100;
srand(time(NULL));
vector<int> p(l,0);
for(int i=0;i<l;++i)
{
p[i]=rand();
}
for(int i=0;i<l;++i)
{
cout<<p[i]<<" ";
}
cout<<endl<<endl;
heapsort(p);
for(int i=0;i<l;++i)
{
cout<<p[i]<<" ";
}
getchar();
}

Python:


import random data_list=[]
for i in range(100):
data_list.append(random.uniform(1,200))
def max_heapify(data_list,i,limit):
temp=i
if 2*i<=limit and data_list[temp]>data_list[2*i]:
temp=2*i
if 2*i+1<=limit and data_list[temp]>data_list[2*i+1]:
temp=2*i+1
if temp!=i:
data_list[temp],data_list[i]=data_list[i],data_list[temp]
max_heapify(data_list,temp,limit) def create_heap(data_list):
list_len=len(data_list)
data_list.insert(0,0)
for i in range((list_len-1)//2,0,-1):
max_heapify(data_list,i,list_len-1) def heap_sort(data_list):
create_heap(data_list)
list_len=len(data_list)
for i in range(1,list_len-1):
data_list[1],data_list[list_len-i]=data_list[list_len-i],data_list[1]
max_heapify(data_list,1,list_len-i-1) heap_sort(data_list)
for i in data_list:
print(i)

C++索引从0开始的堆排序算法实现的更多相关文章

  1. 堆排序算法 java 实现

    堆排序算法 java 实现 白话经典算法系列之七 堆与堆排序 Java排序算法(三):堆排序 算法概念 堆排序(HeapSort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,可以利用数组的特 ...

  2. 必须知道的八大种排序算法【java实现】(三) 归并排序算法、堆排序算法详解

    一.归并排序算法 基本思想: 归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的.然后再把有序子序列合并为整体有序序列. 归并 ...

  3. 【java排序】 归并排序算法、堆排序算法

    一.归并排序算法 基本思想: 归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的.然后再把有序子序列合并为整体有序序列. 归并 ...

  4. Python3标准库:heapq堆排序算法

    1. heapq堆排序算法 堆(heap)是一个树形数据结构,其中子节点与父节点有一种有序关系.二叉堆(binary heap)可以使用一个有组织的列表或数组表示,其中元素N的子元素位于2*N+1和2 ...

  5. Heapsort 堆排序算法详解(Java实现)

    Heapsort (堆排序)是最经典的排序算法之一,在google或者百度中搜一下可以搜到很多非常详细的解析.同样好的排序算法还有quicksort(快速排序)和merge sort(归并排序),选择 ...

  6. 重新学习Mysql数据库4:Mysql索引实现原理和相关数据结构算法

    本文转自互联网 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial ...

  7. FlipView 索引为0 WP8.1

    如果使用FlipView时,出现别的页面切换到含有FlipView的页面时(缓存此页面/MainPage),点击或者滑动FlipView,Flipview自动索引到0 的问题解决办法 1.对Flipv ...

  8. 堆排序算法(C#实现)

    在软件设计相关领域,“堆(Heap)”的概念主要涉及到两个方面: 一种是数据结构,逻辑上是一颗完全二叉树,存储上是一个数组对象(二叉堆). 另一种是垃圾收集存储区,是软件系统可以编程的内存区域. 本文 ...

  9. mysql索引之二:数据结构及算法原理

    摘要 本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题.特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持也各不相同,因此MySQL数据库支持多种索引类型,如BT ...

随机推荐

  1. 吴裕雄--天生自然HADOOP操作实验学习笔记:hbase学生选课案例

    实验目的 复习hbase的shell操作和javaAPI操作 了解javaWeb项目的MVC设计 学会dao(数据库访问对象)和service层的代码编写规范 学会设计hbase表格 实验原理 前面我 ...

  2. Don't assign one object to another one

    correct way, when changing object, firstly you should create this object and then assign its propert ...

  3. Java安全笔记

    前言 后端接口开发中,涉及到用户私密信息(用户名.密码)等,我们不能传输明文,必须使用加密方式传输.这次政府项目中,安全测试组提出了明文传输漏洞,抽空研究了下Java加解密相关知识,记录下. 散列函数 ...

  4. Python第三方库requests的编码问题

    PS:这个解决方法可能很简单,但是这是平时的一些细节问题,所以有必要提醒一下! 首先代码不多,就是通过get方法去获取豆瓣首页信息,如图:但是会报UnicodeEncodeError: 'gbk' c ...

  5. 剑指offer-面试题26-树的子结构-二叉树

    /* 题目: 输入两棵二叉树A和B,判断B是不是A的子树. */ /* 思路: 1.注意浮点数大小的判断. 2.判断树A的某个节点是否和树B的根节点是否相同, 若相同,则判断以A该节点为根节点是否包含 ...

  6. gulp常用插件之pump使用

    更多gulp常用插件使用请访问:gulp常用插件汇总 pump这是一款小型节点模块,可将流连接在一起并在其中一个关闭时将其全部销毁. 使用标准source.pipe(dest)源时,如果dest发出关 ...

  7. 服务器的公网ip 和内网ip

    原文地址:https://zhidao.baidu.com/question/814783729071869532.html 服务器公网ip 可以用于域名解析ip,服务器远程登录ip,是最主要的服务器 ...

  8. Runtime.addShutdownHook用法

    一.什么是ShutdownHook? 在Java程序中可以通过添加关闭钩子,实现在程序退出时关闭资源.平滑退出的功能. 使用Runtime.addShutdownHook(Thread hook)方法 ...

  9. python3练习100题——045

    题目:统计 1 到 100 之和. sum(range(1,101)) 题目太容易了,我都不想用迭代浪费时间. 觉得这100道题难度设计越来越不合理.

  10. CentOS进行yum操作时不能访问国外镜像的解决方案

    1. 例如执行yum update,经常报错“Cound not resolve host: xxxxx”,一般都是yum源是使用的国外镜像,国内访问很不好.这时可以将源手动替换为国内的清华大学源,或 ...