一,选择排序
选择排序算法有两种:直接选择排序和堆排序.
二,直接选择排序
1.基本思想:第一趟从n个元素的数据序列中选出关键字最大(或最小)的元素并放在最后(或最前)位置,下一趟再从n-1个元素中选择出最大(小)的元素并放在次后(前)位置.以此类推,经过n-1趟完成排序.直接选择算法可用顺序表和单/双链表实现.
2.代码
//以下是书本的直接选择排序 public static void straightSelectSortBook(int [] table){ for(int i =0; i < table.length-1 ; i++){ int min = i; for(int j = i+1; j< table.length ; j++){ if(table[j]<table[min]){ min = j; } } if(min!= i){ int temp = table[i]; table[i] = table[min]; table[min] = temp; } } }
从上面的程序和以前算法的代码可以看到,有时候分组和比较,一旦涉及大规模的数据,拿出空间存放分组的的数据,或者存放用于比较的数据这种做法是不理智,第一,程序会变得很冗余复杂难懂.第二,浪费大量的空间,增加数据移动的次数.解决上面的问题的一个方法,就是利用元素的下标,可以利用下标分组,比较的时候可以单单存放下标这个值就可以啦.
2.算法排序分析
直接选择排序的比较次数与数据序列的初始排序无关.第i趟排序的比较次数是n-i,总比较次数为1+2+3+...+n-1=n(n-1)/2,时间复杂度为O(n^2).
移动次数与数据序列的初始排序有关.当数据序列已排序时,移动次数M=0;当数据序列反序是,每一趟序列都要交换,移动次数M=3(n-1).因此,直接选择排序的时间复杂度为O(n^2).
直接选择排序的空间复杂度为O(1).直接选择排序算法是不稳定的.
三.堆排序
1.介绍
堆排序(heap sort)是完全二叉树的应用,是充分利用完全二叉树特性的一种选择排序.
完全二叉树和满二叉树的区别是: 满二叉树为长度为k,节点个数为2^k-1,而完全二叉树则是一颗深度为k的二叉树,有n个节点,对这颗二叉树进行编号,如果所有的编号和二叉树一一对应,那么这颗二叉树就是完全二叉树.在二叉树的性质中,完全二叉树中第i(0<=i<n)个节点,如果有孩子,则左孩子为第2i+1个节点,右孩子为第2i+2个节点.
在堆排序中有最大堆二叉树和最小堆二叉树.其中最大堆中,每一个节点,如果有子节点的话,左右子节点的值一定是比父节点的小.同理,最小堆中,每一个节点,如果有子节点的话,左右子节点的值一定是比父节点的大.
2.由来介绍
在直接选择排序中,每趟从数据序列中选出一个最小值交换到最前面,其余n-1个元素原地不动,下一趟需要再次比较这些元素,因此直接选择排序算法中的复杂比较很多.如果每趟能够利用前一趟的比较结果,则会减少一些重复比较,从而提交算法效率.
如果先把一组数据序列先构造成最大堆二叉树的序列或者是最小堆的序列,由于完全二叉树的性质和最大(小)堆的性质,父节点的值一定是比子节点大或者小.所以在最大堆二叉树中只要比较了父节点,只要满足条件,子节点是不用比较的,简便了时间复杂度.
3.代码实现分析
要实现这个算法,有两步,一个是构造最小堆(最大堆)的算法,另外一个是让最小堆(最大堆)的序列中的数组进行比较,再进行位置调整.不断循环上面两个步骤就可以啦.
3.1 构造最大堆和最小堆的方法
public static void sift(int[] table,int begin, int end){ int i = begin , j = 2*i +1; //i为子树的根,j为i节点的左孩子 int temp = table[i]; //获取table为i节点的值 while(j<=end){//向下筛选 if(j<end && table[j] > table[j+1]){ //找出左右孩子的较小者 j++; //构造最小堆二叉树 } if(temp >table[j]){ //如果父节点比较小子节点大,把父节点附上子节点的值 table[i] = table[j];//子节点的值赋值到父节点 i=j; //同时i和j向下移动一层 j=2*i + 1; }else break; //如果父节点比较子节点小,break跳出此循环 } table[i] = temp; //将最后比较完成的结果,把temp赋给最后的那个值 }
3.2堆排序
public static void heapSort (int [] table){ int n = table.length; for(int j = n/2 -1 ;j>= 0; j--){//从倒数第二层开始比较然后排序,一直到第一个位置 sift(table,j,n-1); } for(int j = n-1 ; j >0 ;j--){ //从最末尾的位置与第一个元素互换位置,再再一次sift int temp = table[0]; table[0] = table[j]; table[j] = temp ; sift(table,0,j-1); } }
4.算法分析
将一个序列调整为准的时间复杂度为O(log2n),因此堆排序的时间复杂度为O(nlog2n).堆排序的空间复杂度为O(1).堆排序算法不稳定的.