zoukankan      html  css  js  c++  java
  • 算法练习——堆排序

    堆是一个数据结构,可以看作是一个数组,并且具有完全二叉树的性质,每个结点都对应一个数组的元素。

    堆和数组对应的关系类似于一种层序排列。

    比如:对于数组   A= {27,17,16,13,10,1,5,7,12,14,8,9}

    A数组在堆中表现的情况如下

    1:各结点之间的性质

    对于根节点从  0  开始的一个堆,各个结点存在以下关系  

    体现在图(2)中

      //计算二叉树的右孩子下标值
        public int findrightChild(int i) {
              return 2*(i+1);
          }
          
          //计算二叉树的左孩子下标值
          public int findLeftChild(int i) {
              return 2*(i+1)-1;
          }
         
         //计算父亲结点的下标值
         public int findParent(int i){
             return (i-1)/2;
         }

     2:什么是最大堆?

     最大堆指的是除了根节点外所有的结点都满足于:根节点的属性值大于孩子结点的属性值。  A[Parent(i)] >= A[i]

     3:如何进行堆排序?

    堆排序之前都必须做下面三种操作:

    3.1   MaxHeapify()  

      用于维持最大堆的性质   时间复杂度为   O(nlogn)     每次都是在破坏的那个元素开始维持性质  并且是自顶向底开始进行的

    伪代码描述:

     1 Max_Heap(A,i)
     2     l = findleftChild(i)
     3     r = findrightChild(i)
     4     if l <= A.headSize && A[l] > A[i]
     5         largest = l
     6     else largest = i
     7     if r <= A.headSize && A[r] > A[largest]
     8         largest = r
     9     if largest != i
    10         exchange A[i] with  A[largest]
    11         Max_Heap(A, largest)

    在程序的每一步中,从A[i]、A[LEFT(i)]和A[RIGHT(i)]中选出最大的,并将其下标存储在largest中。如果A[i]是最大的,那么以 i 为根结点的子树已经是最大堆,程序结束。

    否则,最大元素是i的某个孩子结点,则交换A[i]和A[largest]的值。从而使i及其孩子都满足最大堆的性质。在交换后,下标为largtst的结点的值是原来的A[i],于是以该结点为根的子树又有可能会违反最大堆的性质.因此,需要对该子树递归调用MAX-HEAPIFY。

     (1) 首先A[1]违反了最大堆的性质    所以将A[1]与其孩子中更大的一个值作交换,此时    A [1] exchange  A[4]   

    (2)A[4]因此不满足最大堆的性质,在继续与其孩子中的更大一个值做交换   

    (3)已经到了叶子结点,没有任何的孩子结点,则调出递归的过程

    该过程的时间复杂度为  O(n)

    3.2  BuildMaxHeap()    

      创建最大堆    具有线性时间复杂度

    思路主要是从低向上的方式将A数组转化为  最大堆的形式

    建立堆的伪代码描述:

    1 Create_heap(A)
    2 A.heapSize = A.length;
    3 for i = [A.length/2] downto 1
    4     Max_Heap(A,i)

     思考:  建立堆的时候为什么  i = [A.length/2]    开始呢?

      当用数组表示存储n个元素的堆时,叶结点下标分别是[n/2]+1,[n/2]+2,[n/2]+3 ...,n

         由上面的图解值,首先从i = length / 2  开始  使每个结点的子树都满足  最大堆的性质。不断的递归调用MaxHeapify() 方法由低向顶部一步一步实现。

    3.3  HeapSort()        

       对于一个数组做原址排序(原址排序:任何时候只需要常数个的额外空间来保存临时变量)

    最后一步:对于已经排列好的堆 ,我们只需要每次将最后一个结点与根节点作交换,并且取出根节点  ,作为输出元素,然后将剩余的结点再调用  MaxHeapify()  方法,让其他结点仍然具有最大堆 的性质。

    1 // (自底向上进行)
    2 HeapSort(A)
    3     Create_heap(A)
    4     for i = A.length downto 2
    5         Exchange A[i] with A[1]
    6         A.heapSize--
    7         Max_Heap(A,i)

    HEAPSORT过程的时间复杂度是O(nlgn),因为每次调用CreateMaxHeap()的时间复杂度是O(n),而n-1次调用MAXHEAPIFY,每次的时间为O(lgn)。

     当所有的元素都递归排序完成,则依次挑出来的元素都为已经排列好的序列。

    下面给出具体的实现代码:

     1 package com.hone.heapsort;
     2 
     3 public class HeapSort {
     4     static int[] a = {4,1,3,2,16,9,10,14,8,7};
     5     public static int [] heap =a ;
     6     public static int heapsize = heap.length;
     7     
     8     //1:保证堆满足最大堆的性质
     9     /*
    10      * 这里面的性质有一个问题,对于第一个根节点的元素不是最大值的时候,会出现局部
    11      * 最大堆  (并不是一个问题,以为每次调用的点,都是从该点开始堆才遭到破坏)
    12      */
    13     
    14     public static  void maxHeap(int[] A , int i){
    15         
    16         int largest = i; 
    17         int l = findLeftChild(i);
    18         int r = findrightChild(i);
    19         if (l < heapsize && A[l] > A[i]) 
    20             largest = l;
    21         if (r < heapsize && A[r] > A[largest]) 
    22             largest = r;
    23         if (largest != i) {
    24             swap(A,largest,i);
    25             maxHeap(A, largest);
    26         }
    27         
    28     }
    29     
    30     //2:用于创建最大堆
    31     public  void  createHeap(int[] A){
    32         for (int i = heapsize/2-1; i >= 0; i--) {
    33             maxHeap(A, i);
    34         }
    35     }
    36 
    37     //3: 用于堆排序算法
    38     public  void heapSort(int[] A){
    39         createHeap(A);
    40         System.out.print("排序后: ");
    41         for (int i = heapsize-1; i >= 0; i--) {
    42             swap(A,i,0);
    43             System.out.print(A[i]+" ");
    44             heapsize --;
    45             maxHeap(A, 0);
    46         }
    47     }
    48     
    49     // 定义一个方法交换两个数据
    50     private static void swap(int[] a,int i,int j) {
    51         int temp;
    52         temp = a[j];
    53         a[j] = a[i];
    54         a[i] = temp;
    55     }
    56     //定义一个方法计算二叉树的右孩子
    57     public static int findrightChild(int i) {
    58         return 2*(i+1);
    59     }
    60     
    61     //定义一个方法计算二叉树的左孩子
    62     public static int findLeftChild(int i) {
    63         return 2*(i+1)-1;
    64     }
    65     
    66     
    67     public static int findParent(int i){
    68         return (i-1)/2;
    69     }
    70     
    71     public static void main(String[] args){
    72         HeapSort hs = new HeapSort();
    73         System.out.print("原数组: ");
    74         for (int i = 0; i < a.length; i++) {
    75             System.out.print(a[i]+" ");
    76         }
    77         System.out.println();
    78         hs.heapSort(a);
    79     }
    80 }

     但是,方法并没有得到完整的封装,而且没有很好的用到java中面向对象的思想。

    因此代码做如下改变:

    下面代码转载于:http://www.cnblogs.com/developerY/p/3319618.html     只作为学习使用。

     1 package com.hone.heapsort;
     2 
     3 //定义一个类将MaxHeap单独的类
     4 public class MaxHeap {
     5     int[] heap;
     6     int heapsize;
     7     
     8     public MaxHeap(int[] array)
     9     {
    10         this.heap=array;    
    11         this.heapsize=heap.length;
    12     }
    13     
    14     public void BuildMaxHeap()
    15     {
    16         for(int i=heapsize/2-1;i>=0;i--)
    17         {
    18             Maxify(i);//依次向上将当前子树最大堆化
    19         }
    20     }
    21     
    22     public void HeapSort()
    23     {
    24         for(int i=0;i<heap.length;i++)
    25         {
    26             //执行n次,将每个当前最大的值放到堆末尾
    27             int tmp=heap[0];
    28             heap[0]=heap[heapsize-1];
    29             heap[heapsize-1]=tmp;
    30             heapsize--;
    31             Maxify(0);
    32         }
    33     }
    34     
    35     public void Maxify(int i)
    36     {
    37         int l=Left(i);
    38         int r=Right(i);
    39         int largest;
    40         
    41         if(l<heapsize&&heap[l]>heap[i])
    42             largest=l;
    43         else
    44             largest=i;
    45         if(r<heapsize&&heap[r]>heap[largest])
    46             largest=r;
    47         if(largest==i||largest>=heapsize)//如果largest等于i说明i是最大元素 largest超出heap范围说明不存在比i节点大的子女
    48             return ;
    49         int tmp=heap[i];//交换i与largest对应的元素位置,在largest位置递归调用maxify
    50         heap[i]=heap[largest];
    51         heap[largest]=tmp;
    52         Maxify(largest);
    53     }
    54     
    55     public void IncreaseValue(int i,int val)
    56     {
    57         heap[i]=val;
    58         if(i>=heapsize||i<=0||heap[i]>=val)
    59             return;
    60         int p=Parent(i);
    61         if(heap[p]>=val)
    62             return;
    63         heap[i]=heap[p];
    64         IncreaseValue(p, val);
    65     }
    66     
    67     private int Parent(int i)
    68     {
    69         return (i-1)/2;
    70     }
    71     private int Left(int i)
    72     {
    73         return 2*(i+1)-1;
    74     }
    75     private int Right(int i)
    76     {
    77         return 2*(i+1);
    78     }
    79 }
     1 package com.hone.heapsort;
     2 
     3 public class Demo {
     4     
     5     public static void main(String[] args)
     6     {
     7         int[] array=new int[]{1,2,19,4,7,8,9,10,14,16};
     8         MaxHeap heap=new MaxHeap(array);
     9         System.out.println("执行最大堆化前堆的结构:");
    10         printHeapTree(heap.heap);
    11         heap.BuildMaxHeap();
    12         System.out.println("执行最大堆化后堆的结构:");
    13         printHeapTree(heap.heap);
    14         heap.HeapSort();
    15         System.out.println("执行堆排序后数组的内容");
    16         printHeap(heap.heap);
    17         
    18     }
    19     private static void printHeapTree(int[] array)
    20     {
    21         for(int i=1;i<array.length;i=i*2)
    22         {
    23             for(int k=i-1;k<2*(i)-1 &&k<array.length;k++)
    24             {
    25                 System.out.print(array[k]+" ");
    26             }
    27             System.out.println();
    28         }    
    29     }
    30     private static void printHeap(int[] array)
    31     {
    32         for(int i=0;i<array.length;i++)
    33         {
    34             System.out.print(array[i]+" ");
    35         }
    36     }
    39 }
  • 相关阅读:
    c# GDI+ 绘制矩形圆角
    ActivityManager
    PowerDesigner15 下载 数据库建模工具
    IaaS、PaaS和SaaS
    discuz 社区工具
    c# 字符串比较方法
    android adb 命令大全
    unity
    backtrack5 中文网
    aws
  • 原文地址:https://www.cnblogs.com/xiaxj/p/7403455.html
Copyright © 2011-2022 走看看