博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【算法学习笔记】堆排序和归并排序、其他几种排序的代码实现、比较和应用(习题)
阅读量:3906 次
发布时间:2019-05-23

本文共 5139 字,大约阅读时间需要 17 分钟。

文章目录


基本堆排序

调整为大根堆:

void shif(int a[],int low,int high){
int i = low,j = 2*i;//j为左孩子 int temp = a[i]; while(j <= high) {
if(j < high && a[j] < a[j+1])//如果右孩子更大 j++; if(temp < a[j]){
//如果根节点小于最大孩子 a[i] =a[j];//将a[j]调整到双亲结点位置上 i = j; j = 2*i;//继续向下调整 } else break;//如果根节点大于等于最大的左右孩子 } a[i] = temp;}void HeapSort(int a[],int n){
int i; for(i = n/2;i >= 1;i--) {
shif(a,i,n);//初始化循环建立堆 } for(i = n;i >= 2; i--)//进行n-1趟堆排序,每一趟堆中元素个数减一 {
swap(a[i],a[1]);//将最后一个元素与a[1]交换 shif(a,1,i-1);//对a[1..i-1]进行筛选,得到i-1个结点的堆 }} int main() {
int a[6]={
0,11,74,3,15,6};HeapSort(a,5);for(int i = 1;i <= 5;i++)cout<
<<" "; }
  • 每一趟通过堆调整产生有序区,它是全局有序区
  • 每次调整时间为o(log2n)
  • 产生初始堆的时间为o(n),比较次数约4n
  • 最好最坏和平均时间复杂度为o(nlog2n)
  • 是不稳定的排序算法

应用:STL中的priority_queue优先队列

一旦元素在优先队列容器中,删除操作将删除最高优先级元素

1.有20个数组,每个数组有500个元素,且是有序的,如何在20*500个数中找出排名前500的数

涉及知识点:

priority_queue中元素的比较 模板申明带3个参数:priority_queue<Type, Container, Functional>Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list。STL里面默认用的是vector。

比较方式默认用operator<,所以如果把后面2个参数缺省的话,优先队列就是大顶堆(降序),队头元素最大
仿函数本质就是类重载了一个operator(),创建一个行为类似函数的对象。

template 
struct greater : public binary_function
{
bool operator()(const T& x, const T& y) const {
return x > y; }};

题目思路:假设元素类型为int,有序数组的个数为max,利用堆结构,堆中结点类型为node,用vectormyv存储结果。

下面是假如有三个有序数组,找出前三个最小的元素的代码:

#include
#include
#include
#define max 3struct node{
//小根堆的结点类型 int pn;//段号 int sn;//该段号中元素的序号 int data;//元素值};struct cmp{
bool operator() (const node& s1, const node& s2)const {
return s1.data > s2.data;//创建小根堆 }};void Top(int *a[max],vector
&myv){
priority_queue
, cmp>qu;//优先队列,注意其中的三个参数 这样每次出队时都是元素值最小的结点 struct node e; for(int i = 0;i < max; i++) {
e.pn = i; e.sn = 0; e.data = a[e.pn][e.sn];//将所有段的首元素入队 qu.push(e); } for(int i = 0;i < max; i++) {
e = qu.top(); qu.pop();myv.push_back(e.data);//元素值插入到vector中e.sn++;//该段的下一个元素入队e.data = a[e.pn][e.sn];qu.push(e); }}void dis(vector
p){ vector
::iterator it; for(it = p.begin() ; it != p.end(); it++) { cout<< *it<<" "; }} int main() { int a1[3] = { 2,6,9};int a2[3] = { -1,-1,5};int a3[3] = { -3,2,4};vector
my;int *a[max];a[0] = a1;a[1] = a2;a[2] = a3;Top(a,my);dis(my); }

在这里插入图片描述

设有两个递增的序列a,b 长度都为n,求前k个最小的a[i]+b[j]

与上一题类似,利用小根堆,先将a[0]+b[0]插入到小根堆qu中,当k>0时循环,出队元素e,插入到result中,k–,vector<node>result存放结果

void Top(int a[],int b[],int n,int k,vector
&p)//求前k个最小和{
priority_queue
qu;//优先队列,注意跟上一题比较,这里只需要一个参数 这样每次出队时都是元素值最小的结点 struct node e,temp; e.i = 0; e.j = 0; e.data = a[e.i]+b[e.j]; qu.push(e); while(k) {
e = qu.top(); qu.pop();p.push_back(e);k--;if(e.j+1 < n){
temp.i = e.i;temp.j = e.j + 1;temp.data = a[temp.i++]+b[temp.j];qu.push(temp);}if(e.j == 0 && e.i+1< n)//将a[e1.i+1]+b[e1.0]入队{
temp.i = e.i+1;temp.j = e.j;temp.data = a[temp.i]+ b[temp.j];qu.push(temp);} }}void dis(vector
p){
for(int i = 0;i < p.size(); i++) {
cout<< p[i].data<<" "; }} int main() {
int a[3] = {
2,4,5};int b[3] = {
1,2,6};vector
vec;Top(a,b,3,6,vec);dis(vec); }

注:在b没有扫描完时将a[e1.i]+b[e1.j+1]入队,当e.j = 0时还要将a[ei.i+1]+b[e1.0]入队

经过自己的试验,去掉e.j == 0 && 这句话,输出结果也是一样的

在这里插入图片描述

若要在N个海量数据(超过10亿,不能一次性放入内存)中找出最大的k个元素,(内存中可以容纳k个元素),最好采取什么数据结构和策略?

首先读入k个元素,假设第一次读取的k个元素就是前k个最大元素,把k个元素建成小根堆,然后从第k+1个元素开始,读取的每个元素d都与堆顶元素进行比较,如果元素d大于堆顶元素,则把堆顶的元素替换成d,再将其调整为小根堆,当所有数据都读入并比较完之后,这个小根堆里面的所有元素就是最大的k个元素。

归并排序

分为自底向上的非递归过程和自顶向下的递归过程,每一趟产生的是局部有序区, 其最好 最坏 平均时间复杂度均为o(nlog2n)

是不稳定的排序算法。
空间复杂度为o(n)
(快速排序的空间复杂度是o(log2n)

设有一个整数序列,求数组中一共多少个逆序对

可以采用归并排序,在合并的过程中计算逆序数,这是一种高效的算法,时间复杂度远小于暴力解法。

设low<=i<=mid,mid+1<=j<=high,当a[i]<=a[j]时不产生逆序对,当a[i]>a[j]时逆序数有mid-i+1个(注意两半分别已经排好序了)

int sum;void merge(int a[], int low, int mid, int high){
int i = low; int j = mid + 1; int *tmp = (int *) malloc((high - low +1)* sizeof(int));//分配足够空间 int k = 0; while(j <= high && i <= mid) {
if(a[i] > a[j]){
sum += mid - i + 1; tmp[k++] = a[j++];}else tmp[k++] = a[i++]; } while(j <= high) tmp[k++] = a[j++]; while(i <= mid) tmp[k++] = a[i++]; for(int k1 = 0;k1 < k;k1++) a[low+k1] = tmp[k1];//把tmp[0..k-1]复制到a[low..high]中 free(tmp);//释放空间}void merge_sort(int a[],int low,int high){
if(low < high){
//注意一开始的条件判断 int mid = (low + high)/2; merge_sort(a,low,mid);//先不断递归 得到最底层的数字 再合并 merge_sort(a, mid+1, high); merge(a,low,mid,high); }} int main() {
int a[5] = {
3,1,4,5,2};merge_sort(a, 0, 4);cout<

快速排序

快速排序之所以比较快,是因为相比冒泡排序,每次交换是跳跃式的。每次排序的时候设置一个基准点,将小于等于基准点的数全部放到基准点的左边,将大于等于基准点的数全部放到基准点的右边。这样在每次交换的时候就不会像冒泡排序一样只能在相邻的数之间进行交换,交换的距离就大得多了。因此总的比较和交换次数就少了,速度自然就提高了。当然在最坏的情况下,仍可能是相邻的两个数进行了交换。因此快速排序的最差时间复杂度和冒泡排序是一样的,都是O(N2),它的平均时间复杂度为O (NlogN)。其实快速排序是基于一种叫做“二分”的思想。

#include 
int a[101],n;//定义全局变量,这两个变量需要在子函数中使用void quicksort(int left,int right){
int i,j,t,temp;if(left>right)return;temp=a[left]; //temp中存的就是基准数i=left;j=right;while(i!=j){
//顺序很重要,要先从右往左找while(a[j]>=temp && i

转载地址:http://pxten.baihongyu.com/

你可能感兴趣的文章
Linux世界里的时间
查看>>
Linux日志学习
查看>>
Linux块设备加密之dm-crypt分析
查看>>
网站建设工具对比:IM Creator, Mobirise, Webydo以及uKit
查看>>
python利用企业微信api来进行发送自定义报警的类实现
查看>>
linux内核中协议栈--tcp实现的一点细节
查看>>
Linux-2.6.25 TCPIP函数调用大致流程
查看>>
BP 神经网络之我见s庆
查看>>
java中对文件的操作
查看>>
java中的内部类之简单学习
查看>>
java的字符流简单介绍
查看>>
java用FileReader和FileWrite读取和写入字符
查看>>
java的序列化和反序列化
查看>>
初识java的xml
查看>>
通过DOM方式对xml文件进行解析
查看>>
利用DOM方式生成XML文件
查看>>
java中使用SAX生成XML文件
查看>>
利用java获取网页内容
查看>>
Matlab中画图的说明
查看>>
python之对list进行切片学习
查看>>