常见排序列表
选择排序
最简单但是最没用的排序算法,也有优化空间
最简单:算法思想简单
最没用:时间复杂度高O(n²)、不稳定
基本思想:第i趟简单选择排序是指通过n-i次关键字的比较,从n-i+1个记录中选出关键字最小的记录,并与第i个记录进行交换。共需进行i-1趟比较,直到所有记录排序完成为止。
Java代码实现:
public class SelectionSort {
public static void main(String[] args) {
int[] arr = {5,3,6,8,4,7,2,1,9};
for(int i = 0; i<arr.length-1;i++) {
int minPos = i;
for(int j =i+1;j<arr.length;j++ ) {
/*
* if(arr[j] < arr[minPos]) minPos = j;
*/
//可优化为
minPos = arr[j] < arr[minPos] ? j : minPos;
}
swap(arr,i,minPos);
System.out.println();
System.out.println("经过第"+(i+1)+"次循环之后,数组的内容:");
print(arr);
}
}
//交换
static void swap(int[] arr,int i,int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
//打印数组
static void print(int[] arr) {
for(int i = 0; i<arr.length;i++) {
System.out.print(arr[i]+" ");
}
}
}
冒泡排序
冒泡排序算法的原理如下:
- 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
- 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
Java代码实现:
public class BubbleSort {
public static void main(String[] args) {
int[] a = {9,8,5,3,1,2,7,6,4};
sort(a);
print(a);
}
static void sort(int[] a) {
for(int i=a.length-1;i>0;i--)
findMax(a,i);
}
static void findMax(int[] a, int n) {
for(int j = 0; j < n; j++) {
if(a[j] > a[j+1]) swap(a,j,j+1);
}
}
static void swap(int[] a,int i,int j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
static void print(int[] a) {
for(int i = 0; i<a.length;i++) {
System.out.print(a[i]+" ");
}
}
}
插入排序
对于基本有序的数组最好用
稳定
基本思想:在一个已排好序的记录子集的基础上,每一步将下一个待排序的记录有序地插入到已排好序的记录子集中,直到将所有待排记录全部插入为止。
即边插入边排序,保证子序列中随时都是排好序的
Java代码实现:
public class InsertionSort {
public static void main(String[] args) {
int[] a = {8,9,5,3,1,2,7,6,4};
sort(a);
print(a);
}
static void sort(int[] a) {
for(int i=1;i<a.length;i++) {
for(int j = i;j>0 && a[j] < a[j-1] ;j--) {
//if(a[j] < a[j-1])
swap(a,j,j-1);
}
}
}
static void swap(int[] a,int i,int j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
static void print(int[] a) {
for(int i = 0; i<a.length;i++) {
System.out.print(a[i]+" ");
}
}
}
分析:
直接插入排序算法简便,比较适用于待排序记录数目较少且基本有序的情况。当待排记录数目较大时,直接插入排序的性能就不好,为此我们可以对直接插入排序做进一步的改进。在直接插入排序法的基础上,从减少“比较关键字”和“移动记录”两种操作的次数着手来进行改进。
简单排序算法总结:
冒泡:
基本不用,太慢
选择:
基本不用,不稳
插入:
样本小且基本有序的时候效率比较高
希尔排序
改进的插入排序
希尔排序的基本思想:先将待排序记录序列分割成若干个“较稀疏的”子序列,分别进行直接插入排序。经过上述粗略调整,整个序列中的记录已经基本有序,最后再对全部记录进行一次直接插入排序
间隔序列
比较相隔较远距离(称为增量)的数,使得数移动时能跨过多个元素,则进行一次比较就可能消除多个元素交换。
算法先将要排序的一组数按某个增量d分成若干组,每组中记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量对它进行分组,在每组中再进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成。
一般的初次取序列的一半为增量,以后每次减半,直到增量为1。
希尔排序间隔的选取会直接影响算法的效率
后来发现这种 /2 的间隔序列并不是最高的,还有别的间隔序列比这种还要高
Knuth序列
h=1
h=3*h+1
Java代码实现:
public class ShellSort {
public static void main(String[] args) {
int[] arr = {5,14,3,10,6,13,8,12,4,15,7,11,2,1,9};
sort(arr);
print(arr);
}
static void sort(int[] arr) {
//Knuth序列确定间隔序列
int h=1;
while(h<=arr.length/3) {
h=h*3+1;
}
for(int gap =h;gap>0;gap=(gap-1)/3) {
for(int i=gap;i<arr.length;i++) {
for(int j = i;j>gap-1 ;j-=gap) {
if(arr[j] < arr[j-gap])
swap(arr,j,j-gap);
}
}
}
}
static void swap(int[] arr,int i,int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
static void print(int[] arr) {
for(int i = 0; i<arr.length;i++) {
System.out.print(arr[i]+" ");
}
}
}
归并排序
基本思想:将两个或两个以上的有序表组合成一个新有序表
在内部排序中,通常采用的是2-路归并排序。即:将两个位置相邻的记录有序子序列
归并为一个记录的有序序列。
2-路归并排序排序过程
- 初始序列看成n个有序子序列,每个子序列长度为1
- 两两合并,得到 n/2 个长度为2或1的有序子序列
- 再两两合并,重复直至得到一个长度为n的有序序列为止
归并排序Java代码实现:
public class MergeSort {
public static void main(String[] args) {
int[] arr = {1,4,7,8,3,6,9};
sort(arr,0,arr.length-1);
print(arr);
}
static void sort(int[] arr, int left, int right) {
if(left == right) return;
//分为两半
int mid = left + (right - left)/2;
//左边排序
sort(arr,left,mid);
//右边排序
sort(arr,mid+1,right);
merge(arr,left,mid+1,right);
}
static void merge(int[] arr , int leftPtr, int rightPtr, int rightBound) {
int mid =rightPtr - 1;
int[] temp =new int[rightBound - leftPtr +1];
int i=leftPtr;
int j=rightPtr;
int k=0;
while(i <= mid && j<=rightBound) {
temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
/*
* if(arr[i] <= arr[j]) { temp[k++] = arr[i++]; } else { temp[k++] = arr[j++]; }
*/
}
while(i<=mid) temp[k++] = arr[i++];
while(j<=rightBound) temp[k++] = arr[j++];
//print(temp);
for(int m=0;m<temp.length;m++) arr[leftPtr +m]=temp[m];
}
static void swap(int[] arr,int i,int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
static void print(int[] arr) {
for(int i = 0; i<arr.length;i++) {
System.out.print(arr[i]+" ");
}
}
}
Java对象排序
对象排序一般要求稳定
快速排序
首先对无序的记录序列进行“一次划分”,之后分别对分割所得两个子序列“递归”进行快速排序。
动画演示:
Java代码实现:
public class QuickSort {
public static void main(String[] args) {
int[] arr = {7,3,2,8,1,9,5,4,6,10};
sort(arr,0,arr.length-1);
print(arr);
}
static void sort(int[] arr,int leftBound,int rightBound) {
if(leftBound >= rightBound) return;
int mid = partition(arr,leftBound,rightBound);
sort(arr,leftBound,mid-1);
sort(arr,mid+1,rightBound);
}
static int partition(int[] arr,int leftBound,int rightBound) {
int pivot = arr[rightBound];
int left = leftBound;
int right = rightBound-1;
while(left<=right) {
while(left <= right && arr[left]<= pivot) left++;
while(left <= right && arr[right]>pivot) right--;
if(left < right) swap(arr,left,right);
}
swap(arr,left,rightBound);
return left;
}
static void swap(int[] arr,int i,int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
static void print(int[] arr) {
for(int i = 0; i<arr.length;i++) {
System.out.print(arr[i]+" ");
}
}
}
计数排序
非比较排序
桶思想的一种
算法思想:
计数排序的基本思想是对于给定的输入序列中的每一个元素x,确定该序列中值等于x的元素的个数(此处并非比较各元素的大小,而是通过对元素值的计数和计数值的累加来确定)。将元素相同的个数存储在对应的数组(类似于桶)中,最后按顺序输出即可。
适用于特定的问题,也就是对源数据有要求
量大但是范围小
如:某大型企业数万名员工年龄排序
如何快速得知高考名次(腾讯面试)
public class CountSort {
public static void main(String[] args) {
int[] arr = {2,4,2,3,7,1,1,2,2,5,6,9,8,5,7,4,8,9,0};
int[] result = sort(arr);
System.out.println(Arrays.toString(result));
}
static int[] sort(int[] arr) {
int[] result = new int[arr.length];
int[] count = new int[10];
for(int i=0; i<arr.length;i++) {
count[arr[i]]++;
}
System.out.println(Arrays.toString(count));
//不稳定
//for(int i=0,j=0; i<count.length;i++) {
// while(count[i]-- >0) result[j++] = i;
//}
//累加数组
for(int i=1; i<count.length;i++) {
count[i] = count[i]+count[i-1];
}
System.out.println(Arrays.toString(count));
for(int i=arr.length-1;i>=0;i--) {
result[--count[arr[i]]] = arr[i];
}
return result;
}
}
基数排序
非比较排序
桶思想的一种
多关键字排序
基本思想: 基数排序是一种借助“多关键字排序”的思想来实现
多关键字排序
最高位优先MSD
先对最高位关键字排序,将序列分成若干子序列,每个子序列有相同的值;
然后让每个子序列对次关键字排序,又分成若干更小的子序列;
依次重复,直至就每个子序列对最低位关键字排序,就可以得到一个有序的序列。
最低位优先LSD
首先依据最低位排序码对所有对象进行一趟排序。
再依据次低位排序码对上一趟排序结果排序。
依次重复,直到依据排序码最后一趟排序完成,就可以得到一个有序的序列。
Java代码实现:
public class RadixSort {
//最低位优先实现
public static void main(String[] args) {
int[] arr = {421,24,1105,534,305,430,126};
//查找数组中最大值
int max = findMax(arr);
//得到最大值的位数
int digit = findDigit(max);
int[] result = sort(arr, digit);
System.out.println(Arrays.toString(result));
}
static int[] sort(int[] arr,int digit) {
int[] result = new int[arr.length];
int[] count = new int[10];
for(int i=0; i<digit;i++) {
int division = (int)Math.pow(10, i);
//System.out.println(division);
for(int j=0; j<arr.length;j++) {
int num = arr[j]/division %10;
//System.out.println(num+" ");
count[num]++;
}
//System.out.println();
//System.out.println(Arrays.toString(count));
for(int m=1; m<count.length;m++) {
count[m] = count[m]+count[m-1];
}
//System.out.println(Arrays.toString(count));
for(int n=arr.length-1;n>=0;n--) {
int num = arr[n]/division %10;
result[--count[num]] = arr[n];
}
System.arraycopy(result,0,arr,0,arr.length);
Arrays.fill(count,0);
}
return result;
}
//找到最大值
static int findMax(int[] arr) {
int max = arr[0];
for(int i = 1; i < arr.length; i++) {
max= arr[i] > max ? arr[i] :max;
}
return max;
}
static int findDigit(int digit) {
int count=0;
while(digit>0)
{
digit/=10;
count++;
}
return count;
}
}