排序算法总结
排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。
当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序。
快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短, 因此快速排序通常是首选的排序。
快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短, 因此快速排序通常是首选的排序。
1、插入排序
先将数组的第1个元素看成是一个有序的子数组,然后从第2个元素开始逐个进行插入,直至整个数组有序为止。
时间复杂度O(n^2)。
public static void insertSort(int[] arr) {
if(arr==null || arr.length==0)
return;
for(int i=1;i<arr.length;i++){
int j=i;
int target=arr[i];
while(j>0 && target<arr[j-1]){
arr[j]=arr[j-1];
j--;
}
arr[j]=target;
}
}
2、希尔排序
希尔排序(Shell Sort)是插入排序的一种,其基本思想是:把数组按下标的一定增量分组,对每组进行插入排序;随着增量逐渐缩小一半,数组逐渐接近有序,当增量减至1时,整个数组有序了,算法便终止。
public class Shell_sort {
public static void shellSort(int[] date){
int n=date.length;
for(int i=n/2;i>0;i=i/2){
for(int j=0;j<i;j++){
incrementalInsertionSort(date,j,n-1,i);
}
}
}
private static void incrementalInsertionSort(int[] a,int first,int last,int space){
int unsorted,index;
for(unsorted=first+space;unsorted<=last;unsorted=unsorted+space){
int firstUnsorted=a[unsorted];
for(index=unsorted-space;(index>=first)&&(firstUnsorted<a[index]);index=index-space)
a[index+space]=a[index];
a[index+space]=firstUnsorted;
}
}
}
3、选择排序
从数组中选出最小的值放在第1位置,然后在剩下的数中选出最小的值放在第2位置,以此类推,就会的到有序的数组。
public class SelectSort {
public static void selectSort(int[] arr) {
int minIndex = 0;
for(int i=0; i<arr.length-1; i++) {
minIndex = i;
for(int j=i+1; j<arr.length; j++) {
if(arr[j] < arr[minIndex])
minIndex = j;
}
if(minIndex!=i)
swap(arr, i, minIndex);
}
}
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
4、堆排序
基本思想:
- 先将待排序数组建成一个最大堆,此堆为初始的无序区
- 将堆顶元素与无序区最后一个元素交换,由此得到新的无序区和有序区
- 重复以上两个过程,直到无序区只有一个元素为止
下面是一个堆排序的例子:
5、冒泡排序
自上而下地不断比较两个相邻的元素,把较大的数往下沉,把较小的数往上冒,最后得到有序的数组。
public class BubbleSort {
public static void bubbleSort(int[] arr){
if(arr == null || arr.length == 0)
return ;
for(int i=0;i<arr.length-1;i++){
for(int j=arr.length-1;j>i;j--){
if(arr[j]<arr[j-1])
swap(arr,j-1,j);
}
}
}
public static void swap(int[] arr,int i,int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
6、快速排序(分治法)
基本思想:
1、先从数组中取出一个数作为基准数
2、进行分区,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边
3、再对左右区间重复第二步,直到各区间只有一个数。
对挖坑填数进行总结·
1.i =L; j = R; 将基准数挖出形成第一个坑a[i]。
2.j--由后向前找比它小的数,找到后挖出此数填前一个坑a[i]中。
3.i++由前向后找比它大的数,找到后也挖出此数填到前一个坑a[j]中。
4.再重复执行2,3二步,直到i==j,将基准数填入a[i]中。
2.j--由后向前找比它小的数,找到后挖出此数填前一个坑a[i]中。
3.i++由前向后找比它大的数,找到后也挖出此数填到前一个坑a[j]中。
4.再重复执行2,3二步,直到i==j,将基准数填入a[i]中。
public static void quickSort(int[] arr, int l, int r){
if (l<r){
int i=l, j=r, x=arr[l];
while(i<j){
while(i<j && arr[j]>=x) // 从右向左找第一个小于x的数
j--;
if(i<j)
arr[i++]=arr[j];
while(i<j && arr[i]<x) // 从左向右找第一个大于等于x的数
i++;
if(i<j)
arr[j--]=arr[i];
}
arr[i]=x;
quickSort(arr,l,i-1); // 递归调用
quickSort(arr,i+1,r);
}
}
7、归并排序(分治法)
归并排序将一个数组分为两个子数组,然后递归地将每个子数组再分成两个子数组,直到每个子数组只含有一个元素为止。从这里开始进入归并状态,两个含一个元素的子数组归并为一个含两个元素的子数组,以此类推,最后得到一个有序的数组。
归并的过程——将两个有序的子数组合并成一个有序的数组。
public class Merge_sort {
//调用mergeSort(date,0,date.length-1)
public static void mergeSort(int[] date,int first,int last){
if(first<last){
int mid=(first+last)/2;
mergeSort(date, first, mid);
mergeSort(date, mid+1, last);
merge(date, first, mid, last);
}
}
private static void merge(int[] a,int first,int center,int last){
int [] tmpArr=new int[a.length];
int mid=center+1;
int third=first;
int tmp=first;
while(first<=center&&mid<=last){
if(a[first]<=a[mid]){
tmpArr[third]=a[first];
first++;
}else{
tmpArr[third]=a[mid];
mid++;
}
third++;
}
while(mid<=last)
tmpArr[third++]=a[mid++];
while(first<=center)
tmpArr[third++]=a[first++];
while(tmp<=last)
a[tmp]=tmpArr[tmp++];
}
}
8、基数排序
基数排序不比较对象,而是将数组元素作为长度相等的的字符串对待。若元素为整数,先把每个元素按最大数的位数在数前以0补全,分成10个桶,先以个位数的值进行装桶,即个位数为1则放入1号桶,为9则放入9号桶,按顺序装回原数组;再以百位数划分.....以此类推,最终得到有序数组。基数排序是最快的,时间复杂度为O(n),但不适用于所有数据。
public class RadixSort{
//调用radixSort(data,10);
public static void radixSort(int[] data, int radix){
int[] tmp=new int[data.length];
int[] buckets = new int[radix];
for (int i=0,rate=1;i<data.length-1;i++){
Arrays.fill(buckets, 0);
System.arraycopy(data, 0, tmp, 0, data.length);// 将data中的元素完全复制到tmp数组中
for (int j = 0; j < data.length; j++){
int subKey = (tmp[j]/rate) % radix;
buckets[subKey]++;
}
for (int j = 1; j < radix; j++)
buckets[j] = buckets[j] + buckets[j - 1];
for (int m = data.length - 1; m >= 0; m--){
int subKey = (tmp[m] / rate) % radix;
data[--buckets[subKey]] = tmp[m];
}
rate *= radix;
}
}
}
8、各种排序的稳定性,时间复杂度和空间复杂度总结
稳定性:所有相等的数经过某种排序方法后,仍能保持它们在排序之前的相对次序,就称这种排序方法是稳定的。