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

目录

1 问题描述

2 解决方案 

2.1  堆排序原理简介

2.2  变治法原理简介

2.3  具体编码

2.4  运行结果截图


1 问题描述

(1)实验题目

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

(2)实验目的

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

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

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

(3)实验要求

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

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


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  运行结果截图

时间: 2024-08-09 22:00:46

算法笔记005:堆排序【变治法】的相关文章

算法笔记之堆排序

一.对堆排序的相关了解 1.堆排序的运行时间是 O(nlogn) : 2.定义: 堆heap是一棵具有以下属性的二叉树-- (1)它是一棵完全二叉树: (2)每个结点大于或等于它的任意一个孩子. 备注:完全二叉树的定义--除了最后一层没填满以及最后一层的叶子都是偏左放置的,其他层都是满的二叉树! 3.二叉堆有两种:最大堆和最小堆.在堆排序中我们使用的是最大堆,最小堆常常在构造优先队列时使用. 4.一条路径的深度指的是这条路径的边数,一个结点的深度是指从根结点到该结点的路径的长度. 二.对堆进行排

算法笔记_003:矩阵相乘问题【分治法】

目录 1 问题描述  1.1实验题目 1.2实验目的 1.3实验要求 2 解决方案 2.1 分治法原理简述 2.2 分治法求解矩阵相乘原理 2.3 具体实现源码 2.4 运算结果截图 1 问题描述 1.1实验题目 设M1和M2是两个n×n的矩阵,设计算法计算M1×M2 的乘积. 1.2实验目的 (1)提高应用蛮力法设计算法的技能: (2)深刻理解并掌握分治法的设计思想: (3)理解这样一个观点:用蛮力法设计的算法,一般来说,经过适度的努力后,都可以对其进行改进,以提高算法的效率. 1.3实验要求

算法笔记之排序

最近在看<算法笔记>,如果单从算法来说,这本书真正做到了短小精悍,首先以排序入题,那么我们今天也来说说排序. 排序 将一堆杂乱无章的元素按照某种规则有序排列的过程就叫"排序".排序是一种非常基础的算法,有着广泛的理论和实践基础.对一个排序算法来说,一般从如下3个方面衡量算法的优劣: 时间复杂度:主要是分析关键字的比较次数和记录的移动次数. 空间复杂度:分析排序算法中需要多少辅助内存 稳定性:若两个记录A和B的关键字值相等,但排序后A.B的先后次序保持不变,则称这种算法是稳定

算法三之堆排序

一.堆(Heap)定义 (1)n个关键字序列Kl,K2,-,Kn称为(Heap),当且仅当该序列满足如下性质(简称为堆性质): k(i)<=k(2i)且k(i)<=k(2i+1)(1≤i≤ n/2), 当然,这是小根堆,大根堆则换成>=号. (2)k(i)相当于二叉树的非叶子结点,K(2i)则是左子节点,k(2i+1)是右子节点 若将此序列所存储的向量R[1..n]看做是一棵完全二叉树的存储结构,则堆实质上是满足如下性质的完全二叉树:树中任一非叶子结点的关键字均不大于(或不小于)其左右孩

算法笔记_023:拓扑排序(Java)

目录 1 问题描述 2 解决方案 2.1 基于减治法实现 2.2 基于深度优先查找实现 1 问题描述 给定一个有向图,求取此图的拓扑排序序列. 那么,何为拓扑排序? 定义:将有向图中的顶点以线性方式进行排序.即对于任何连接自顶点u到顶点v的有向边uv,在最后的排序结果中,顶点u总是在顶点v的前面. 2 解决方案 2.1 基于减治法实现 实现原理:不断地做这样一件事,在余下的有向图中求取一个源(source)(PS:定义入度为0的顶点为有向图的源),它是一个没有输入边的顶点,然后把它和所有从它出发

1165: 零起点学算法72——首字母变大写

1165: 零起点学算法72--首字母变大写 Time Limit: 1 Sec  Memory Limit: 64 MB   64bit IO Format: %lldSubmitted: 705  Accepted: 439[Submit][Status][Web Board] Description 输入一个英文句子,将每个单词的第一个字母改成大写字母. Input 输入数据包含多个测试实例,每个测试实例是一个长度不超过100的英文句子,占一行. Output 请输出按照要求改写后的英文句

算法笔记-DTW动态时间规整

算法笔记-DTW动态时间规整 简介 简单的例子 定义 讨论 约束条件 步模式 标准化 点与点的距离函数 具体应用场景 分类 点到点匹配 算法笔记-DTW动态时间规整 动态时间规整/规划(Dynamic Time Warping, DTW)是一个比较老的算法,大概在1970年左右被提出来,最早用于处理语音方面识别分类的问题. 1.简介 简单来说,给定两个离散的序列(实际上不一定要与时间有关),DTW能够衡量这两个序列的相似程度,或者说两个序列的距离.同时DTW能够对两个序列的延展或者压缩能够有一定

小算法笔记

素数: 除 1 外只能被 1 和自身整除的数. 方法一: #include <stdio.h> #define N 1000 int num = 0; int prime(int n) { int i; if(n % 2 == 0) return (n == 2); if(n % 3 == 0) return (n == 3); if(n % 5 == 0) return (n == 5); for(i = 7; i*i <= n; ++i) if(n % i == 0) return

算法笔记_018:旅行商问题(Java)

目录 1 问题描述 2 解决方案 2.1 蛮力法   1 问题描述 何为旅行商问题?按照非专业的说法,这个问题要求找出一条n个给定的城市间的最短路径,使我们在回到触发的城市之前,对每个城市都只访问一次.这样该问题就可以表述为求一个图的最短哈密顿回路的问题.(哈密顿回路:定义为一个对图的每个顶点都只穿越一次的回路) 很容易看出来,哈密顿回路也可以定义为n+1个相邻顶点v1,v2,v3,...,vn,v1的一个序列.其中,序列的第一个顶点和最后一个顶点是相同的,而其它n-1个顶点都是互不相同的.并且