算法笔记005:堆排序【变治法】




 


1 问题描述

1)实验题目

   用基于变治法的堆排序算法对任意一组给定的数据进行排序

2)实验目的

   1)深刻理解并掌握变治法的设计思想;

   2)掌握堆的概念以及如何用变治法把任意给定的一组数据改变成堆;

   3)提高应用变治法设计算法的技能。

3)实验要求

   1)设计与实现堆排序算法;

   2)待排序的数据可以手工输入(通常规模比较小,10个数据左右),用以检测程序的正确性;也可以计算机随机生成(通常规模比较大,15003000个数据左右),用以检验(用计数法)堆排序算法的时间效率。

 


2 解决方案

2.1  堆排序原理简介

堆可以定义为一颗二叉树,树的节点中包含键(每个节点是一个键),并且满足下面两个条件:

(1)树的形状要求——这颗二叉树是基本完备的(或者简称为完成二叉树),这意味着,树的每一层都是满的,除了最后一层最右边的元素有可能缺位。

(2)父母优势要求,又称为堆特性——每一个节点的键都要大于或等于它子女的键(对于任何孩子节点也要自动满足父母优势要求)。

2.2  变治法原理简介

变治法:首先,在“变”的阶段,出于这一或者那样的原因,把问题的实例变得更容易求解(PS:类似本文求解问题,在排序前先把数组中数进行成堆处理);然后,在第二阶段或者说“治”的阶段,对实例进行求解。

根据我们对问题实例的变换方式,变治思想有3种主要的类型:

(1)变换为同样问题的一个更简单或者更方便的实例——我们称之为实例化简;

(2)变换为同样实例的不同表现——我们称之为改变表现;

(3)变换为另一个问题的实例,这种问题的算法是已知的——我们称之为问题的化简。

2.3  具体编码

复制代码
package com.liuzhen.heapsort;

public class HeapSort {
    /*将array[a]和array[b]、array[c]中最大值进行比较,如果较小则将array[a]与array[b]、array[c]中最大值
    进行交换,否则直接返回数组array*/
    public static int[] getMaxA(int[] array,int a,int b ,int c){
        int temp = 0;
        if(array[b] >= array[c]){
            if(array[a] < array[b]){
                temp = array[a];
                array[a] = array[b];
                array[b] = temp;
            }
        }
        else{
            if(array[a] < array[c]){
                temp = array[a];
                array[a] = array[c];
                array[c] = temp;
            }
        }
        return array;
    }
    
    //根据堆排序父母优势规则,返回一个给定长度的数组的成堆结果
    public static int[] getHeapSort(int[] array , int len){
        boolean judge = true;
        while(judge){
            //根据堆排序父母优先规则,对数组array进行排序
            for(int i = 1;i <= len/2;i++){
               if((2*i+1) < len)   
                   array = getMaxA(array,i,(2*i),(2*i+1));
               if((2*i) == len-1){    //当2*i == len-1时,说明array[i]只有一个左孩子节点a[2*i]
                   int temp = 0;
                   if(array[i] < array[2*i]){
                       temp = array[i];
                       array[i] = array[2*i];
                       array[2*i] = temp;
                   }
               }
            }
            
            //遍历数组array,一旦出现根节点小于其叶子节点时,跳出for循环
            int j;
            for(j = 1;j < len/2;j++){
                if((2*j+1) < len){
                    if(array[j] < array[2*j])
                        break;
                    if(array[j] < array[2*j+1])
                        break;
                }
                if((2*j) == len-1){
                    if(array[j] < array[2*j])
                        break;
                }
            }
            
            if(j == len/2)  //如果j==len/2,说明遍历结果符合堆排序规则,直接结束while循环
                judge = false;
        }        
        return array;
    }
    
    //使用数组成堆,对一个数组元素进行从小到大排序,并返回排序后的结果
    public static int[] getResultSort(int[] array , int len){
        array =  getHeapSort(array , len);           //首先对数组进行堆排序处理
        int temp = 0;        //数组值交换中间变量
        int sortLen = len;   //排序过程中,需要重新进行堆排序的数组长度,并初始化为array的总长度
        while(sortLen > 2){    
//            for(int i = 1;i < len;i++)
//                System.out.print(array[i]+"\t");
//            System.out.println();
            temp = array[1];             //交换array[0]和array[sortLen-1]的值,即把最大的值放在未排序的数组最后一位
            array[1] = array[sortLen-1];
            array[sortLen-1] = temp;            
            sortLen = sortLen - 1;                   //交换成功后,未排序的数组长度自动减1            
            array = getHeapSort(array,sortLen);      //对未排序的数组,重新进行堆排序        
        }        
        return array;
    }
    
    //初始化一个长度为n的随机数组
    public static int[] initArray(int n){
        int[] result = new int[n];
        result[0] = 0;
        for(int i = 1;i < n;i++)
            result[i] = (int)(Math.random()*1000); //采用随机函数随机生成0~1000之间的数
        return result;        
    }
    
    public static void main(String args[]){
        int[] array = {0,1,4,5,3,5,23,45,12,23,34,56,78,23,24,25}; //此处定义数组,对array[1]到array[len-1]进行排序
        int len = array.length;
        int[] result = getResultSort(array,len);
        System.out.println("手动输入数组,使用堆排序,最终排序结果:");
        for(int i = 1;i < len;i++){
            System.out.print(result[i]+"\t");
        }
        
        System.out.println();
        System.out.println();
        int[] oneArray = initArray(1000);
        int len1 = 1000;
        int[] result1 = getResultSort(oneArray,len1);
        System.out.println("系统随机生成的长度为1000的数组(其值均在0~1000之间),使用堆排序,最终排序结果:");
        for(int j = 1;j < len1;j++){
            System.out.print(result1[j]+"\t");
            if(j%15 == 0)
                System.out.println();
        }    
    }
}
复制代码

2.4  运行结果截图

 

每天一小步,成就一大步
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
算法设计与分析》目录: 第一篇引入篇 第1章算法概述1.1用计算机求解问题算法 1.1.1用计算机求解问题的步骤 1.1.2算法及其要素和特性 1.1.3算法设计及基本方法 1.1.4从算法到实现 1.2算法描述 1.2.1算法描述简介 1.2.2算法描述约定 1.2.3一个简单问题的求解过程 1.3现代常用算法概览* 1.3.1压缩算法 1.3.2加密算法 1.3.3人工智能算法 1.3.4并行算法 1.3.5其他实用算法 第2章算法分析基础 2.1算法分析体系及计量 2.1.1算法分析的评价体系 2.1.2算法的时间复杂性 2.1.3算法的空间复杂性 2.1.4NP完全性问题 2.2算法分析实例 2.2.1非递归算法分析 2.2.2递归算法分析 2.2.3提高算法质量 第二篇基础篇 第3章算法基本工具和优化技巧3.1循环与递归 3.1.1循环设计要点 3.1.2递归设计要点 3.1.3循环与递归的比较 3.2算法与数据结构 3.2.1原始信息与处理结果的对应存储 3.2.2数组使信息有序化 3.2.3数组记录状态信息 3.2.4大整数存储及运算 3.2.5构造趣味矩阵 3.3优化算法的基本技巧 3.3.1算术运算的妙用 3.3.2标志量的妙用 3.3.3信息数字化 3.4优化算法的数学模型 3.4.1杨辉三角形的应用 3.4.2最大公约数的应用 3.4.3公倍数的应用 3.4.4斐波那契数列的应用 3.4.5递推关系求解方程 习题 第三篇核心篇 第4章基本的算法策略4.1迭代算法 4.1.1递推法 4.1.2倒推法 4.1.3迭代法解方程 4.2蛮力法 4.2.1枚举法 4.2.2其他范例 4.3分治算法 4.3.1分治算法框架 4.3.2二分法 4.3.3二分法异 4.3.4其他分治方法 4.4贪婪算法 4.4.1可绝对贪婪问题 4.4.2相对或近似贪婪问题 4.4.3贪婪策略算法设计框架 4.5动态规划 4.5.1认识动态规划 4.5.2动态规划算法设计框架 4.5.3突出阶段性的动态规划应用 4.5.4突出递推的动态规划应用 4.6算法策略间的比较 4.6.1不同算法策略特点小结 4.6.2算法策略间的关联 4.6.3算法策略侧重的问题类型 习题 第5章图的搜索算法 5.1图搜索概述 5.1.1图及其术语 5.1.2图搜索及其术语 5.2广度优先搜索 5.2.1算法框架 5.2.2广度优先搜索的应用 5.3深度优先搜索 5.3.1算法框架 5.3.2深度优先搜索的应用 5.4回溯法 5.4.1认识回溯法 5.4.2回溯法算法框架 5.4.3应用1——基本的回溯搜索 5.4.4应用2——排列及排列树的回溯搜索 5.4.5应用3——最优化问题的回溯搜索 5.5分支限界法 5.5.1分支搜索算法 5.5.2分支限界搜索算法 5.5.3算法框架 5.6 图的搜索算法小结 习题 第四篇应用篇 第6章算法设计实践6.1循环赛日程表 6.2求3个数的最小公倍数 6.3猴子选大王 6.4最大子段和问题 6.5背包问题 6.5.1与利润无关的背包问题 6.5.2与利润有关的背包问题

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值