内部排序->选择排序->堆排序

文字描述

  堆排序中,待排序数据同样可以用完全二叉树表示, 完全二叉树的所有非终端结点的值均不大于(或小于)其左、右孩子结点的值。由此,若序列{k1, k2, …, kn}是堆,则堆顶元素(或完全二叉树的根)必为序列中n个元素的最小值(或最大值)。

  若在输出堆顶的最小值之后,使得剩余n-1个元素的序列重又建成一个堆,则得到n个元素中的次小值。如此反复执行,便能得到一个有序序列,这个过程称之为堆排序。

  由此,实现堆排序需要解决两个问题:(1)如何由一个无序序列建成一个堆?(2)如何在输出堆顶元素之后,调整剩余元素成为一个新的堆?

  先讨论第(2)个问题,假设有个最大堆(堆顶元素为堆的最大值)输出堆顶元素之后, 以堆中最后一个元素代替之,此时根结点的左、右子树均为堆,则仅需自上至下进行调整即可。称这个自堆顶至叶子的调整过程为“筛选”。

再讨论第(1)个问题,由一个无序序列建成堆的过程就是一个反复”筛选”的过程。若将此序列看成一个完全二叉树,则最后一个非终端结点是第[n/2]个元素,由此“筛选”只需从第[n/2]个元素开始。

示意图

算法分析

  堆排序在树形选择排序上进行了改进, 和树形选择排序比, 堆排序可以减少辅助空间, 避免和”最大值”的冗余比较等优点.

  堆排序在最坏情况下,其时间复杂度也为nlogn。相对于快速排序来说,这是堆排序的最大优点(快速排序最坏情况下时间复杂度为n*n, 但平均性能为nlogn)。

  堆排序只需一个记录大小的辅助空间供交换用。

  堆排序是一种不稳定的排序方法。

  另外,堆排序方法对记录数较少的文件并不提倡使用, 但是对n较大的文件还是很有效的,因为其运行时间主要耗费在建立初始堆和调整新堆时的反复“筛选”上。

代码实现

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <math.h>
  4 /*
  5  * double log2(double x);    以2为底的对数
  6  * double ceil(double x);    取上整
  7  * double floor(double x);    取下整
  8  * double fabs(double x);    取绝对值
  9  */
 10
 11 #define DEBUG
 12
 13 #define EQ(a, b) ((a) == (b))
 14 #define LT(a, b) ((a) <  (b))
 15 #define LQ(a, b) ((a) <= (b))
 16
 17 #define MAXSIZE  100
 18 #define INF         1000000
 19 typedef int KeyType;
 20 typedef char InfoType;
 21 typedef struct{
 22     KeyType key;
 23     InfoType otherinfo;
 24 }RedType;
 25
 26 typedef struct{
 27     RedType r[MAXSIZE+1];
 28     int length;
 29 }SqList;
 30
 31 void PrintList(SqList L){
 32     int i = 0;
 33     printf("下标值:");
 34     for(i=0; i<=L.length; i++){
 35         printf("[%d] ", i);
 36     }
 37     printf("\n关键字:");
 38     for(i=0; i<=L.length; i++){
 39         if(EQ(L.r[i].key, INF)){
 40             printf(" %-3c", ‘-‘);
 41         }else{
 42             printf(" %-3d", L.r[i].key);
 43         }
 44     }
 45     printf("\n其他值:");
 46     for(i=0; i<=L.length; i++){
 47         printf(" %-3c", L.r[i].otherinfo);
 48     }
 49     printf("\n\n");
 50     return ;
 51 }
 52
 53 //堆采用顺序存储表示
 54 typedef SqList HeapType;
 55
 56 /*
 57  *已知H->r[s,...,m]中记录的关键字除H->r[s].key之外均满足的定义
 58  *本汉书调整H-r[s]的关键字,使H->r[s,...,m]成为一个大顶堆(对其中
 59  *记录的关键字而言)
 60  */
 61 void HeapAdjust(HeapType *H, int s, int m)
 62 {
 63     RedType rc = H->r[s];
 64     int j = 0;
 65     //沿key较大的孩子结点向下筛选
 66     for(j=2*s; j<=m; j*=2){
 67         //j为key较大的纪录的下标
 68         if(j<m && LT(H->r[j].key, H->r[j+1].key))
 69             j+=1;
 70         //rc应该插入位置s上
 71         if(!LT(rc.key, H->r[j].key))
 72             break;
 73         H->r[s] = H->r[j];
 74         s = j;
 75     }
 76     //插入
 77     H->r[s] = rc;
 78 }
 79
 80 /*
 81  * 对顺序表H进行堆排序
 82  */
 83 void HeapSort(HeapType *H)
 84 {
 85     int i = 0;
 86     //把H->r[1,...,H->length]建成大顶堆
 87     for(i=H->length/2; i>=1; i--){
 88         HeapAdjust(H, i, H->length);
 89     }
 90 #ifdef DEBUG
 91     printf("由一个无序序列建成一个初始大顶堆:\n");
 92     PrintList(*H);
 93 #endif
 94     RedType tmp;
 95     for(i=H->length; i>1; i--){
 96         //将堆顶记录和当前未经排序子序列H->r[1,...,i]中最后一个记录相互交换
 97         tmp = H->r[1];
 98         H->r[1] = H->r[i];
 99         H->r[i] = tmp;
100         //将H->r[1,...,i-1]重新调整为大顶堆
101         HeapAdjust(H, 1, i-1);
102 #ifdef DEBUG
103         printf("调整1至%d的元素,使其成为大顶堆:\n", i-1);
104         PrintList(*H);
105 #endif
106     }
107 }
108
109 int  main(int argc, char *argv[])
110 {
111     if(argc < 2){
112         return -1;
113     }
114     HeapType H;
115     int i = 0;
116     for(i=1; i<argc; i++){
117         if(i>MAXSIZE)
118             break;
119         H.r[i].key = atoi(argv[i]);
120         H.r[i].otherinfo = ‘a‘+i-1;
121     }
122     H.length = (i-1);
123     H.r[0].key = 0;
124     H.r[0].otherinfo = ‘0‘;
125     printf("输入数据:\n");
126     PrintList(H);
127     //对顺序表H作堆排序
128     HeapSort(&H);
129     return 0;
130 }

堆排序

运行

原文地址:https://www.cnblogs.com/aimmiao/p/9379140.html

时间: 2024-10-10 12:54:04

内部排序->选择排序->堆排序的相关文章

算法大神之路----排序(选择排序法)

选择排序法,顾名思义,就是把特定的数据选择出来进行排序. 选择排序法有两种方式 在所有的数据中,当由大到小排序,那么就将最大值放到第一个位置 如果由小到大排序,那么就将最小值放到第一个位置 以由小到大排序举例,当排序时候,扫描整个数据,拿第一个依次与其他做比较,如果其他数据比第一个大,或者相等,那么就不交换,如果其他数据比第一个数小,那么就交换二者的位置,扫描结束后,则从第二个数开始,依次扫描. 方法分析 无论是最坏还是最好情况,甚至是平均情况下,都需要对全部数据进行扫描,找到最大或最小值,因此

01. Java的经典排序--选择排序--冒泡排序--折半查找(二分查找)

Java的经典排序--选择排序--冒泡排序--折半查找 选择排序 选择排序 3 2 1 5 8 0 1 3 2 5 8 1 1 2 3 5 8 2 1 2 3 5 8 3 1 2 3 5 8 public static void main(String[] args) { int[] arr={3,2,1,5,8}; selectSort(arr); for(int i = 0 ; i < arr.length ; i ++){ System.out.println(arr[i]) ; } }

排序 选择排序&amp;&amp;堆排序

选择排序&&堆排序 1.选择排序: 介绍:选择排序(Selection sort)是一种简单直观的排序算法.它的工作原理如下.首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾.以此类推,直到所有元素均排序完毕. 步骤:假设数组array长度为N即有数组内有N个数据未排序数据 1.第一趟遍历将这N个数据中最小的数据和array[0]交换. 2.第二趟则遍历N-1个数据,将这N-1个数据中最小的和arra

内部排序-&gt;选择排序-&gt;树形选择排序

文字描述 树形选择排序又称锦标赛排序; 比如,在8个运动员中决出前3名至多需要11场比赛, 而不是7+6+5=18场比赛(它的前提是甲胜乙,乙胜丙,则甲必能胜丙) 首先对n个记录的关键字进行两两比较,然后在(n/2)个较小者之间再进行两两比较,直至选出最小关键字的记录为止,这个过程可用一颗有n个叶子结点的完全二叉树表示.关于完全二叉树的定义和与本排序算法用到的性质见附录1 示意图 算法分析 由于含n个叶子结点的完全二叉树的深度为[log2n]+1, 则在树形选择排序中,除了最小关键字外,每选择一

数据结构排序-选择排序

选择排序中的两个经典算法:简单选择排序,堆排序. 简单选排:通过n-1次数据元素的比较,从n-i+1个记录中选择最小的数据,并与第i个数据进行交换,它的时间复杂度是O(n^2). 堆排序:利用堆的特征进行排序,复杂度为O(n*logn). 1 #include <stdio.h> 2 #include <stdlib.h> 3 4 int n; 5 6 /* 7 * 选择排序 8 */ 9 void SelectSort(int *array) 10 { 11 int i, j,

排序--选择排序

//选择排序void Select_Sort(int *a,int n){ int i,j,k,temp; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++) { if(a[k]>a[j]) k=j; } temp=a[i]; a[i]=a[k]; a[k]=temp; }}

排序—选择排序

选择排序 排序要求:把长度为n的数组a按照从小到大的顺序进行排序. 冒泡排序思路:给定一个长度为n的数组a,循环n-1次,每次循环找出a[i]到a[n]中最小数的数,然后把该数和a[i]交换位置. 如何找出最小的数?:循环数组,逐个判断大小,把较小的数的脚标取出来,此次循环结束之后把脚标位置的数和a[i]进行交换. 排序示例: 原数组: 2.0.3.6.8.4.9.5.1.7.第1次循环排序结果: 0.2.3.6.8.4.9.5.1.7.第2次循环排序结果: 0.1.3.6.8.4.9.5.2.

iOS疯狂详解之排序(选择排序/插入排序)

选择排序 1.先求最小值 2.找到位置 3.把位置的数放到有序区 4.重复 for (int j = 0; j < count - 1; j++) { int minIndex = j;// 最小值的角标 for (int i = minIndex + 1; i < count; i++) { if (array[minIndex] > array[i]) { minIndex = i; } } if (minIndex != j) { // 优化 无序区的头 不是第一个 // 最小值

数据结构——排序——选择排序算法

选择排序(Selection sort)是一种简单直观的排序算法.它的工作原理如下.首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾.以此类推,直到所有元素均排序完毕. 选择排序的主要优点与数据移动有关.如果某个元素位于正确的最终位置上,则它不会被移动.选择排序每次交换一对元素,它们当中至少有一个将被移到其最终位置上,因此对n个元素的表进行排序总共进行至多n-1次交换.在所有的完全依靠交换去移动元素的排序方