此文章主要用于回顾常见的几种排序算法,对算法的原理并没有很详细的说明。
冒泡排序
原理:每次两两进行比较交换次序,以便每次将最大值将“沉”到已排好序的最后一个位置。
//冒泡排序
public class BubbleSort {
public static int[] sort(int[] arr){
int len = arr.length;
for (int i = 0; i < len; i++) { //次数
for (int j = 0; j < len-i-1; j++) { //这里注意还要减一,避免越界
if(arr[j] > arr[j+1]){
swap(arr,j,j+1); //交换函数
}
}
}
return arr;
}
}
插入排序
原理:减治法。每次将当前数据插入到前面已排好序的数列中。插入的时候从后往前遍历找到合适位置。
//插入排序,减治法
public class InsertSort {
public static int[] sort(int[] arr){
int len = arr.length;
for (int i = 1; i < len; i++) { //注意这里从1开始
for(int j = i;j > 0 && arr[j] < arr[j-1];j--){
swap(arr,j,j-1);
}
}
return arr;
}
}
tips:插入时,可以二分查找插入,提高效率。
选择排序
原理:第i次排序从第i-1个元素到最后一个元素中找出最小的元素,放到第i个位置上。
//选择排序
public class SelectSort {
public static int[] sort(int[] arr){
int len = arr.length;
for (int i = 0; i < len-1; i++) {
int min = i;;
for (int j = i+1; j < len; j++) {
if(arr[j] < arr[min]){
min = j;
}
}
swap(arr,i,min);
}
return arr;
}
}
归并排序
原理:分治法。相当于将数组分成两份,将左右两边分别排好序之后再归并起来。用递归的方法实现。
//归并排序,递归,分治法
public class MergeSort {
public static int[] sort(int[] arr){
mergeSort(arr,0,arr.length-1);
return arr;
}
public static void mergeSort(int[] arr,int start,int end){
if(end - start == 1){
if(arr[start] > arr[end]){
swap(arr,start,end);
}
return;
}
if(end == start) return;
int center = (start+end)/2;
mergeSort(arr,start,center);
mergeSort(arr,center+1,end);
merge(arr,start,center,end);
}
//归并,相当于归并两个数组
public static void merge(int[] arr,int start,int center,int end){
int[] newArr = new int[end-start+1];
int i = start;
int j = center+1;
int k = 0;
while(i<=center && j<=end){
if(arr[i] < arr[j]){
newArr[k++] = arr[i++];
}else{
newArr[k++] = arr[j++];
}
}
if(i<=center){
for(;i<=center;i++)
newArr[k++] = arr[i];
}
if(j<=end){
for(;j<=end;j++)
newArr[k++] = arr[j];
}
k = 0;
for (int l = start; l <= end; l++) {
arr[l] = newArr[k++];
}
}
}
快排算法
原理:分治法。每次选出一个基准数据(一般认为是第一个数据)。将基准插入到合适位置(这个位置左边的数都小于基准,右边的数都大于基准)。递归实现。
//快排,递归,分治
public class QuickSort {
public static int[] sort(int[] arr){
quickSort(arr,0,arr.length-1);
return arr;
}
public static void quickSort(int[] arr,int start,int end){
if(start >= end) return;
if(end - start == 1){ //为了优化,可以不要这个判断
if(arr[end] < arr[start]){
swap(arr,end,start);
}
return;
}
int center = findPosition(arr,start,end);
quickSort(arr,start,center-1);
quickSort(arr,center+1,end);
}
public static int findPosition(int[] arr,int start,int end){
int centerValue = arr[start];
//找出center所在位置
int i = start,j = end+1; //为了++i和--j
while(true){
while(arr[++i] <= centerValue) {if(i == end) break;}
while(arr[--j] >= centerValue) {if(j == start) break;}
if(i>=j) break;
swap(arr,i,j);
}
swap(arr,start,j);
return j;
}
}
堆排序
原理:生成一个大顶堆(或小顶堆),每次将堆顶与最后一个数据交换位置,取出堆顶,剩余的数据重新调整堆结构。这里调整堆结构不太好理解。
//堆排序
public class HeapSort {
public static int[] sort(int[] arr){ //用数组来表示二叉树树结构
int[] arr2 = new int[arr.length+1];
for (int i = 1; i < arr.length+1; i++) { //向后移动一位,形成二叉树的位置关系,left = 2*root
arr2[i] = arr[i-1];
}
int len = arr2.length;
while(len != 1){
arr2 = shiftHeapByInsert(arr2,len); //对数组[1,len]的位置形成大顶堆
swap(arr2,1,len-1); //将大顶堆的堆顶放入尾部(并非最后)
len--;
}
for (int i = 0; i < arr2.length-1; i++) {
arr[i] = arr2[i+1];
}
return arr;
}
public static int[] shiftHeapByInsert(int[] arr,int len){
int[] arr2 = arr;
for (int i = 2; i < len; i++) {
int k = i; //代表插入arr中的位置
arr2[k] = arr[i];
while(k != 1){
int j = k/2;
if(arr2[k] > arr2[j]){
swap(arr2,k,j);
k = j;
}else{
break;
}
}
}
return arr2;
}
}
希尔排序
原理:将小于n的整数d1作为第一个距离增量,第一次让所有距离为d1的数据排序。第二次让所有为d2的数据排序(d2<d1),,,直到最后一次让所有距离为1的数据排序,也就是所有数据排序。这里的距离增量可以人为设置。其中每次让所有距离为d的数据排序的算法为直接插入排序。
//希尔排序
public class ShellSort {
public static int[] sort(int[] arr){
int h=1; //增量
int length = arr.length;
while (h < length/3)
h = 3*h + 1;
while(h >= 1){
for (int i = h; i <length ; i++) {
for(int j = i; j >= h && arr[j]<arr[j-h] ; j -= h )
swap(arr, j, j-h);
}
h = h/3;
}
return arr;
}
}
参考书籍:《算法 第4版》,《算法分析与设计 第2版》