编程珠玑---读书笔记---堆的实现及堆排序

摘要:
堆是用于表示一组元素的数据结构。与堆内存不同。堆的性质,首先:顺序:任何节点的值都小于或等于其子节点的值,这意味着最小元素位于根节点。最大顶部堆栈与此相反。接下来,我们考虑一种排序算法,堆排序算法:该算法的最坏运行时间是O,这保证了最坏的性能,而快速排序的最坏时间是O(n^2);因为树是平衡的,所以函数的运行效率非常高;排序算法通过在同一个实现数组中包含两个抽象结构来避免使用额外的空间#include<iostream>#include#include#include#include#include

堆是用来表示元素集合的一种数据结构。与“堆内存”不同。堆的性质,第一:顺序性:任何结点的值都小于或者等于其子结点的值,这意味着最小元素位于根结点。

最大顶堆跟这个相反。第二个性质是形状:一种二叉树,最底层的叶子结点尽可能靠左分布,如果有n个结点,那么所有结点到根的距离不会超过logn。

下面用vector来实现堆:

我们规范的从下标1开始,函数定义如下:

root=1;

value(i)=x[i];

leftchild(i)=2*i;

rightchild(i)=2*i+1;

parent(i)=i/2;两个关键函数:siftup和siftdown。

#include <iostream>
#include <string>
#include <cstdio>
#include <cmath>
#include <vector>
#include <algorithm>
#include <sstream>
#include <cstdlib>
#include <fstream>
#include <queue>
#include <ctime>
#include <set>
using namespace std;
int tmp[12]={12,20,15,29,23,17,22,35,40,26,51,19};
vector<int> x;
void siftup(int n)
{
	int i,p;
	for(i=n;i>1 && x[p=i/2]>x[i];i=p ){
		int t=x[p];
		x[p]=x[i];
		x[i]=t;
	}
}
void siftdown(int n)
{
	int i,c;
	for(i=1;(c=2*i)<=n;i=c)
	{
		if(c+1<=n && x[c+1]<x[c])c++;
		if(x[i]<=x[c])break;
		int t=x[c];
		x[c]=x[i];
		x[i]=t;
	}
}
int main()
{
	x.push_back(0);
	for(int i=0;i<12;i++)x.push_back(tmp[i]);
	
	x.push_back(13);
	
	siftup(x.size()-1);
	
	for(int i=1;i!=x.size();i++)cout<<x[i]<<" ";
	cout<<endl;
	
	x[1]=18;
	siftdown(x.size()-1);
	
	for(int i=1;i!=x.size();i++)cout<<x[i]<<" ";
	cout<<endl;
	
	return 0;

}

 


这两个函数的运行时间是O(logn)。

下面我们将一般抽象接口,实现一个优先队列,一次插入,一次extracmin,运行时间都是O(logn)都比较高效,查找并删除堆中的最小元素,然后重新

组织数组使其保持堆性质。

#include <iostream>
#include <string>
#include <cstdio>
#include <cmath>
#include <vector>
#include <algorithm>
#include <sstream>
#include <cstdlib>
#include <fstream>
#include <queue>
#include <ctime>
#include <set>
using namespace std;
int tmp[12]={12,20,15,29,23,17,22,35,40,26,51,19};
vector<int> x;
void siftup(int n)
{
	int i,p;
	for(i=n;i>1 && x[p=i/2]>x[i];i=p ){
		int t=x[p];
		x[p]=x[i];
		x[i]=t;
	}
}
void siftdown(int n)
{
	int i,c;
	for(i=1;(c=2*i)<=n;i=c)
	{
		if(c+1<=n && x[c+1]<x[c])c++;
		if(x[i]<=x[c])break;
		int t=x[c];
		x[c]=x[i];
		x[i]=t;
	}
}
void insert(int t)
{
	x.push_back(t);
	siftup(x.size()-1);
}
int extracmin()
{
	int t=x[1];
	x[1]=x[x.size()-1];
	x.resize(x.size()-1);
	siftdown(x.size()-1);
	return t;
}
int main()
{
	x.push_back(0);
	for(int i=0;i<12;i++)x.push_back(tmp[i]);
	
	insert(13);
	for(int i=1;i!=x.size();i++)cout<<x[i]<<" ";
	cout<<endl;
	
	while(x.size()>1)
	{
		cout<<extracmin()<<endl;
	}
	return 0;
}


当insert和extracmin都运用到n个元素的堆时候,都需要O(logn)的时间。

下面我们考虑一种排序算法,堆排序算法:

该算法在最坏情况的运行时间也是O(nlogn),保证最坏的性能,而由于快速排序的最坏时间O(n^2);由于树是平衡的,所以函数的运行效率都很高;

该排序算法通过在同一个实现数组中包含两种抽象结构来避免使用额外的空间;

#include <iostream>
#include <string>
#include <cstdio>
#include <cmath>
#include <vector>
#include <algorithm>
#include <sstream>
#include <cstdlib>
#include <fstream>
#include <queue>
#include <ctime>
#include <set>
using namespace std;
vector<int> x;
void siftup(int n)
{
	int i,p;
	for(i=n;i>1 && x[p=i/2]>x[i];i=p ){
		int t=x[p];
		x[p]=x[i];
		x[i]=t;
	}
}
void siftdown(int n)
{
	int i,c;
	for(i=1;(c=2*i)<=n;i=c)
	{
		if(c+1<=n && x[c+1]<x[c])c++;
		if(x[i]<=x[c])break;
		int t=x[c];
		x[c]=x[i];
		x[i]=t;
	}
}
int main()
{
	x.push_back(0);
	int tmp[12]={12,69,17,20,51,26,23,29,35,40,21,15};
	for(int i=0;i<12;i++)x.push_back(tmp[i]);
	
	for(int i=2;i!=x.size();i++)siftup(i);
	for(int i=x.size()-1;i>=2;i--)
	{
		int t=x[1];
		x[1]=x[i];
		x[i]=t;
		siftdown(i-1);
	}
	for(int i=1;i!=x.size();i++)cout<<x[i]<<" ";
	cout<<endl;
	
}


由于是最小堆,所以排序后是降序排序。

免责声明:文章转载自《编程珠玑---读书笔记---堆的实现及堆排序》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇【转载】通过搜狗站长平台查看网站的搜狗流量及搜索关键字CImageList使用指南下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

树状数组与线段树(一)

树状数组: 一共需要三个函数: ①lowbit(int x) ②add(int x,int p) ③query(int x) 1.动态求连续区间和 给定n个数组成的一个数列,规定有两种操作,一是修改某个元素,二是求子数列[a,b]的连续和。 输入格式 第一行包含两个整数n和m,分别表示数的个数和操作次数。 第二行包含n个整数,表示完整数列。 接下来m行,...

《二叉树》学习心得

树的介绍 1. 树的定义 树是一种数据结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合。 把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:(01) 每个节点有零个或多个子节点;(02) 没有父节点的节点称为根节点;(03) 每一个非根节点有且只有一个父节点;(04) 除了根节点外,每个子节...

c/c++实现获取NOD32升级账号密码

功能有待完善和添加 #include <iostream> #include <ctime> #include <cstring> #include <string> #include <fstream> #include <sstream> #include <cstdlib...

【转帖】C++编译原理 资料

转自:http://blog.csdn.net/shiwenbin333/article/details/5157797 首先是预编译,这一步可以粗略的认为只做了一件事情,那就是“宏展开”,也就是对那些#***的命令的一种展开。       例如define MAX 1000就是建立起MAX和1000之间的对等关系,好在编译阶段进行替换。       例如...

ROS之pcl_ros

1 概要:PCL(Point Cloud Library)ROS接口堆,PCL_ROS是在ROS中涉及n维点云和3D几何处理的3D应用的首选桥梁。这个包提供运行ROS和PCL的接口和工具,包括nodelets、nodes和c++接口 2 源码地址: git  https://github.com/ros-perception/perception_pcl....

Android游戏开发实践(1)之NDK与JNI开发01

Android游戏开发实践(1)之NDK与JNI开发01 NDK是Native Developement Kit的缩写,顾名思义,NDK是Google提供的一套原生Java代码与本地C/C++代码“交互”的开发工具集。而Android是运行在Dalvik虚拟机之上,支持通过JNI的方式调用本地C/C++动态链接库。C/C++有着较高的性能和移植性,通过这种...