排序专题(四) / 不稳定的内部排序 / 堆排序

  • 堆的定义
    • 前置定义
      • 完全二叉树
        • 完全二叉树是这样一棵树:设此树的高度为h,除第h层外,其它各层(1~h-1)的结点数都达到最大个数(2i-1),第h层所有的结点都连续集中在最左边。
      • 满二叉树(满二叉树在国内国外的定义是不同的)
        • 国内定义:设一个二叉树的高度为h,若其有2h-1个结点,则其为满二叉树。(显然,叶子结点只可能出现在第h层,且除叶子结点外的其它结点都有两个子节点)
        • 美国及国际上所定义的满二叉树
          • full binary tree,美国NIST给出的定义为:A binary tree in which each node has exactly zero or two children. In other words, every node is either a leaf or has two children. For efficiency, any Huffman coding is a full binary tree.
          • 即:满二叉树的任意结点,要么度为0,要么度为2,。换句话说:每个结点,要么为叶子结点,要么同时具有左右孩子。简洁明了地说,Huffman树就是一棵满二叉树。(这显然与国内的定义完全不同)
    • 显然,堆应该是一个完全二叉树
    • 堆一般都是用一个一维数组来存储的,完全二叉树的结构是隐含在这个一维数组中的,下所述假设数组的下标是从0开始的,i为该元素在存储数组中的索引:
      • 父结点i的左孩子在存储数组中的索引为:2*i+1
      • 父结点i的右孩子在存储数组中的索引为:2*i+2
      • 子结点i的父结点在存储数组中的索引为:floor((i-1)/2) (floor:向下取整;ceil:向上取整;)
    • 有了上面的铺垫大顶堆/小顶堆就很容易定义了
      • 大顶堆:若一个结点有左右孩子,则该结点值大于其左右孩子结点的值
      • 小顶堆:若一个结点有左右孩子,则该结点值小于其左右孩子结点的值
    • 则显然:大顶堆的根元素一定是最大元素;小顶堆的根元素一定是最小元素
  • 堆排序
    • 时间复杂度:O(nlogn)
    • 排序过程
      • 初始构建堆(堆排序主要时间花费在这一步骤上,有两种方式:)
        • 从空数组开始一个个数据添加,每添加一个元素根据堆的定义调整使得添加完后的数组符合堆的定义
        • 从数组索引为array.length/2处开始调整,直至根结点,具体看下代码中注释
      • 输出堆顶元素,用堆的最后一个元素替代输出堆顶元素所空出来的位置,调整剩余元素为新的堆结构,如此循环,直至输出所有元素,得到有序序列,具体调整过程如下:
        • 用堆的最后一个元素替代输出堆顶元素所空出来的位置,从堆顶开始向下调整新的结构为堆
        • 可以证明:调整的过程,堆顶元素走的路线一定是一条从根到某叶子结点的唯一路线
        • 具体看下代码中注释
  • 堆改进,增加删除方法,使其成为类似二叉排序树的一种存储结构
    • 删除方法
      • 情况1:如果删除的是最末元素,直接删除
      • 情况2:如果删除的元素既没有左孩子又没有右孩子,则用最末元素与该元素交换,删除最末元素,从原最末元素现所在索引开始向上调整新结构为堆结构
      • 情况3:如果删除的元素有左孩子或者有右孩子或者同时具有左右孩子,则:
        • 大顶堆:交换该元素与其左右孩子中的较大者,一直到该元素被交换成为一个叶子结点,则就是情况2了
        • 小顶堆:交换该元素与其左右孩子中的较小者,一直到该元素被交换成为一个叶子结点,则就是情况2了
  • 堆排序代码
    • public class HeapSort {
          
          /*
           *  How to run the "adjuster" method on the given array just with one extra Integer space?
           *  
           *  Can you give another solution?
           */
          public static void hahahaha(int[] array) {
              build(array);
              int max = array[0];
              for (int j = array.length - 1; j > 0; j --) {
                  adjuster(array, j);
                  array[j] = array[0];
              }
              array[0] = max;
          }
          
          private static void build(int[] array) {
              
              int index = array.length / 2;
              
              while (index > 0) {
                  if (index * 2 + 1 < array.length) {
                      if (index * 2 + 2 < array.length) {
                          if (array[index * 2 + 1] > array[index * 2 + 2]) {
                              if (array[index] < array[index * 2 + 1]) {
                                  Util.swap(array, index, index * 2 + 1);
                              }
                          } else {
                              if (array[index] < array[index * 2 + 2]) {
                                  Util.swap(array, index, index * 2 + 2);
                              }
                          }
                      } else {
                          if (array[index] < array[index * 2 + 1]) {
                              Util.swap(array, index, index * 2 + 1);
                          }
                      }
                  }
                  
                  int innerIndex = index;
                  while (innerIndex != 0) {
                      if (array[innerIndex] > array[(innerIndex - 1) / 2]) {
                          Util.swap(array, innerIndex, (innerIndex - 1) / 2);
                          int temp = innerIndex;
                          while (temp * 2 + 1 < array.length) {
                              if (temp * 2 + 2 < array.length) {
                                  if (array[temp * 2 + 1] > array[temp * 2 + 2]) {
                                      if (array[temp] < array[temp * 2 + 1]) {
                                          Util.swap(array, temp, temp * 2 + 1);
                                          temp = temp * 2 + 1;
                                      } else {
                                          break;
                                      }
                                  } else {
                                      if (array[temp] < array[temp * 2 + 2]) {
                                          Util.swap(array, temp, temp * 2 + 2);
                                          temp = temp * 2 + 2;
                                      } else {
                                          break;
                                      }
                                  }
                              } else {
                                  if (array[temp] < array[temp * 2 + 1]) {
                                      Util.swap(array, temp, temp * 2 + 1);
                                  }
                                  break;
                              }
                          }
                          innerIndex = (innerIndex - 1) / 2;
                      } else {
                          break;
                      }
                  }
                  
                  index --;
              }
          }
          
          private static void adjuster(int[] array, int toIndex) {
              
              array[0] = array[toIndex];
              
              int index = 0;
              while (index * 2 + 1 < toIndex) {
                  if (index * 2 + 2 < toIndex) {
                      if (array[index * 2 + 1] > array[index * 2 + 2]) {
                          if (array[index] < array[index * 2 + 1]) {
                              Util.swap(array, index, index * 2 + 1);
                              index = index * 2 + 1;
                          } else {
                              break;
                          }
                      } else {
                          if (array[index] < array[index * 2 + 2]) {
                              Util.swap(array, index, index * 2 + 2);
                              index = index * 2 + 2;
                          } else {
                              break;
                          }
                      }
                  } else {
                      if (array[index] < array[index * 2 + 1]) {
                          Util.swap(array, index, index * 2 + 1);
                      }
                      break;
                  }
              }
          }
          
      }

  • 改进堆为类似二叉排序树的代码
    • public class MaxBinarySortHeap {
          
          private transient int size;
          
          private transient List<Integer> maxRootHeap;
          
          private transient Map<Integer, Integer> equivalientStatisticsMap;
          
          public MaxBinarySortHeap() {
              this.size = 0;
              this.maxRootHeap = new ArrayList<Integer>();
              this.equivalientStatisticsMap = new HashMap<Integer, Integer>();
          }
          
          public int size() {
              return size;
          }
          
          public int[] getHeapSequence() {
              int[] resultArray = new int[maxRootHeap.size()];
              int index = 0;
              for (Integer element : maxRootHeap) {
                  resultArray[index ++] = element;
              }
              return resultArray;
          }
          
          public void build(int[] array) {
              
              maxRootHeap.clear();
              equivalientStatisticsMap.clear();
              size = 0;
              
              for (int i = 0; i < array.length; i++) {
                  add(array[i]);
              }
              
          }
          
          public void add(int element) {
              
              size ++;
              
              if (!(maxRootHeap != null && maxRootHeap.contains(element))) {
                  maxRootHeap.add(element);
                  int index = maxRootHeap.size() - 1;
                  while (index > 0 && maxRootHeap.get(index) > maxRootHeap.get((index - 1) / 2)) {
                      Util.swap(maxRootHeap, index, (index - 1) / 2);
                      index = (index - 1) / 2;
                  }
              } else {
                  if (equivalientStatisticsMap.containsKey(element)) {
                      equivalientStatisticsMap.put(element, equivalientStatisticsMap.get(element) + 1);
                  } else {
                      equivalientStatisticsMap.put(element, 1);
                  }
              }
          }
          
          public boolean delete(int element) {
              if (!(maxRootHeap != null && maxRootHeap.contains(element))) {
                  return false;
              } else {
                  
                  size --;
                  
                  if (equivalientStatisticsMap.containsKey(element)) {
                      size -= equivalientStatisticsMap.get(element);
                      equivalientStatisticsMap.remove(element);
                  }
                  
                  int index = Util.getIndexByElement(maxRootHeap, element);
                  if (index == maxRootHeap.size() - 1) {
                      maxRootHeap.remove(index);
                      return true;
                  } else if (index * 2 + 1 >= maxRootHeap.size() && index * 2 + 2 >= maxRootHeap.size()) {
                      Util.swap(maxRootHeap, index, maxRootHeap.size() - 1);
                      maxRootHeap.remove(maxRootHeap.size() - 1);
                      innerMaxRootHeapAdjuster(maxRootHeap, index);
                      return true;
                  } else {
                      while (index * 2 + 1 < maxRootHeap.size()) {
                          if (index * 2 + 2 < maxRootHeap.size()) {
                              if (maxRootHeap.get(index * 2 + 1) > maxRootHeap.get(index * 2 + 2)) {
                                  Util.swap(maxRootHeap, index, index * 2 + 1);
                                  index = index * 2 + 1;
                              } else {
                                  Util.swap(maxRootHeap, index, index * 2 + 2);
                                  index = index * 2 + 2;
                              }
                          } else {
                              Util.swap(maxRootHeap, index, index * 2 + 1);
                              maxRootHeap.remove(index * 2 + 1);
                              return true;
                          }
                      }
                      Util.swap(maxRootHeap, index, maxRootHeap.size() - 1);
                      maxRootHeap.remove(maxRootHeap.size() - 1);
                      innerMaxRootHeapAdjuster(maxRootHeap, index);
                      return true;
                  }
              }
          }
          
          public int[] sort() {
              int[] resultArray = new int[size];
              if (maxRootHeap.size() <= 2) {
                  int index = 0;
                  for (Integer element : maxRootHeap) {
                      resultArray[index ++] = element;
                      if (equivalientStatisticsMap.containsKey(element)) {
                          int temp = equivalientStatisticsMap.get(element);
                          while (temp -- > 0) {
                              resultArray[index ++] = element;
                          }
                      }
                  }
                  
                  return resultArray;
              } else {
                  
                  List<Integer> tempList = new ArrayList<Integer>(maxRootHeap.size());
                  for (Integer element : maxRootHeap) {
                      tempList.add(element);
                  }
                  
                  int index = 0;
                  for (int i = 0; i < maxRootHeap.size(); i ++) {
                      maxRootHeapSortingAdjuster(maxRootHeap, i);
                      resultArray[index ++] = maxRootHeap.get(i);
                      if (equivalientStatisticsMap.containsKey(maxRootHeap.get(i))) {
                          int temp = equivalientStatisticsMap.get(maxRootHeap.get(i));
                          while (temp -- > 0) {
                              resultArray[index ++] = maxRootHeap.get(i);
                          }
                      }
                  }
                  
                  maxRootHeap = tempList;
                  
                  return resultArray;
              }
          }
          
          /*
           *  According to the given index of the element which destory the max root heap's structure,
           *  adjust the sequence to a max root heap.
           *  
           *  Note : the given index must be of an element which is a leaf node of the complete binary tree.
           */
          private void innerMaxRootHeapAdjuster(List<Integer> list, int index) {
              while (index != 0) {
                  if (list.get(index) > list.get((index - 1) / 2)) {
                      Util.swap(list, index, (index - 1) / 2);
                      index = (index - 1) / 2;
                  } else {
                      break;
                  }
              }
          }
          
          private void maxRootHeapSortingAdjuster(List<Integer> list, int fromIndex) {
              list.add(fromIndex, list.get(list.size() - 1));
              list.remove(list.size() - 1);
              int index = 0;
              while (index * 2 + 1 + fromIndex < list.size()) {
                  if (index * 2 + 2 + fromIndex < list.size()) {
                      if (list.get(index * 2 + 1 + fromIndex) > list.get(index * 2 + 2 + fromIndex)) {
                          if (list.get(index + fromIndex) < list.get(index * 2 + 1 + fromIndex)) {
                              Util.swap(list, index + fromIndex, index * 2 + 1 + fromIndex);
                              index = index * 2 + 1;
                          } else {
                              break;
                          }
                      } else {
                          if (list.get(index + fromIndex) < list.get(index * 2 + 2 + fromIndex)) {
                              Util.swap(list, index + fromIndex, index * 2 + 2 + fromIndex);
                              index = index * 2 + 2;
                          } else {
                              break;
                          }
                      }
                  } else {
                      if (list.get(index + fromIndex) < list.get(index * 2 + 1 + fromIndex)) {
                          Util.swap(list, index + fromIndex, index * 2 + 1 + fromIndex);
                          index = index * 2 + 1;
                      } else {
                          break;
                      }
                  }
              }
          }
      
      }

  • PS:
    • Code还是去年工作半年闲暇时码的,目的是为了弥补自己本科期间没有动手写这些基础code的缺憾
    • 总是工作时抽闲写一点,注释较少,望君手下留情
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值