vlambda博客
学习文章列表

8大常见排序算法之选择排序!

选择排序

简单选择排序

原理

工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

Java代码

public class Sort {
   public static void main(String[] args) {
       int[] result = {2,4,1,3,6,5};
       int temp;
       System.out.println("----简单选择排序前顺序----");
       for (int i : result) {
           System.out.print(i);
      }
       //简单选择排序
       int len = result.length;
       for(int i=0; i<len; i++){
           //记录当前位置
           int position = i;
           //找出最小的数,并用position指向最小数的位置
           for(int j=i+1; j<len; j++){
               if(result[j]<result[position]) {
                   position=j;
              }//endif
          }//endfor
           //交换最小数data[position]和第i位数的位置
            temp = result[position];
           result[position] = result[i];
           result[i] = temp;
      }//endfor
       System.out.println();
       System.out.println("----简答选择排序后结果----");
       for (int i : result) {
           System.out.print(i);
      }
  }
}

复杂度

最好情况下,即待排序记录初始状态就已经是升序排列了,则不需要移动记录。

最坏情况下,即待排序记录初始状态是按第一条记录最大,之后的记录从大到小顺序排列,则需要移动记录的次数最多为3(n-1)。简单选择排序过程中需要进行的比较次数与初始状态下待排序记录序列的排列情况无关。当i=1时,需进行n-1次比较;当i=2时,需进行n-2次比较;依次类推,共需要进行的比较次数是(n-1)+(n-2)+…+2+1=n(n-1)/2,即进行比较操作的时间复杂度为O(n2),进行移动操作的时间复杂度为O(n2)。

算法稳定性

由于会改变相对位置,所以简单选择排序是不稳定排序。


堆排序

原理

是一棵顺序存储完全二叉树

  • 其中每个结点的关键字都不大于其孩子结点的关键字,这样的堆称为小根堆

  • 其中每个结点的关键字都不小于其孩子结点的关键字,这样的堆称为大**根堆**。

举例来说,对于n个元素的序列{R0, R1, ... , Rn}当且仅当满足下列关系之一时,称之为堆:

  • Ri <= R2i+1 且 Ri <= R2i+2 (小根堆)

  • Ri >= R2i+1 且 Ri >= R2i+2 (大根堆)

其中i=1,2,…,n/2向下取整;

如上图所示,序列R{3, 8, 15, 31, 25}是一个典型的小根堆,元素3在数组中以R[0]表示,它的左孩子结点是R[1],右孩子结点是R[2]。

Java代码

import java.util.ArrayList;

public class tets {
   public static void main(String[] args) {
       int[] result = {2,4,1,3,6,5};
       int temp;
       System.out.println("----快速排序前顺序----");
       for (int i : result) {
           System.out.print(i);
      }
       heapSort(result);

       System.out.println();
       System.out.println("----快速排序后结果----");
       for (int i : result) {
           System.out.print(i);
      }
  }
   public static int[] heapSort(int[] array) {
       //这里元素的索引是从0开始的,所以最后一个非叶子结点array.length/2 - 1
       for (int i = array.length / 2 - 1; i >= 0; i--) {
           adjustHeap(array, i, array.length);  //调整堆
      }

       // 上述逻辑,建堆结束
       // 下面,开始排序逻辑
       for (int j = array.length - 1; j > 0; j--) {
           // 元素交换,作用是去掉大顶堆
           // 把大顶堆的根元素,放到数组的最后;换句话说,就是每一次的堆调整之后,都会有一个元素到达自己的最终位置
           swap(array, 0, j);
           // 元素交换之后,毫无疑问,最后一个元素无需再考虑排序问题了。
           // 接下来我们需要排序的,就是已经去掉了部分元素的堆了,这也是为什么此方法放在循环里的原因
           // 而这里,实质上是自上而下,自左向右进行调整的
           adjustHeap(array, 0, j);
      }
       return array;
  }

   /**
    * 整个堆排序最关键的地方
    * @param array 待组堆
    * @param i 起始结点
    * @param length 堆的长度
    */
   //核心代码!!!
   public static void adjustHeap(int[] array, int i, int length) {
       // 先把当前元素取出来,因为当前元素可能要一直移动
       int temp = array[i];
       for (int k = 2 * i + 1; k < length; k = 2 * i + 1) {  //2*i+1为左子树i的左子树(因为i是从0开始的),2*k+1为k的左子树
           // 让k先指向子节点中最大的节点
           if (k + 1 < length && array[k] < array[k + 1]) {  //如果有右子树,并且右子树大于左子树
               k++;
          }
           //如果发现结点(左右子结点)大于根结点,则进行值的交换
           if (array[k] > temp) {
               swap(array, i, k);
               // 如果子节点更换了,那么,以子节点为根的子树会受到影响,所以,循环对子节点所在的树继续进行判断
               i  =  k;
          } else {  //不用交换,直接终止循环
               break;
          }
      }
  }
   /**
    * 交换元素
    * @param arr
    * @param a 元素的下标
    * @param b 元素的下标
    */
   public static void swap(int[] arr, int a, int b) {
       int temp = arr[a];
       arr[a] = arr[b];
       arr[b] = temp;
  }
}