最近接触了个OAF的项目,开始学Java,在测试堆排序时出现个问题,就是:在Java中使用堆排序,当整数数目超过10000个时会报堆栈溢出错误,在我的机器上(>8970)就会出错, 哪位高手能否帮忙看看问题在哪里? 代码如下: package com.package2;
/**
* Function: 测试堆排序
*/
import java.util.*;
public class SortMethod {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//QuickSort st=new QuickSort(); //递归时: 3千万,6s; 1亿,20s; 非递归时: 3千万,11s; 1亿,36s
//MergeSort st=new MergeSort(); //非递归时: 3千万,5s; 1亿,15s; 递归时: 3千万,7s; 1亿,26s;
HeapSort st = new HeapSort();
TestSort ts=new TestSort();
//int size=11; boolean debug=false;
int size=8970; boolean debug=true; //debug=true时仅打印排序开始与结束时间,不打印排序过程
int arr[]=new int[size];
for(int i=0;i<size;i++) arr[i]=(int)(Math.random()*10000); //生成测试数据
ts.process(debug, st, arr);
}
}
class TestSort{
public void process(boolean debug, Sort sort, int[] arr){
sort.setArr(arr);
sort.print(debug);
sort.sort(debug);
sort.print(debug);
}
}
interface Sort{
public void setArr(int[] arr);
public void print(boolean debug);
public void printSystime(boolean debug, String pre);
public void sort(boolean debug);
}
class HeapSort implements Sort{
int arr[];
public void setArr(int[] arr) {
this.arr = arr;
}
public void print(boolean debug){
if(!debug){
String text="";
for(int i=0;i<arr.length;i++){
text+=","+arr[i];
}
text=text.replaceFirst(",","");
System.out.println("Array is:"+text);
//System.out.print(text+"\n");
}
}
public void printSystime(boolean debug, String pre){
if(debug){
Calendar cal=Calendar.getInstance();
System.out.println(pre+cal.getTime());
}
}
public void heapSort(boolean debug, int[] arr, int high){
if(high>0){
initSort(debug,arr,high);//初始化堆,找出最大的放在堆顶
arr[0]=arr[high]+arr[0];
arr[high]=arr[0]-arr[high];
arr[0]=arr[0]-arr[high];
heapSort(debug, arr, high-1);
}
}
public void initSort(boolean debug, int[] arr,int high){
int max=(high+1)/2; //完全二叉树中非叶子节点的最大下标
/**
* 未优化(自上向下):从树的根部开始排序,会造成过多的交换
*/
/*for(int root=0;root<max;root++){
boolean flag=buildHeap(arr,high,root);
//如果孩子之间有交换,就要重新开始
if(flag) root=-1;
print(debug);
}*/
/**
* 优化(自下向上):从树的n-1层开始排序,
*/
for(int root=max-1;root>=0;root--){
buildHeap(arr,high,root); //boolean flag=buildHeap(arr,high,root);
//优化后,子节点之间有序,不需要重复排序
//未优化前,如果子节点之间有交换,就要重新开始排序;
//if(flag) root=max;
print(debug);
}
}
//返回一个标记,如果有根与子节点交换就要重新从顶根开始查找不满足最大堆树结构
public boolean buildHeap(int arr[],int high,int root){
int l_child=2*root+1;//左节点
int r_child=2*root+2;//右节点
int swap;
//判断是否有右子节点
if(r_child>high){ //没有的话直接比较,小于交换
if(arr[root]<arr[l_child]){
swap = arr[root];
arr[root]=arr[l_child];
arr[l_child]=swap;
return true;
}else{
return false;
}
}
//在根与两个子节点之间进行排序交换
if((arr[root]<arr[l_child])||(arr[root]<arr[r_child])){
swap = arr[root]; //将root值放入交换区
//在根与两个子节点之间找出最大的那个值进行交换
if(arr[l_child]>=arr[r_child]){
//交换根与左子节点的值
arr[root]=arr[l_child];
arr[l_child]=swap;
//左节点>右节点,左右交换,使排序后 左节点<=右节点
if(arr[l_child]>arr[r_child]){
arr[l_child]=arr[r_child];
arr[r_child]=swap;
}
return true;
}else{
//交换根与右子节点的值
arr[root]=arr[r_child];
arr[r_child]=swap;
//左节点>右节点,左右交换,使排序后 左节点<=右节点
if(arr[l_child]>arr[r_child]){
arr[r_child]=arr[l_child];
arr[l_child]=swap;
}
return true;
}
}else{ //两个子节点之间进行排序交换
if(arr[l_child]>arr[r_child]){
swap = arr[l_child];
arr[l_child]=arr[r_child];
arr[r_child]=swap;
}
}
return false;
}
@Override
public void sort(boolean debug) {
// TODO Auto-generated method stub
printSystime(debug,"HeapSort Start at:");
heapSort(debug, arr,arr.length-1);
printSystime(debug,"HeapSort End at:");
}
}
经过不停的偿试,可以确认是递归调用引起的异常,将递归调用改为循环调用后,问题得到解决。 但是测试的结果让人大失所望: 采用堆排序: 对10万个<10000的随机数进行排序,居然要22秒左右;对20万个<10000的随机数进行排序,居然要1分24秒 而在同样的环境下,采用快速排序: 对3千万个<10000的随机数进行排序,只要6秒左右;对1亿个<10000的随机数进行排序,也不过20秒左右 采用归并排序(非递归),则更快一点: 对3千万个<10000的随机数进行排序,只要5秒左右;对1亿个<10000的随机数进行排序,也不过15秒左右 -------------------------------------------------- 偿试改写为递归调用,又回到最初的问题了,超过数据8500,就会堆栈溢出错误 package com.package2;
/**
* Function: 测试堆排序
*/
import java.util.*;
public class SortMethod {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
HeapSort st = new HeapSort();
TestSort ts=new TestSort();
//int size=11; boolean debug=false;
int size=8970; boolean debug=true; //debug=true时仅打印排序开始与结束时间,不打印排序过程
int arr[]=new int[size];
for(int i=0;i<size;i++) arr[i]=(int)(Math.random()*10000); //生成测试数据
ts.process(debug, st, arr);
}
}
class TestSort{
public void process(boolean debug, Sort sort, int[] arr){
sort.setArr(arr);
sort.print(debug);
sort.sort(debug);
sort.print(debug);
}
}
interface Sort{
public void setArr(int[] arr);
public void print(boolean debug);
public void printSystime(boolean debug, String pre);
public void sort(boolean debug);
}
class HeapSort implements Sort{
int arr[];
public void setArr(int[] arr) {
this.arr = arr;
}
public void print(boolean debug){
if(!debug){
String text="";
for(int i=0;i<arr.length;i++){
text+=","+arr[i];
}
text=text.replaceFirst(",","");
System.out.println("Array is:"+text);
//System.out.print(text+"\n");
}
}
public void printSystime(boolean debug, String pre){
if(debug){
Calendar cal=Calendar.getInstance();
System.out.println(pre+cal.getTime());
}
}
/**
* 构建大顶堆
*/
public void adjustHeap(boolean debug, int[] arr, int root, final int len) {
int swap = arr[root],i;
for (i = (root << 1) + 1; i < len; i = (i << 1) + 1) {
if (i+1<len && arr[i]<arr[i+1]) ++i;
if (swap >= arr[i]) break; //根节点与最大的子节点比较,如不需要交换则退出循环
arr[root] = arr[i];
root = i;
}
arr[root] = swap;
}
public void heapSort_nonRecu(boolean debug, int[] arr) {
//length >> 1 等价于 length/2,计算完全二叉树中非叶子节点的最大下标 (从1开始)
//root指示所有非叶子节点,也即所有根节点 root=(arr.length >> 1)-1
//初始化堆,创建大顶堆
for (int root = (arr.length >> 1) - 1; root >= 0; --root) {
//for (int root = (arr.length/2) - 1; root >= 0; --root) {
adjustHeap(debug, arr, root, arr.length);
}
//print(debug);
int swap;
//反复进行:将大顶堆的顶点置换到堆的最后(数组尾部)==>去尾==>创建大顶堆 //这一过程
for (int len = arr.length; len > 1; --len) {
//将大顶堆的顶点置换到堆的最后(数组尾部)
swap=arr[0];
arr[0]=arr[len-1];
arr[len-1]=swap;
adjustHeap(debug, arr, 0, len - 1); //去尾(len - 1),创建大顶堆adjustHeap
}
}
//递归调用,当数据超过8500时,stack溢出,不明原因
public void heapSort(boolean debug, int[] arr) {
//初始化堆,创建大顶堆
initHeap(debug, arr, (arr.length/2) - 1);
print(debug);
//反复进行:将大顶堆的顶点置换到堆的最后(数组尾部)==>去尾==>创建大顶堆 //这一过程
splitHeap(debug, arr, arr.length);
}
public void initHeap(boolean debug, int[] arr, int root) {
//初始化堆,创建大顶堆
if(root<0) return;
int r=root;
if (r>0){
adjustHeap(debug, arr, r, arr.length);
r--;
initHeap(debug, arr, r);
}
}
public void splitHeap(boolean debug, int[] arr, int len) {
//反复进行:将大顶堆的顶点置换到堆的最后(数组尾部)==>去尾==>创建大顶堆 //这一过程
if(len<=1) return;
int s=len;
if(s > 1){
s--;
int swap=arr[0];
arr[0]=arr[s];
arr[s]=swap;
adjustHeap(debug, arr, 0, s);
splitHeap(debug, arr, s);
}
}
@Override
public void sort(boolean debug) {
// TODO Auto-generated method stub
printSystime(debug,"HeapSort Start at:");
//heapSort_nonRecu(debug,arr);
heapSort(debug, arr);
printSystime(debug,"HeapSort End at:");
}
}
|