1、选择排序 <br> 最差时间复杂度О(n²) 最优时间复杂度О(n²) 平均时间复杂度О(n²) 最差空间复杂度О(n) total, O(1) auxiliary 选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。 选择排序的主要优点与数据移动有关。如果某个元素位于正确的最终位置上,则它不会被移动。选择排序每次交换一对元素,它们当中至少有一个将被移到其最终位置上,因此对n个元素的表进行排序总共进行至多n-1次交换。在所有的完全依靠交换去移动元素的排序方法中,选择排序属于非常好的一种。 JAVA语言实现 public void selectSort(Comparable[] array) { System.out.println("===========Insert Sort Started==========="); Comparable temp; int min; for (int index = 0; index < array.length; index++) { //假定第一个元素为最小元素 min = index; //循环遍历元素,每遍历一个元素,与当前最小元素比较,若此元素比当前最小元素小,则将此元素置为最小元素 for (int time = index + 1; time < array.length; time++) { if (array[time].compareTo(array[min]) < 0) { min = time; } } //遍历一遍,找到一个最小元素,把此最小元素放在数组的第一个位置 if (min != index) { temp = array[index]; array[index] = array[min]; array[min] = temp; } } System.out.println("The array after sorted...."); System.out.println(Arrays.toString(array)); System.out.println("============Insert Sort Ended============"); } 2、插入排序 最差时间复杂度O(n^2) 最优时间复杂度O(n) 平均时间复杂度O(n^2) 最差空间复杂度总共O(n) ,需要辅助空O(1) 一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下: 1.从第一个元素开始,该元素可以认为已经被排序 2.取出下一个元素,在已经排序的元素序列中从后向前扫描 3.如果该元素(已排序)大于新元素,将该元素移到下一位置 4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置 5.将新元素插入到该位置后 6.重复步骤2~5 JAVA语言实现 public class Insertion {
public static void insertionSort(Comparable []data){
for(int index=1;index<data.length;index++){
Comparable key = data[index];
int position = index;
//shift larger values to the right
while(position>0&&data[position-1].compareTo(key)>0){
data[position] = data[position-1];
position--;
}
data[position]=key;
}
}
public static void main(String []args){
Comparable []c={4,9,23,1,45,27,5,2};
insertionSort(c);
for(int i=0;i<c.length;i++)
System.out.println("插入排序:"+c[i]);
}
} 3、希尔排序 最差时间复杂度根据步长序列的不同而不同。 已知最好的: O(n\log^2 n) 最优时间复杂度O(n) 平均时间复杂度根据步长序列的不同而不同。 最差空间复杂度O(n ) 原始的算法实现在最坏的情况下需要进行O(n2)的比较和交换。V. Pratt的书[1] 对算法进行了少量修改,可以使得性能提升至O(n log2 n)。这比最好的比较算法的O(n log n)要差一些。 希尔排序通过将比较的全部元素分为几个区域来提升插入排序的性能。这样可以让一个元素可以一次性地朝最终位置前进一大步。然后算法再取越来越小的步长进行排序,算法的最后一步就是普通的插入排序,但是到了这步,需排序的数据几乎是已排好的了(此时插入排序较快)。 JAVA语言实现 static <E extends Comparable<? super E>> void shellSort(List<E> a) { int h = 1; while (h < a.size()/3) h = h_3 + 1; // <O(n^(3/2)) by Knuth,1973>: 1, 4, 13, 40, 121, ... for (; h >= 1; h /= 3) for (int i = h; i < a.size(); i++) for (int j = i; j >= h && a.get(j).compareTo(a.get(j-h)) < 0; j-=h) Collections.swap(a, j, j-h); } 4、归并排序 最差时间复杂度Theta(n_log n) 最优时间复杂度Theta(n) 平均时间复杂度Theta(n*log n) 最差空间复杂度Theta(n) 归并操作的过程如下: 1.申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列 2.设定两个指针,最初位置分别为两个已经排序序列的起始位置 3.比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置 4.重复步骤3直到某一指针达到序列尾 5.将另一序列剩下的所有元素直接复制到合并序列尾 public int[] Two_Way_Merge_Sort(int[] A, int[] B) { int[] C = new int[A.length + B.length]; int k = 0; int i = 0; int j = 0; while(i < A.length && j < B.length) { if (A[i] < B[j]) C[k++] = A[i++]; else C[k++] = B[j++]; } while (i < A.length) C[k++] = A[i++]; while (j < B.length) C[k++] = B[j++]; return C; }
/**
- one good implements. <br />
*/ public class Sort {
public static final int CUTOFF = 11;
/**
* merge sort algorithm.
*
* @param arr an array of Comparable item.
* 1.here only use one temp array (think about it). <br />
* 2.copy the element back after the sub merge operation. @see merge(T, T, int, int);
* the above two points make it more efficient. <br />
*/
@SuppressWarnings("unchecked")
public static <T extends Comparable<? super T>> void mergeSort( T[] arr ) {
//you may use insertionSort instead when the arr.length is not that large.
/*if ( arr.length < CUTOFF ) {
insertionSort( arr );
return;
}*/
T[] tmpArr = (T[]) new Comparable[arr.length];
mergeSort(arr, tmpArr, 0, arr.length - 1);
}
/**
* internal method to make a recursive call to merge. <br />
*
* @param arr an array of Comparable items. <br />
* @param tmpArr temp array to placed the merged result. <br />
* @param left left-most index of the subarray. <br />
* @param right right-most index of the subarray. <br />
*/
private static <T extends Comparable<? super T>>
void mergeSort( T[] arr, T[] tmpArr,
int left, int right ) {
//recursive way
if ( left < right ) {
int center = ( left + right ) / 2;
mergeSort(arr, tmpArr, left, center);
mergeSort(arr, tmpArr, center + 1, right);
merge(arr, tmpArr, left, center + 1, right);
}
//loop instead, not working, do it youself.
/*
int n = 0, j;
while ( true ) {
int step = ( int ) Math.pow(2, ++n);
int len = step / 2;
int count = arr.length / step;
int rpos;
//previous pow(2, k) elements
for ( j = 0; j < count; j++ ) {
rpos = j + len;
System.out.println(j+", "+rpos);
merge( arr, tmpArr, j, rpos, rpos + len - 1);
}
//the rest elements
//for () ;
if ( step * 2 >= arr.length ) break;
}
*/
}
/**
* internal method to merge the sorted halves of a subarray. <br />
*
* @param arr an array of Comparable items. <br />
* @param tmpArr temp array to placed the merged result. <br />
* @param leftPos left-most index of the subarray. <br />
* @param rightPos right start index of the subarray. <br />
* @param endPos right-most index of the subarray. <br />
*/
private static <T extends Comparable<? super T>> void merge( T[] arr, T[] tmpArr,
int lPos, int rPos, int rEnd ) {
int lEnd = rPos - 1;
int tPos = lPos;
int leftTmp = lPos;
while ( lPos <= lEnd && rPos <= rEnd ) {
if ( arr[lPos].compareTo( arr[rPos] ) <= 0 )
tmpArr[ tPos++ ] = arr[ lPos++ ];
else
tmpArr[ tPos++ ] = arr[ rPos++ ];
}
//copy the rest element of the left half subarray.
while ( lPos <= lEnd )
tmpArr[ tPos++ ] = arr[ lPos++ ];
//copy the rest elements of the right half subarray. (only one loop will be execute)
while ( rPos <= rEnd )
tmpArr[ tPos++ ] = arr[ rPos++ ];
//copy the tmpArr back cause we need to change the arr array items.
for ( ; rEnd >= leftTmp; rEnd-- )
arr[rEnd] = tmpArr[rEnd];
}
} 5、堆排序 最差时间复杂度O(nlogn) 最优时间复杂度O(nlogn)[1] 平均时间复杂度Theta(nlog n) 最差空间复杂度O(n) total, O(1) auxiliary 堆排序(Heapsort)是指利用堆这种资料结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。 JAVA语言实现 public class HeapSort { private static int[] sort = new int[]{1,0,10,20,3,5,6,4,9,8,12,17,34,11}; public static void main(String[] args) { buildMaxHeapify(sort); heapSort(sort); print(sort); }
private static void buildMaxHeapify(int[] data){
//没有子节点的才需要创建最大堆,从最后一个的父节点开始
int startIndex = getParentIndex(data.length - 1);
//从尾端开始创建最大堆,每次都是正确的堆
for (int i = startIndex; i >= 0; i--) {
maxHeapify(data, data.length, i);
}
}
/**
* 创建最大堆
* @param data
* @param heapSize 需要创建最大堆的大小,一般在sort的时候用到,因为最多值放在末尾,末尾就不再归入最大堆了
* @param index 当前需要创建最大堆的位置
*/
private static void maxHeapify(int[] data, int heapSize, int index){
// 当前点与左右子节点比较
int left = getChildLeftIndex(index);
int right = getChildRightIndex(index);
int largest = index;
if (left < heapSize && data[index] < data[left]) {
largest = left;
}
if (right < heapSize && data[largest] < data[right]) {
largest = right;
}
//得到最大值后可能需要交换,如果交换了,其子节点可能就不是最大堆了,需要重新调整
if (largest != index) {
int temp = data[index];
data[index] = data[largest];
data[largest] = temp;
maxHeapify(data, heapSize, largest);
}
}
/**
* 排序,最大值放在末尾,data虽然是最大堆,在排序后就成了递增的
* @param data
*/
private static void heapSort(int[] data){
//末尾与头交换,交换后调整最大堆
for (int i = data.length - 1; i > 0; i--) {
int temp = data[0];
data[0] = data[i];
data[i] = temp;
maxHeapify(data, i, 0);
}
}
/**
* 父节点位置
* @param current
* @return
*/
private static int getParentIndex(int current){
return (current - 1) >> 1;
}
/**
* 左子节点position 注意括号,加法优先级更高
* @param current
* @return
*/
private static int getChildLeftIndex(int current){
return (current << 1) + 1;
}
/**
* 右子节点position
* @param current
* @return
*/
private static int getChildRightIndex(int current){
return (current << 1) + 2;
}
private static void print(int[] data){
int pre = -2;
for (int i = 0; i < data.length; i++) {
if (pre < (int)getLog(i+1)) {
pre = (int)getLog(i+1);
System.out.println();
}
System.out.print(data[i] + " |");
}
}
/**
* 以2为底的对数
* @param param
* @return
*/
private static double getLog(double param){
return Math.log(param)/Math.log(2);
}
} 6、基数排序(Radix sort) 最差时间复杂度O(kN) 最差空间复杂度O(kN) 将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。基数排序的方式可以采用LSD(Least significant digital)或MSD(Most significant digital),LSD的排序方式由键值的最右边开始,而MSD则相反,由键值的最左边开始。 基数排序JAVA实现 public static void radixSort(int[] array){
//首先确定排序的趟数;
int max=array[0];
for(int i=1;i<array.length;i++){
if(array[i]>max){
max=array[i];
}
}
int time=0;
//判断位数;
while(max>0){
max/=10;
time++;
} //建立10个队列;
LinkQueue<Integer>[] queue=new LinkQueue[10];
for(int i=0;i<10;i++){
queue[i]=new LinkQueue<Integer>();
}
//进行time次分配和收集;
for(int i=0;i<time;i++){
//分配数组元素;
for(int j=0;j<array.length;j++){
//得到数字的第time+1位数;
queue[array[j]%(int)Math.pow(10, i+1)/(int)Math.pow(10, i)].enQueue(array[j]);
}
int count=0;//元素计数器;
//收集队列元素;
for(int k=0;k<10;k++){
while(queue[k].size()>0){
array[count]=(Integer) queue[k].deQueue().getElement();
count++;
}
}
}
}