网上关于堆和栈的资源很多,推荐几篇文章
http://blog.youkuaiyun.com/hairetz/article/details/4141043 (堆和栈的区别,以及程序数据存储位置。非常好的文章)
http://jingyan.baidu.com/article/6c67b1d6a09f9a2786bb1e4a.html
http://www.cnblogs.com/jztan/p/5878630.html (常见的排序介绍、思想,还有动画演示)
摘取一点有用的信息:
一、预备知识程序的内存分配
一个由C/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)—— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap) —— 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—— 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后由系统释放。
4、文字常量区 —— 常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区 —— 存放函数体的二进制代码。
二、例子程序
int a = 0; //全局初始化区
char *p1; //全局未初始化区
int main(){
int b; //栈
char s[] = "abc"; //栈
char *p2; //栈
char *p3 = "123456"; //123456/0在常量区,p3在栈上
static int c = 0; //全局(静态)初始化区
p1 = (char *)malloc(10); //分配得来得10和20字节的区域就在堆区
p2 = (char *)malloc(20);
strcpy(p1,"123456"); //123456/0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方
}
三、堆、栈区别总结:
1.堆栈空间分配
①栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
②堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。
2.堆栈缓存方式
①栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放。
②堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。
3.堆栈数据结构区别
①堆(数据结构):堆可以被看成是一棵树,如:堆排序。
②栈(数据结构):一种先进后出的数据结构。
四、顺带复习堆排序
介绍:堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。堆排序利用了大根堆(或小根堆)堆顶记录的关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单。
大根堆排序基本思想:
① 先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区
② 再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key
③由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。
……
直到无序区只有一个元素为止。(对照着最后一个参考文章看看)
实现代码:
#include<iostream>
using namespace std;
//arr是待调整的堆数组, len是数组的长度, elem是待调整的数组元素的位置
//本函数功能是根据数组arr构建大根堆
void HeapAdjust(int arr[],int len,int elem)
{
int Child;
for(;2*elem+1<len;elem=Child)
{
//子结点的位置=2*(父结点位置)+1
Child=2*elem+1;
//得到子结点中较大的结点
if(Child<len-1&&arr[Child+1]>arr[Child]) ++Child;
//如果较大的子结点大于父结点那么把较大的子结点往上移动,替换它的父结点
if(arr[elem]<arr[Child])
swap(arr[elem],arr[Child]);
else
break; //否则退出循环
}
}
//堆排序算法
void HeapSort(int arr[],int len)
{
//调整序列的前半部分元素,调整完之后第一个元素是序列的最大的元素
//len/2-1是最后一个非叶节点,此处"/"为整除
for(int elem=len/2-1;elem>=0;--elem)
HeapAdjust(arr,len,elem);
//从最后一个元素开始对序列进行调整,不断的缩小调整的范围直到第一个元素
for(int i=len-1;i>0;--i)
{
//把第一个元素和当前的最后一个元素交换,
//保证当前的最后一个位置的元素都是在现在的这个序列之中最大的
swap(arr[0],arr[i]);
//不断缩小调整heap的范围,每一次调整完毕保证第一个元素是当前序列的最大值
HeapAdjust(arr,i,0);
}
}
int main()
{
int num[]={9,3,88,7,64,5,4,32,2,1,0,23,10,6,8};
int len=sizeof(num)/sizeof(int);
HeapSort(num,len);
for(int i=0;i<len;++i)
cout<<num[i]<<' ';
return 0;
}