操作系统意义上的堆和栈的概念以及数据结构意义上的堆和栈的概念

本文详细探讨了操作系统中的堆和栈,包括它们在C/C++和Java中的应用,以及理论知识如申请方式、效率比较和存储内容。同时,深入介绍了数据结构中的堆,特别是作为优先队列的实现,强调了优先队列不等同于优先堆的观点,并阐述了数据类型与数据结构的逻辑区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[duī zhàn] 

堆和栈的区别



在计算机领域,堆栈是一个不容忽视的概念,但是很多人甚至是计算机专业的人也没有明确堆栈其实是两种数据结构。堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除。要点:堆,顺序随意。栈,后进先出(Last-In/First-Out)。


1对比编辑

操作系统意义上的堆和栈的概念

栈(操作系统):由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈
栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放
堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于数据结构中的链表。
堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。
数据结构意义上的堆和栈的概念
堆(数据结构):堆可以被看成是一棵树,它是一个特殊的完全二叉树,如:堆排序
栈(数据结构):一种后进先出的数据结构,即队列。

一个程序一般分为3段:text段,data段,bss段
text段:就是放程序代码的,编译时确定,只读,
data段:存放在编译阶段(而非运行时)就能确定的数据,可读可写
就是通常所说的静态存储区,赋了初值的全局变量静态变量存放在这个区域,常量也存放在这个区域
bss段:定义而没有赋初值的全局变量静态变量,放在这个区域 

C/C++

一个由C/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由 编译器自动分配释放 ,存放 函数的参数名, 局部变量的名等。其操作方式类似于 数据结构中的栈。
2、堆区(heap)— 由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收。注意它与 数据结构中的堆是两回事,分配方式倒是类似于 链表
3、全局区( 静态区)(static)— 全局变量静态变量的存储是放在一块的,初始化的 全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
4、文字常量区—常量字符串就是放在这里的,程序结束后由系统释放 。
5、程序代码区— 存放 函数体二进制代码

例子程序

这是一个前辈写的,非常详细
1
2
3
4
5
6
7
8
9
10
11
12
13
//main.cpp
int a = 0; //全局初始化区
char *p1; //全局未初始化区
main()
{
     int b; //栈
     char s[] = "abc" ; //栈
     char *p2; //栈
     char *p3 = "123456" ; //123456\0在常量区,p3在栈上。
     static int c =0; //全局(静态)初始化区
     p1 = ( char *) malloc (10);
     p2 = ( char *) malloc (20); //分配得来的10和20字节的区域就在堆区。
}
strcpy(p1, "123456"); 123456\0放在 常量区, 编译器可能会将它与p3所指向的"123456"优化成一个地方。

2区别编辑

java

1. 栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。
2. 栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据可以共享,详见第3点。堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。
3.Java中的数据类型有两种。
一种是基本类型(primitivetypes), 共有8种,即int,short, long, byte, float, double, boolean, char(注意,并没有string的基本类型)。这种类型的定义是通过诸如int a= 3; long b = 255L;的形式来定义的,称为自动变量。值得注意的是,自动变量存的是字面值,不是类的实例,即不是类的引用,这里并没有类的存在。如int a= 3; 这里的a是一个指向int类型的引用,指向3这个字面值。这些字面值的数据,由于大小可知,生存期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就存在于栈中。
另外,栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:
  复制内容到剪贴板代码:
1
2
int a = 3 ;
int b = 3 ;
编译器先处理int a= 3;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。接着处理int b= 3;在创建完b的引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。这样,就出现了a与b同时均指向3的情况。
特别注意的是,这种字面值的引用与类对象的引用不同。假定两个类对象的引用同时指向一个对象,如果一个对象引用变量修改了这个对象的内部状态,那么另一个对象引用变量也即刻反映出这个变化。相反,通过字面值的引用来修改其值,不会导致另一个指向此字面值的引用的值也跟着改变的情况。如上例,我们定义完a与b的值后,再令a=4;那么,b不会等于4,还是等于3。在编译器内部,遇到a=4;时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存放4的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。
另一种是包装类数据,【如Integer,String, Double等将相应的基本数据类型包装起来的类。这些类数据全部存在于【堆】中】,Java用new()语句来显示地告诉编译器,在运行时才根据需要动态创建,因此比较灵活,但缺点是要占用更多的时间。 4.String是一个特殊的包装类数据。即可以用String str = new String("abc");的形式来创建,也可以用Stringstr = "abc";的形式来创建(作为对比,在JDK 5.0之前,你从未见过Integer i = 3;的表达式,因为类与字面值是不能通用的,除了String。而在JDK5.0中,这种表达式是可以的!因为编译器在后台进行Integer i = new Integer(3)的转换)。前者是规范的类的创建过程,即在Java中,一切都是对象,而对象是类的实例,全部通过new()的形式来创建。Java中的有些类,如DateFormat类,可以通过该类的getInstance()方法来返回一个新创建的类,似乎违反了此原则。其实不然。该类运用了单例模式来返回类的实例,只不过这个实例是在该类内部通过new()来创建的,而getInstance()向外部隐藏了此细节。那为什么在String str = "abc";中,并没有通过new()来创建实例,是不是违反了上述原则?其实没有。
4. 关于Stringstr = "abc"的内部工作。Java内部将此语句转化为以下几个步骤:【String str = "abc",String str不要连着】
(1)先定义一个名为str的对String类的对象引用变量:Stringstr;
(2)【在【栈】中查找有没有存放值为"abc"的地址,如果没有,则开辟一个存放字面值为"abc"的地址,接着创建一个新的String类的对象o,并将o的字符串值指向这个地址,而且在栈中这个地址旁边记下这个引用的对象o。如果已经有了值为"abc"的地址,则查找对象o,并返回o的地址。】【上文说数据时存放在堆中,此文说数据存放在栈中】
(3)将str指向对象o的地址。
值得注意的是,一般String类中字符串值都是直接存值的。但像Stringstr = "abc";这种场合下,其字符串值却是保存了一个指向存在栈中数据的引用!
为了更好地说明这个问题,我们可以通过以下的几个代码进行验证。
  复制内容到剪贴板代码:
1
2
3
String str1 = "abc" ;
String str2 = "abc" ;
System.out.println(str1==str2); //true
注意,我们这里并不用str1.equals(str2);的方式,因为这将比较两个字符串的值是否相等。==号,根据JDK的说明,只有在两个引用都指向了同一个对象时才返回真值。而我们在这里要看的是,str1与str2是否都指向了同一个对象。
  结果说明,JVM创建了两个引用str1和str2,但只创建了一个对象,而且两个引用都指向了这个对象。
我们再来更进一步,将以上代码改成:
  复制内容到剪贴板代码:
1
2
3
4
5
String str1 = "abc" ;
String str2 = "abc" ;
str1 = "bcd" ;
System.out.println(str1 + "," + str2); //bcd, abc
System.out.println(str1==str2); //false
这就是说,赋值的变化导致了类对象引用的变化,str1指向了另外一个新对象!而str2仍旧指向原来的对象。上例中,当我们将str1的值改为"bcd"时,JVM发现在栈中没有存放该值的地址,便开辟了这个地址,并创建了一个新的对象,其字符串的值指向这个地址。
事实上,String类被设计成为不可改变(immutable)的类。如果你要改变其值,可以,但JVM在运行时根据新值悄悄创建了一个新对象,然后将这个对象的地址返回给原来类的引用。这个创建过程虽说是完全自动进行的,但它毕竟占用了更多的时间。在对时间要求比较敏感的环境中,会带有一定的不良影响。
再修改原来代码:
  复制内容到剪贴板代码:
1
2
3
4
5
6
7
String str1 = "abc" ;
String str2 = "abc" ;
str1 = "bcd" ;
Stringstr3 = str1;
System.out.println(str3); //bcd
Stringstr4 = "bcd" ;
System.out.println(str1 == str4); //true
我们再接着看以下的代码。
  复制内容到剪贴板代码:
  String str1 = new String("abc");
  String str2 = "abc";
  System.out.println(str1==str2); //false 创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。
Stringstr1 = "abc";
  String str2 = new String("abc");
  System.out.println(str1==str2); //false
  创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。
以上两段代码说明,只要是用new()来新建对象的,都会在堆中创建,而且其字符串是单独存值的,即使与栈中的数据相同,也不会与栈中的数据共享。
5. 数据类型包装类的值不可修改。不仅仅是String类的值不可修改,所有的数据类型包装类都不能更改其内部的值。
6. 结论与建议:
(1)我们在使用诸如Stringstr = "abc";的格式定义类时,总是想当然地认为,我们创建了String类的对象str。担心陷阱!对象可能并没有被创建!唯一可以肯定的是,指向String类的引用被创建了。至于这个引用到底是否指向了一个新的对象,必须根据上下文来考虑,除非你通过new()方法来显要地创建一个新的对象。因此,更为准确的说法是,我们创建了一个指向String类的对象的引用变量str,这个对象引用变量指向了某个值为"abc"的String类。清醒地认识到这一点对排除程序中难以发现的bug是很有帮助的。
(2)使用Stringstr = "abc";的方式,可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于Stringstr = new String("abc");的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。这个思想应该是享元模式的思想,但JDK的内部在这里实现是否应用了这个模式,不得而知。
(3)当比较包装类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==。
(4)由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率

C/C++

一个由C/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由 编译器自动分配释放 ,存放 函数的参数名, 局部变量的名等。其操作方式类似于 数据结构中的栈。
2、堆区(heap)— 由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收。注意它与 数据结构中的堆是两回事,分配方式倒是类似于 链表
3、全局区( 静态区)(static)— 全局变量静态变量的存储是放在一块的,初始化的 全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
4、文字常量区—常量字符串就是放在这里的,程序结束后由系统释放 。
5、程序代码区— 存放 函数体二进制代码

例子程序

这是一个前辈写的,非常详细
1
2
3
4
5
6
7
8
9
10
11
12
13
//main.cpp
int a = 0; //全局初始化区
char *p1; //全局未初始化区
main()
{
     int b; //栈
     char s[] = "abc" ; //栈
     char *p2; //栈
     char *p3 = "123456" ; //123456\0在常量区,p3在栈上。
     static int c =0; //全局(静态)初始化区
     p1 = ( char *) malloc (10);
     p2 = ( char *) malloc (20); //分配得来的10和20字节的区域就在堆区。
}
strcpy(p1, "123456"); 123456\0放在 常量区, 编译器可能会将它与p3所指向的"123456"优化成一个地方。

3理论知识编辑

申请方式

stack:
操作系统自动分配(应用程序没有这个功能)。 例如,声明在 函数中一个 局部变量int b; 系统自动在栈中为b开辟空间
heap:
需要程序员自己申请,并指明大小,在c中 malloc函数
如p1 = (char *)malloc(10);
在C++中用new运算符
如p2 = new char[20];//(char *)malloc(10);
但是注意p1、p2本身是在栈中的。

申请响应

栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示 栈溢出
堆:首先应该知道操作系统有一个记录空闲 内存地址链表,当系统收到程序的申请时,会遍历该链表,寻 找第一个空间大于所申请空间的堆结点,然后将 该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于 大多数系统,会在这块内存空间中的 首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲 链表中。

申请限制

栈:在Windows下, 是向 低地址扩展数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向 高地址扩展数据结构,是不连续的内存区域。这是由于系统是用 链表来存储的空闲 内存地址的,自然是不连续的,而 链表的遍历方向是由低地址向高地址。堆的大小受限于 计算机系统中有效的 虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

效率比较

栈由系统自动分配,速度较快。但程序员是无法控制的。
堆是由new分配的内存,一般速度比较慢,而且容易产生 内存碎片,不过用起来最方便.
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈,而是直接在进程的 地址空间中保留一块内存,虽然用起来最不方便。但是速度快,也最灵活

存储内容

栈: 在 函数调用时,在大多数的C 编译器中,参数是由 右往左入栈的,然后是函数中的 局部变量。注意 静态变量是不入栈的。
当本次 函数调用结束后, 局部变量出栈,然后是参数,最后栈顶 指针指向最开始存的地址,也就是 主函数中的下一条指令,程序由该点继续运行。
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员安排。

存取比较

char s1[] = "aaaaaaaaaaaaaaa";
char *s2 = "bbbbbbbbbbbbbbbbb";
aaaaaaaaaaa是在运行时刻赋值的;
而bbbbbbbbbbb是在编译时就确定的;
但是,在以后的存取中,在栈上的 数组指针所指向的字符串(例如堆)快。
比如
#include
void main()
{
char a = 1;
char c[] = "1234567890";
char *p ="1234567890";
a = c[1];
a = p[1];
return;
}
对应的汇编代码
10: a = c[1];
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
0040106A 88 4D FC mov byte ptr [ebp-4],cl
11: a = p[1];
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
00401070 8A 42 01 mov al,byte ptr [edx+1]
00401073 88 45 FC mov byte ptr [ebp-4],al
第一种在读取时直接就把字符串中的元素读到 寄存器cl中,而第二种则要先把 指针值读到edx中,再根据edx读取字符,显然慢了。

小结

堆和栈的区别可以用如下的比喻来看出:
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。

4主要分别编辑

操作系统方面的堆和栈,如上面说的那些。
还有就是 数据结构方面的堆和栈,这些都是不同的概念。这里的堆实际上指的就是(满足堆性质的) 优先队列的一种 数据结构,第1个元素有最高的优先权;栈实际上就是满足后进先出的性质的数学或数据结构。
虽然堆栈,堆栈的说法是连起来叫,但是他们还是有很大区别的,连着叫只是由于历史的原因。
堆与栈的分布

堆与栈的分布

5补充编辑

堆栈是一种存储部件,即数据的写入跟读出不需要提供地址,而是根据写入的顺序决定读出的顺序。
形象来说,栈就是一条流水线,而流水线中加工的就是方法的主要程序,在分配栈时,由于程序是自上而下 顺序执行,就将程序指令一条一条压入栈中,就像流水线一样。而堆上站着的就是工作人员,他们加工流水线中的商品,由 程序员分配:何时加工,如何加工。而我们通常使用new运算符为对象在堆上分配内存(C#),堆上寻找对象的任务交给句柄,而栈中由栈 指针管理
词条标签: 计算机 编程 数据结构    区域 内存分配



注释:

数据结构是什么

数据结构=一些存储块+操作这些存储块的规则(也叫算法)。
数据结构是 计算机存储、组织 数据的方式。数据结构是指相互之间存在一种或多种特定关系的 数据元素的集合。通常情况下,精心选择的数据结构可以带来更高的运行或者存储 效率算法不是指的是数据结构中数据元素之间的各种关系的集合。

数据结构是指相互之间存在着一种或多种关系的 数据元素的集合和该集合中数据 元素之间的关系组成。记为:
Data_Structure=(D,R)
其中 D是数据元素的 集合R是该集合中所有元素之间的关系的 有限集合
注释:
数据结构往往同高效的检索算法索引技术有关。
算法不是指的是数据结构中数据元素之间的各种关系的集合。


一般认为,一个数据结构是由数据元素依据某种逻辑联系组织起来的。对数据元素间逻辑关系的描述称为数据的逻辑结构;数据必须在计算机内存储,数据的 存储结构是数据结构的实现形式,是其在计算机内的表示;此外讨论一个数据结构必须同时讨论在该类数据上执行的运算才有意义。一个逻辑数据结构可以有多种 存储结构,且各种 存储结构影响数据处理的效率。

注释:
1、一个数据结构是由数据元素依据某种逻辑联系组织起来的。这里说的逻辑联系,就是指 数据结构中数据元素之间的各种关系的集合。
2、一个逻辑数据结构可以有多种 存储结构,那存储结构都有哪些? 顺序存储和链接存储是数据的两种最基本的存储结构。

数据结构方面的储存结构

分类

顺序存储方法它是把逻辑上相邻的结点存储在物理位置相邻的 存储单元里,结点间的逻辑关系由存储单元的邻接关系来体现,由此得到的存储表示称为 顺序存储结构顺序存储结构是一种最基本的存储表示方法,通常借助于 程序设计语言中的 数组来实现。 【所以数组既是一种数据结构,也可认为是一种数据类型(即顺序存储)】

链接存储方法它不要求逻辑上相邻的结点在物理位置上亦相邻,结点间的逻辑关系是由附加的 指针字段表示的。由此得到的存储表示称为 链式存储结构,链式存储结构通常借助于 程序设计语言中的 指针类型来实现。

顺序存储和链接存储的基本原理

顺序存储和链接存储是数据的两种最基本的存储结构。
在顺序存储中,每个 存储空间含有所存元素本身的信息,元素之间的逻辑关系是通过 数组下标位置简单计算出来的 线性表的顺序存储,若一个元素存储在对应数组中的下标位置为i,则它的前驱元素在对应数组中的下标位置为i-1,它的后继元素在对应数组中的下标位置为i+1。在 链式存储结构中,存储结点不仅含有所存元素本身的信息,而且含有元素之间逻辑关系的信息。
数据的 链式存储结构可用链接表来表示。
其中data表示值域,用来存储 节点的数值部分。Pl,p2,…,Pill(1n≥1)均为 指针域,每个指针域为其对应的后继元素或前驱元素所在结点(以后简称为后继结点或前驱结点)的存储位置。通过结点的 指针域(又称为链域)可以访问到对应的后继结点或前驱结点,若一个结点中的某个指针域不需要指向其他结点,则令它的值为空(NULL)。
在数据的顺序存储中,由于每个元素的存储位置都可以通过简单计算得到,所以访问元素的时间都相同;而在数据的链接存储中,由于每个元素的存储位置保存在它的前驱或后继结点中,所以只有当访问到其前驱结点或后继结点后才能够按 指针访问到,访问任一元素的时间与该元素结点在 链式存储结构中的位置有关。
参考:http://baike.baidu.com/view/2820182.htm

数据结构是指同一数据元素类中各数据元素之间存在的关系。数据结构分别为 逻辑结构存储结构物理结构)和数据的运算。数据的逻辑结构是对数据之间关系的描述,有时就把逻辑结构简称为数据结构。逻辑结构形式地定义为(K,R)(或(D,S)),其中,K是数据元素的有限集,R是K上的关系的有限集。

算法的设计取决于数据(逻辑)结构,而算法的实现依赖于采用的存储结构。数据的存储结构实质上是它的逻辑结构在计算机 存储器中的实现,为了全面的反映一个数据的逻辑结构,它在 存储器中的映象包括两方面内容,即数据元素之间的信息和数据元素之间的关系。不同数据结构有其相应的若干运算。数据的运算是在数据的逻辑结构上定义的操作算法,如检索、插入、删除、更新和排序等。
===================================================================================================================================


数据类型:数组、结构体、类。

数据结构



数据类型:数组、结构体、类。

数据结构:
基本数据结构(原子(不可分)数据结构:):数组(即线性表)、链表、树。

二级数据结构:(由基本数据结构复合而成)
即后进先出( LIFO)的 一种数据结构。  实现方法: 顺序栈链栈
参考:http://blog.chinaunix.net/uid-25908383-id-2965531.html

队列即先进先出(FIFO)的一种数据结构。  实现方法:顺序队列链队列
参考:http://blog.chinaunix.net/uid-25908383-id-2972905.html

堆是一种特殊的二叉完全树。
参考:http://blog.youkuaiyun.com/hunter8777/article/details/6363443

编辑

定义

n个关键字序列Kl,K2,…,Kn称为(Heap),当且仅当该序列满足如下性质(简称为堆性质):
(1)ki<=k(2i)且ki<=k(2i+1)(1≤i≤ n),当然,这是小根堆,大根堆则换成>=号。//k(i)相当于 二叉树的非 叶子结点,K(2i)则是左子节点,k(2i+1)是右子节点
若将此序列所存储的向量R[1..n]看做是一棵 完全二叉树存储结构,则 堆实质上是满足如下性质的完全二叉树
树中任一非叶子结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。
【例】关键字序列(10,15,56,25,30,70)和(70,56,30,25,15,10)分别满足堆性质(1)和(2),故它们均是堆,其对应的 完全二叉树分别如小根堆示例和大根堆示例所示。
大根堆和小根堆:根结点(亦称为堆顶)的 关键字是堆里所有结点关键字中最小者的堆称为小根堆,又称 最小堆。根结点(亦称为堆顶)的 关键字是堆里所有结点关键字中最大者,称为大根堆,又称最大堆。注意:①堆中任一子树亦是堆。②以上讨论的堆实际上是 二叉堆(Binary Heap),类似地可定义k叉堆。

高度

堆可以被看成是一棵树,结点在堆中的高度可以被定义为从本结点到叶子结点的最长简单下降路径上边的数目;定义堆的高度为树根的高度。我们将看到,堆结构上的一些基本操作的运行时间至多是与树的高度成正比,为O(lgn)。

参考:http://baike.baidu.com/link?url=B34sQGVCnpiGUEE1yymEhmGUaWvzQFPS_qDPdkWqhyfTjEHl_1WapLtPF_x32Jjz          

堆排序

三级数据结构:(由二级数据结构复合而成)
优先队列(priority queue)
普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。在 优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。 优先队列具有最高进先出 (largest-in,first-out)的行为特征。
参考:http://baike.baidu.com/link?url=dN63cvWnc-bCL5fXfr1YdI7XnzllYKAcnN7S0uaP7umYHaNQ9uxgjDJK2RVxuLQS2wDK8rsqeyFz9eeMSUFNp_
先说说优先队列的实现吧。有一点需要澄清,很多人一直以为Priority Queue就是一个Priority Heap,这种说法当然是片面的。既然优先队列只是存储数据和排序的结合,那么根据我们学过的知识,可以列出以下的实现方法:无序数组、无序链表、有序数组、有序链表以及二叉查找树,当然还有堆。
优先队列可以用堆实现。

注释:

数据类型和数据结构的区别


在逻辑结构上,数据类型就是一块内存块,而数据结构则是由多块内存块构成,且多块内存块间存在一定关系而连接起来的。

参考:http://developer.51cto.com/art/201112/305681.htm


参考:
百度
优先队列 堆
数据结构 堆
数据结构 队列






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值