堆(Heap)栈(Stack)有俩个层面的含义
(1)程序内存布局场景下,堆与栈表示的是两种内存管理方式;
(2)数据结构场景下,堆与栈表示两种常用的数据结构。
一、首先介绍一下内存管理中堆和栈的区别
1.1、栈的介绍
栈用于存放函数的参数值、局部变量等,其操作方式类似于数据结构中的栈。
public void main()
{
int b; //栈
String s= "abc"; //栈
double p2=1.0; //栈
}
其中函数中定义的局部变量按照先后定义的顺序依次压入栈中,也就是说相邻变量的地址之间不会存在其它变量。栈的内存地址生长方向与堆相反,由高到底,所以后定义的变量地址低于先定义的变量,比如上面代码中变量s的地址小于变量b的地址,p2地址小于s的地址。栈中存储的数据的生命周期随着函数的执行完成而结束。
1.2、堆的介绍
Java堆(Java Heap)是java虚拟机所管理的内存中最大的一块
java堆被所有线程共享的一块内存区域
虚拟机启动时创建java堆
java堆的唯一目的就是存放对象实例。
java堆是垃圾收集器管理的主要区域。
从内存回收的角度来看, 由于现在收集器基本都采用分代收集算法, 所以Java堆可以细分为:新生代(Young)和老年代(Old)。 新生代又被划分为三个区域Eden、From Survivor, To Survivor等。无论怎么划分,最终存储的都是实例对象, 进一步划分的目的是为了更好的回收内存, 或者更快的分配内存。
java堆的大小是可扩展的, 通过-Xmx和-Xms控制。
如果堆内存不够分配实例对象, 并且对也无法在扩展时, 将会抛出outOfMemoryError异常。
二、其次介绍一下数据结构中俩者的区别
2.1、栈的介绍
栈是一种运算受限的线性表,其限制是指只仅允许在表的一端进行插入和删除操作,这一端被称为栈顶(Top),相对地,把另一端称为栈底(Bottom)。把新元素放到栈顶元素的上面,使之成为新的栈顶元素称作进栈、入栈或压栈(Push);把栈顶元素删除,使其相邻的元素成为新的栈顶元素称作出栈或退栈(Pop)。这种受限的运算使栈拥有“先进后出”的特性(First In Last Out),简称FILO。
下面是用java代码实现栈的基本操作
public class MyStack {
// 底层用数组实现
private long[] arr;
private int pop;
public MyStack() {
arr = new long[10];
pop = -1;
}
public MyStack(int maxsize) {
arr = new long[maxsize];
pop = -1;
}
/**
* 入栈
* @param data
*/
public void push(long data) {
arr[++pop] = data;
}
/**
* 弹栈
* @return
*/
public long pop() {
return arr[pop--];
}
/**
* 查看栈顶数据
* @return
*/
public long peek() {
return arr[pop];
}
/**
* 遍历数据
* @return
*/
public String list() {
String str = "[ ";
for (int i = 0; i <= pop; i++) {
str += arr[i] + ", ";
}
str = str.substring(0, str.length()-2) + " ]";
return str;
}
@Override
public String toString() {
return list();
}
}
下面是测试用例
public class Test {
public static void main(String[] args) {
MyStack stack = new MyStack(20);
stack.push(10);
stack.push(20);
stack.push(30);
System.out.println(stack);
System.out.println(stack.peek());
stack.pop();
System.out.println(stack);
}
}
2.2、堆的介绍
堆是一种常用的树形结构,是一种特殊的完全二叉树,当且仅当满足所有节点的值总是不大于或不小于其父节点的值的完全二叉树被称之为堆。堆的这一特性称之为堆序性。因此,在一个堆中,根节点是最大(或最小)节点。如果根节点最小,称之为小顶堆(或小根堆),如果根节点最大,称之为大顶堆(或大根堆)。堆的左右孩子没有大小的顺序。下面是一个小顶堆示例:小跟堆
堆的存储一般都用数组来存储堆,i节点的父节点下标就为(i–1)/2 (i – 1) / 2(i–1)/2。它的左右子节点下标分别为 2∗i+1 2 * i + 12∗i+1 和 2∗i+2 2 * i + 22∗i+2。如第0个节点左右子节点下标分别为1和2。
2.3、堆的常用应用之 堆排序
思路:将数组看成为一个堆
堆排序代码
package com.qzw.sort;
import java.util.Arrays;
//堆排序
//时间 O(N*logN) 空间O(1)
public class HeapSort {
public static void main(String[] args) {
int num[] = new int[]{2, 7, 0, 5, 9, 3, 6};
sort( num );
System.out.println( Arrays.toString( num ) );
}
public static void sort(int num[]) {
if (num == null || num.length < 2) {
return;
}
heapinsert( num );
int heapsize = num.length;
swap( num, 0, --heapsize );
while (heapsize > 0) {
heapify( num, 0, heapsize );
swap( num, 0, --heapsize );
}
}
public static void heapinsert(int num[]) {
for (int i = 0; i < num.length; i++) {
while (num[i] > num[(i - 1) / 2]) {
swap( num, i, (i - 1) / 2 );
i = (i - 1) / 2;
}
}
}
public static void heapify(int num[], int index, int heapsize) {
int left = 2 * index + 1;
while (left < heapsize) {
int big = left + 1 < heapsize && num[left+1] > num[left] ? left+1 : left ;
if (num[big] < num[index]) {
big = index;
}
if (num[big] > num[index]) {
swap( num, big, index );
index = big;
left = 2 * index + 1;
}
if (big == index) {
break;
}
}
}
//输入俩个下标将此下标俩个数交换
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}