十大经典排序算法以及部分优化

参考:https://www.cnblogs.com/guoyaohua/p/8600214.html?tdsourcetag=s_pcqq_aiomsg

在这里插入图片描述

1.冒泡排序(Bubble Sort)

1.1基本思想

两两比较大小,大的沉下去(放后面),小的浮起来(放前面)。

1.2算法描述

  • 比较相邻的两个数,如果第一个大就交换位置
  • 对每一对相邻的元素做比较,这样最大的数就在最后的位置
  • 重复以上步骤,除了最后已经排好序的数
  • 重复步骤1~3,直到排序完成

1.3动图演示
冒泡
1.4算法分析

  • 平均时间复杂度 O(n^2) ,最佳情况O(n), 最差情况O(n^2)
    空间复杂度 O(1)

1.5JAVA代码实现

public static int[] bubbleSort(int[] array){
	if(array.length == 0){
		return array;
	}
	for(int i=0;i<array.length-1;i++){		//比较a.length-1轮即可,比较一轮找到一个
		for(int j=0;j<array.length-1-i;j++){		//无序区间[0,a.length-1-i)
			if(array[j+1]<array[j]){
				int temp = array[j+1];
				array[j+1] = array[j];
				array[j] = temp;
			}
		}
	}
	return array;
}

如果你对循环的边界设定不清楚,可以看下这张图:
循环的思路
1.6算法优化实现

  1. 每次比较都使用一个temp变量,同一个空间。
  2. 如果外层循环循环一次内层都没有发生交换,即比较一轮都没有交换的说明数组有序。
  3. 内层循环时,如果最后有几次并没有交换,说明只需要比较到上次交换的位置即可。
public static int[] bubbleSort(int[] array){
	if(array.length == 0){
		return array;
	}
	int temp;
	int changeIndex = 0;	//最后一次交换的位置
	int sortIndex = array.length;	//无序区间的边界
	for(int i=0;i<array.length-1;i++){		
		boolean flag = true;	//优化2
		for(int j=0;j<sortIndex;j++){		
			if(array[j+1]<array[j]){
				temp = array[j+1];
				array[j+1] = array[j];
				array[j] = temp;
				changeIndex = j;	//记录最后一次发生交换的位置
				flag = false;	//发生了交换
			}
		}
		sortIndex = changeIndex;	//改变无序区间的边界
		if(flag){	//这一轮一次交换都没有发生,数组有序
			return array;
		}
	}
	return array;
}

1.7总结

  • 使用场景:适用元素较少的情况下和数组基本有序的情况
  • 优点:简单,空间复杂度低,稳定
  • 缺点:时间复杂度高,效率慢

*2.选择排序(Selection Sort) *

2.1基本思想

表现最稳定的排序算法之一,在未排序序列中找到最小的元素存放在序列的起始位置,再从剩余未排序序列中继续寻找最小的元素,然后放到已排序序列的末尾,重复执行。

2.2算法描述

  • n个元素,初始时:无序区间R[1…n],有序区间为空;
  • 到第i趟开始时,有序区间R[1…i-1],无序区间R[i…n]。然后从无序区间找到最小的数和无序区间第一个数交换,这时有序区间变为R[1…i],无序区间变为R[i+1…n];
  • n-1趟结束后,数组有序。

2.3动图演示

快速排序
2.4算法分析

  • 平均时间复杂度 O(n^2) ,最佳情况O(n^2), 最差情况O(n^2)
    空间复杂度 O(1)

2.5JAVA代码实现

public static int[] selectionSort(int[] array){
	if(array.length == 0){
		return array;
	}
	for(int i = 0;i < array.length;i++){
		int minIndex = i;
		for(int j = i, j < array.length;j++){
			if(array[j] < array[mindex]){
				minIndex = j;
			}
		}
		int temp = array[minIndex];
		array[minIndex] = array[i];
		array[i] = temp;
	}
	return array;
}		

2.6算法优化实现

选择排序是一次确定一个元素的位置,而选择排序的优化则是一次确定两个元素的位置,比如降序:每次将最小值放在起始位置,最大值放在末尾位置。这样外层循环的时间缩短了一半,性能也提高了很多。

public static int[] sort(int[] a) {
		if(a.length == 0) {
			return a;
		}
		int left = 0;
        int right = a.length - 1;
		while(left < right) {
			int minIndex = left;
		 	int maxIndex = left;
			for(int j=left;j <= right;j++) {
				minIndex = a[j] < a[minIndex] ? j : minIndex;
				maxIndex = a[j] > a[maxIndex] ? j : maxIndex;
			}
			if(minIndex != left) {
				int temp = a[minIndex];
				a[minIndex] = a[left];
				a[left] = temp;
			}
			 //如果最大元素下标是left,但是前面已经和最小元素交换了,则此时最大元素下标应该是min
            if (maxIndex == left){
            	maxIndex = minIndex;
            }
            //如果max就是最小的则不用交换
            if (maxIndex != right){
                int tmp = a[right];
                a[right] = a[maxIndex];
                a[maxIndex] = tmp;
            }
            left++;
            right--;	
		}		
		return a;
	}

2.7总结

  • 使用场景:适用元素较少的情况下和数组基本有序的情况
  • 优点:交换次数少,移动次数确定n次
  • 缺点:效率慢,不稳定

*3.插入排序(Insertion Sort) *

3.1基本思想

通过构建有序序列,对未排序的数据从有序序列中从后向前扫描,找到相应位置插入。在这个过程中,需要一直将已排序元素向后移动来为新元素提供空间。

3.2算法描述

  • 将第一个数和第二个数排序,构成一个有序序列;
  • 将第三个数在有序序列中从后向前扫描找到相应位置;
  • 重复步骤2,直到数组有序。

3.3动图演示
插入排序
3.4算法分析

  • 平均时间复杂度 O(n^2) ,最佳情况O(n), 最差情况O(n^2)
    空间复杂度 O(1)

3.5JAVA代码实现

public static int[] insertionSort(int[] array){
	if(array.length == 0){
		return array;
	}
	int current;
	for(int i=0;i<array.length-1;i++){
		current = array[i+1];
		int preIndex = i;
		while(preIndex >= 0 && current<array[preIndex]){
			array[preIndex + 1] = array[preIndex];
			preIndex--;
		}
		array[preIndex + 1] = current;
	}
	return array;
}

3.6算法优化实现

  • 折半插入排序算法(折半排序算法)---- 减少了比较移动次数没有发生变化
  • 2-路插入排序算法 ---- 减少了移动次数‘
  • 表插入排序算法 ---- 链表存储,不需要移动
//折半插入
public void sort1(int[] array){
	    int temp;
	    for(int i = 1; i < array.length; i++){
	        int low = 0;
	        int hight = i-1;
	        temp = array[i];	 
	        while(hight>=low){
	            int mid = ( low + hight ) / 2;
	            if (array[mid] > temp){
	                hight = mid - 1;
	            }else{
	                low = mid + 1;
	            }
	        }
	        for (int j = i-1; j > hight; j--) {
	            array[j+1] = array[j];
	        }	 
	        array[hight+1] = temp;
	    }
}

3.7总结

  • 适用场景:数据少并且数组大部分有序时;
  • 优点:稳定,相对于 冒泡和选择更快
  • 缺点:比较次数不一定,比较次数越少,插入点后的数据移动越多,特别是当数据总量庞大的时候,但用链表可以解决这个问题。

*4.希尔排序(Shell Sort) *

4.1基本思想

希尔排序是改进的插入排序算法,也叫减小增量排序。与插入排序不同之处是会优先比较距离较远的元素。

4.2算法描述

先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为dl的倍数的记录放在同一个组中。先在各组内进行直接插人排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。

该方法实质上是一种分组插入方法。
比较相隔较远距离(称为增量)的数,使得数移动时能跨过多个元素,则进行一次比较就可能消除多个元素交换。

4.3过程演示
在这里插入图片描述
4.4动图演示
在这里插入图片描述
4.5算法分析

  • 平均时间复杂度 O(nlog2n)
    最佳O(nlog2n), 最差O(nlog2n)
    空间复杂度 O(1)

4 .6JAVA代码实现

public static int[]  Sort1(int[] a){
	    if(a == null || a.length <= 1){
	        return a;
	    }
	    //希尔排序  升序 三个for
	    int size = a.length;
	     int i, j, gap;
		for (gap = size / 2; gap > 0; gap /= 2){ // 每次的增量,递减趋势
			for (i = gap; i < size; i++) {
			 //i:代表即将插入的元素角标,作为每一组比较数据的最后一个元素角标 
	         //j:代表与i同一组的数组元素角标
				for (j = i ; j -gap >= 0 && a[j-gap] > a[j]; j -= gap){
					int temp = a[j];
	                a[j] = a[j - gap];
	                a[j - gap] = temp;
	            }
	        }
		}
	    return a;
	}

4.7总结

  • 使用场景:可以用于大型数组,比插入排序和选择排序快;
  • 优点:就地排序,空间复杂度O(1)。改进的插入排序,相对于高效的基于交换元素的排序算法。
  • 缺点:不稳定,时间复杂度依赖于增量序列函数

*5.归并排序(Merge Sort) *

5.1基本思想

该方法是使用分治法的典型应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
5.2算法描述

  • 把长度为n的序列分成两个长度为n/2的子序列;
  • 将这两个子序列分别采用归并排序;
  • 将两个排序好的子序列合并成一个最终的序列。

5.3动图演示
在这里插入图片描述
5.4算法分析

  • 平均时间复杂度 O(nlongn) ,最佳情况O(n), 最差情况O(nlongn)
    空间复杂度 O(n)

5.5JAVA代码实现

public static int[] sort(int[] a,int low,int high){	//刚开始low = 0;high = a.length-1
        int mid = (low+high)/2;
        if(low<high){
            sort(a,low,mid);
            sort(a,mid+1,high);
            //左右归并
            merge(a,low,mid,high);
        }
        return a;
    }
     
    public static void merge(int[] a, int low, int mid, int high) {
        int[] temp = new int[high-low+1];
        int i= low;
        int j = mid+1;
        int k=0;
        // 把较小的数先移到新数组中
        while(i<=mid && j<=high){
            if(a[i]<a[j]){
                temp[k++] = a[i++];
            }else{
                temp[k++] = a[j++];
            }
        }
        // 把左边剩余的数移入数组 
        while(i<=mid){
            temp[k++] = a[i++];
        }
        // 把右边边剩余的数移入数组
        while(j<=high){
            temp[k++] = a[j++];
        }
        // 把新数组中的数覆盖nums数组
        for(int x=0;x<temp.length;x++){
            a[x+low] = temp[x];
        }
    }

5.6优化以及实现

  1. 取消无谓的归并
      归并排序在归并的过程中, 无论前后两个数组如何,都要一一的开辟空间再逐一的比较,我们知道,前后两个数组它本身是有序的。但是也有一种情况,如果前面数组的最后一个元素刚好比后面数组的第一个元素小,那么就没有再归并的必要了。
public static int[] sort(int[] a,int low,int high){
        int mid = (low+high)/2;
        if(low<high){
            sort(a,low,mid);
            sort(a,mid+1,high);
            //左右归并
            if ( a[mid] > a[mid + 1]) {		//在上述代买的基础上添加一个if判断即可
              merge(a,low,mid,high); 
            }
        }
        return a;
    }

2.减小递归深度
随着递归的深入,数组越乎近于有序,此时我们用选择排序代替递归。在上述代码递归函数sort中第一行添上一个判断通常调用选择排序即可。

 if ( high - low <= 15) {
     insertSorted(arr, low, high);
     return;
 }

5.7总结

  • 使用场景:n大时使用较好
  • 优点:稳定,效率高
  • 缺点:占用内存,需要O(n)的辅助空间

*6.快速排序(Quick Sort) *

6.1基本思想
通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

快速排序思想:挖坑填数+分治法
6.2算法描述

  • 从数列中挑出一个元素,称为“基准”(pivot)
  • 重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任何一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
  • 递归地(recursively)把小于基准值元素的子数列和大于基准值元素的子数列排序。

6.3动图演示
在这里插入图片描述

6.4算法分析

  • 平均时间复杂度 O(nlongn)
    最佳O(nlongn), 最差O(n^2)
    空间复杂度 O(logn)

6.5JAVA代码实现

 //递归版本
public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		String[] s = sc.nextLine().split(" ");
		int[] array = new int[s.length];
		for(int i=0;i<s.length;i++) {
			array[i] = Integer.parseInt(s[i]);
		}
		sort(array,0,array.length-1);
}
public static void sort(int[] arr, int low, int high){
	     if(arr.length <= 0) return ;
	     if(low >= high) return ;
	     int left = low;
	     int right = high;

	     int temp = arr[left];   //挖坑1:保存基准的值
	     while (left < right){
	         while(left < right && arr[right] >= temp){  //坑2:从后向前找到比基准小的元素,插入到基准位置坑1中
	             right--;
	         }
	         arr[left] = arr[right];
	         while(left < right && arr[left] <= temp){   //坑3:从前往后找到比基准大的元素,放到刚才挖的坑2中
	             left++;
	         }
	     arr[right] = arr[left];
	     }
	     arr[left] = temp;   //基准值填补到坑3中,准备分治递归快排
	     //System.out.println("Sorting: " + Arrays.toString(arr));
	     sort(arr, low, left-1);
	     sort(arr, left+1, high);
} 	
//非递归,栈实现
public static int[] quickSort(int[] num) {
		if (num.length < 3)
			return null;
		int start = 0;
		int end = num.length - 1;
		int middle = 0;
		Stack<Integer> index = new Stack<Integer>();
		index.push(start);
		index.push(end);
		while (!index.isEmpty()) {

			end = index.pop();
			start = index.pop();
			int i = start;
			int j = end;
			int pivot = num[i]; // 先挖出来一个坑,为了移动其他数据
			while (i < j) {
				while (i < j && num[j] >= pivot)
					// 大于当前的值不动,遇到小于当前的值,把该值,埋到坑里
					j--;
				num[i] = num[j];
				while (i < j && num[i] <= pivot)
					// 小于当前的值不动,遇到大于标记的值,埋到坑里。
					i++;
				num[j] = num[i];
			}
			num[i] = pivot;
			middle = i;
			if (middle - 1 > start) {
				index.push(start);
				index.push(middle - 1);
			}
			if (middle + 1 < end) {
				index.push(middle + 1);
				index.push(end);
			}
		}
		return num;
}    

6.6优化思路

  1. 选择pivot
    可以取第一个或者最后一个(固定值)或 随机选择 或 三数中值分割法

  2. 当某个区间需要排序的数据为一定范围时,进行直接插入排序

//三数选中
	    private static void selectPivotMedianOfThree(Integer[] array,int low,int high){
	        int m = (low+high)/2;
	        //实现 array[mid] <= array[low] <= array[high]
	        if(array[high] < array[low]){
	            swap(array[high],array[low]);
	        }
	        if(array[high] < array[m]){
	            swap(array[high],array[m]);
	        }
	        if(array[m] > array[low]){
	            swap(array[m],array[low]);
	        }
	    }

6.7总结

  • 作用:快速有效的进行数据排序
  • 优点:占内存小,耗时少
  • 缺点:不稳定

*7.堆排序(HeapSort) *

7.1基本思想
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

7.2算法描述

  • 创建一个堆 H[0……n-1];
  • 把堆首(最大值)和堆尾互换;
  • 把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;
  • 重复步骤 2,直到堆的尺寸为 1。

7.3动图演示

在这里插入图片描述
7.4算法分析

  • 平均时间复杂度 O(nlongn)
    最佳O(nlongn), 最差O(nlongn)
    空间复杂度 O(1)

7.5JAVA代码实现

    private static void heapSort(int[] a) {
        // 1.初始化最大堆,从倒数第二层开始初始化
        for (int i = a.length/2; i >=0; i--) {
            maxHeap(a,a.length,i);
        }
        // 2. push堆顶元素倒最后,并且重新建立最大堆
        for (int i = a.length-1; i >=0 ; i--) {
            swap(a,0,i);
            maxHeap(a,i-1,0);
        }
    }

    private static void maxHeap(int[] a, int length, int i) {
        //  最大堆的特性: 任意的父节点总比其两个子节点大.
        //  二叉树节点转换: 如果有节点下标为n,则其左子节点下标为 2n,右子节点下标为2n+1,父节点下标为 n/2
        int t = i+1; //便于理解 数组的下标序号和数组的序号的转换.  下文也需注意这点: 数组实际下标 = 堆下标 - 1;
        while (2*t <= length) { // 该节点不是最后一层,存在左子节点
            int tmpMax = t;// 记录父子节点中的最大下标
            if (a[t - 1] < a[2 * t - 1]) { // 父节点和左子节点比较出最大值
                tmpMax = 2 * t;
            }
            if (a[tmpMax - 1] < a[2 * t]) { // 上次比较中较大的和右子节点比较
                tmpMax = 2 * t + 1;
            }
            if (tmpMax != t) {  // 如果当前根节点不是最大值,则执行交换.
                swap(a, t - 1, tmpMax - 1);
                t = tmpMax;// 继续向下调整
            } else {
                return;// 否则停止调整
            }
        }
    }
 
    private static void swap(int[] a, int i, int j) {
        int tmp = a[i];
        a[i] = a[j];
        a[j] = tmp;
    }

7.6总结

  • 应用场景:n较大时
  • 优点:效率高
  • 缺点:不稳定,不适合较小的序列

*8.计数排序(Counting Sort) *

8.1基本思想
计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。计数排序使用一个额外的数组C,其中第i个元素是待排序数组A中值等于i的元素的个数。然后根据数组C来将A中的元素排到正确的位置。它只能对整数进行排序。

8.2算法描述

  1. 花O(n)的时间扫描一下整个序列 A,获取最小值 min 和最大值 max
  2. 开辟一块新的空间创建新的数组 B,长度为 ( max - min + 1)
  3. 数组 B 中 index 的元素记录的值是 A 中某元素出现的次数
  4. 最后输出目标整数序列,具体的逻辑是遍历数组 B,输出相应元素以及对应的个数

8.3动图演示
在这里插入图片描述
8.4算法分析

  • 平均时间复杂度 O(n+k)
    最佳O(n+k), 最差O(n+k)
    空间复杂度 O(k)

8.5JAVA代码实现

public static int[] sort(int[] data, int k) {  //k为数组中最大元素  
        // 存放临时数据的数组tmp
        int[] tmp = new int[k + 1];  
  
        // 计算数组中每个元素i出现的次数,存入数组tmp中的第i项
        for (int i = 0; i <= data.length - 1; i++) {  
            tmp[data[i]]++;  
        }  
  
        // 计算数组中小于等于每个元素的个数
        for (int j = 1; j <= k; j++) {  
            tmp[j] = tmp[j] + tmp[j - 1];  
        }  
  
        // result数组用来来存放排序结果  
        int[] result = new int[data.length];  
        for (int i = data.length - 1; i >= 0; i--) {  
            result[tmp[data[i]] - 1] = data[i];  
            tmp[data[i]]--;  
        }  
  
        return result;  
    }  

8.6总结

  • 使用场景:数列取值比较集中的情况

  • 优点:当数据范围小时,速度非常快

  • 缺点:需要额外内存,只能对整数排序

*9.桶排序(Bucket Sort) *

9.1基本思想
桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。
工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序。
9.2算法描述

  • 设置固定数量的空桶;
  • 把数据放到对应的桶中;
  • 对每个不为空的桶中数据进行排序;
  • 拼接不为空的桶中数据,得到结果。

注:如果递归使用桶排序为各个桶排序,则当桶数量为1时要手动减小BucketSize增加下一循环桶的数量,否则会陷入死循环,导致内存溢出。
9.3动图演示
在这里插入图片描述

9.4算法分析
桶排序的时间复杂度,取决与对各个桶之间数据进行排序的时间复杂度,因为其它部分的时间复杂度都为O(n)。很显然,桶划分的越小,各个桶之间的数据越少,排序所用的时间也会越少。但相应的空间消耗就会增大。

  • 平均时间复杂度 O(n+k)
    最佳O(n+k), 最差O(n^2)
    空间复杂度 O(n+k)

9.5JAVA代码实现

 public static void bucketSort(int[] arr) {
	        int max = Integer.MIN_VALUE;
	        int min = Integer.MAX_VALUE;
	        for (int i = 0; i < arr.length; i++) {
	            max = Math.max(max, arr[i]);
	            min = Math.min(min, arr[i]);
	        }

	        //桶数
	        int bucketNum = (max - min) / arr.length + 1;
	        ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum);
	        for (int i = 0; i < bucketNum; i++) {
	            bucketArr.add(new ArrayList<Integer>());
	        }

	        //将每个元素放入桶
	        for (int i = 0; i < arr.length; i++) {
	            int num = (arr[i] - min) / (arr.length);
	            bucketArr.get(num).add(arr[i]);
	        }

	        //对每个非空的桶排序,排序后顺便存入临时的List,则list中已经有序)
	        List<Integer> temp = new ArrayList<>();
	        for (int i = 0; i < bucketArr.size(); i++) {
	            if (bucketArr.get(i) != null) {
	                Collections.sort(bucketArr.get(i));
	                temp.addAll(bucketArr.get(i));
	            }
	        }

	        // 将temp中的数据写入原数组
	        for (int i = 0; i < arr.length; i++) {
	            arr[i] = temp.get(i);
	        }
	    }
 

9.6总结

  • 使用场景:数据均匀的在一个区间
  • 优点:稳定,速度快
  • 缺点:耗空间

*10.基数排序(Radix Sort) *

10.1基本思想
基数排序也是非比较的排序算法,对每一位进行排序,从最低位开始排序,复杂度为O(kn),为数组长度,k为数组中的数的最大的位数;

基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。基数排序基于分别排序,分别收集,所以是稳定的。

10.2算法描述

  • 取得数组中的最大数,并取得位数;
  • arr为原始数组,从最低位开始取每个位组成radix数组;
  • 对radix进行计数排序(利用计数排序适用于小范围数的特点);

10.3动图演示
在这里插入图片描述
10.4算法分析

  • 平均时间复杂度 O(nk)
    最佳O(n
    k), 最差O(n*k)
    空间复杂度 O(n+k)

10.5JAVA代码实现

private static void radixSort(int[] arr) {
        //待排序列最大值
        int max = arr[0];
        int exp;//指数

        //计算最大值
        for (int anArr : arr) {
            if (anArr > max) {
                max = anArr;
            }
        }

        //从个位开始,对数组进行排序
        for (exp = 1; max / exp > 0; exp *= 10) {
            //存储待排元素的临时数组
            int[] temp = new int[arr.length];
            //分桶个数
            int[] buckets = new int[10];

            //将数据出现的次数存储在buckets中
            for (int value : arr) {
                //value的个位
                buckets[(value / exp) % 10]++;
            }

            //更改buckets[i],
            for (int i = 1; i < 10; i++) {
                buckets[i] += buckets[i - 1];
            }

            //将数据存储到临时数组temp中
            for (int i = arr.length - 1; i >= 0; i--) {
                temp[buckets[(arr[i] / exp) % 10] - 1] = arr[i];
                buckets[(arr[i] / exp) % 10]--;
            }

            //将有序元素temp赋给arr
            System.arraycopy(temp, 0, arr, 0, arr.length);
        }

    }

10.6总结

  • 使用场景:适合取值很大的数,也可对字符串进行排序
  • 优点:稳定
  • 缺点:需要额外空间

11.总结

- 基数排序 vs 计数排序 vs 桶排序 对桶的使用:

基数排序:根据键值的每位数字来分配桶
计数排序:每个桶只存储单一键值
桶排序:每个桶存储一定范围的数值

== - 算法选择 ==

(1)是否稳定:

稳定:冒泡排序、插入排序、归并排序和基数排序。

不稳定:选择排序、快速排序、希尔排序、堆排序。

(2)时间复杂度:

O(n2) :各类简单排序:直接插入、直接选择和冒泡排序。

O(nlog2n)):快速排序、堆排序和归并排序;

O(n) : 基数排序,桶排序

(3)n数值大小:

n较小(如n≤50),可采用直接插入或直接选择排序。

若初始状态基本有序(正序),则应选用直接插人、冒泡或快速排序为宜;

若n较大,则应采用时间复杂度为O(nlgn)的排序方法:快速排序(时间短)、堆排序(辅助空间小于快速排序)或归并排序(稳定)。

发布了35 篇原创文章 · 获赞 7 · 访问量 3173

猜你喜欢

转载自blog.csdn.net/weixin_44358097/article/details/104400852