常用的排序方法整理

常用的排序方法

排序方法

平均时间复杂度 时间复杂度(最好) 时间复杂度(最坏) 空间复杂度 稳定性
冒泡排序(BubbleSort) O(n²) O(n) O(n²) O(1) 稳定
直接选择排序(SectionSort) O(n²) O(n²) O(n²) O(1) 不稳定
直接插入排序(InsertSort) O(n²) O(n) O(n²) O(1) 稳定
希尔排序(ShellSort) O(nlog₂n) O(n) O(n²) O(1) 不稳定
快速排序(QuickSort) O(nlog₂n) O(nlog₂n) O(n²) O(nlog₂n) 不稳定
归并排序(MergeSort) O(nlog₂n) O(nlog₂n) O(nlog₂n) O(n) 稳定
堆排序(HeapSort) O(nlog₂n) O(nlog₂n) O(nlog₂n) O(1) 不稳定

桶排序(BucketSort)

/基数排序(RadixSort)/

计数排序(CountSort)

O(d(n+m))

O(d(n+m)) O(d(n+m)) O(n+m) 稳定

Tips: d(n+m)=n+n*(logN-logM);

其中,算法的稳定性是指,当数组中存在两个相同的数据,经过排序之后两个数据的位置是否发生相对改变。

1、冒泡排序(BubbleSort):

1:将每个数与下一个数进行比较,如果大于,则两者交换位置;(经过1次循环之后,最大数位于数组末端)

2:对剩余的 n - 1个数,重复第一步操作,经过n次循环之后,数组达到有序。

 Tips:1. 可以将整个过程理解成泡泡的上升,最大的泡泡浮得最快,位于数组末端,

         2.最好的情况是数组基本有序,最坏的情况是数组基本逆序。

public class BubbleSort {
	public static void bubbleSort(int[] arr){
		if(arr == null || arr.length <2){
			return;
		}
		for(int i = arr.length - 1; i > 0; i--){//循环N次
			for(int j = 0; j < i; j++){
				if(arr[j] > arr[j+1]){
					SwapArr.swap(arr, j, j+1);
				}
			}
		}
	}
	
}

2、直接选择排序(SectionSort)

1:遍历整个数组,找到其中最小的数,并与数组最前端的数交换位置(初始为0,经过一次遍历,最小数位于最前端);

2:从1位置继续遍历,重复第1步操作,循环n次,最终数组达到有序。

Tips: 每次都在数组里找最小的数放到前面(已经排好序的数组后面)

public class SectionSort {
	public static void sectionSort(int[] arr){
		if(arr == null || arr.length <2){
			return;
		}
		for(int i = 0; i < arr.length - 1; i++){
			int minIndex = i;
			for(int j = i + 1; j < arr.length; j++){
				if(arr[minIndex] > arr[j]){
					minIndex = j;					
				}
			}			
			SwapArr.swap(arr, i, minIndex);
		}	
	}
	
}

3、直接插入排序(InsertSort)

1:从1位置开始,与前面的数进行比较,如果小于前面的数则交换位置,直到不再小于前面的数;

2:从2位置开始,重复第1步,直到达到最后一个位置。

Tips:1.从第二个数开始,做插入操作(插入的位置在小于自己的数之后,大于自己的数之前),最终达到有序,

          2.最好的情况是数组基本有序,最坏的情况是数组基本逆序。

public class InsertionSort {
	public static void insertionSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int i = 1; i < arr.length; i++) {
			for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
				SwapArr.swap(arr, j, j+1);
			}
		}
	}
}

4、希尔排序(ShellSort)

1:首先确定一个步长 k,根据步长把数组分为(n/k)个部分进行排序;

2:缩短步长,继续细分待排序的数组;

3:直到步长缩短为1,此时数组达到有序。

Tips:1.过程类似于插入排序,但是通过将待排数组分为若干个子序列,减少移动次数,是插入排序的一种优化,

          2.常用的初始步长是 (n / 2)。

          3.最好的情况是数据基本有序。

public class ShellSort {
	public static void shellSort(int[] arr) {
		if (arr == null || arr.length < 2)
			return;
		int gap = arr.length / 2;
		for (; gap > 0; gap /= 2) {
			for (int i = gap; i < arr.length; i++) {
				for (int j = i - gap; j >= 0 && arr[j] > arr[j + gap]; j -= gap) {
					SwapArr.swap(arr, j, j+gap);
				}
			}
		}
	}

	public static void shellSort(int[] arr, int n) {
		if (arr == null || arr.length < 2)
			return;
		int gap = n / 2;
		for (; gap > 0; gap /= 2) {
			for (int i = gap; i < n; i++) {
				for (int j = i - gap; j >= 0 && arr[j] > arr[j + gap]; j -= gap) {
					SwapArr.swap(arr, j, j+gap);
				}
			}
		}
	}
}

5、快速排序(QuickSort)

1:随机选出一个哨兵partition(枢轴 pivot),把大于partition的值放在右边,小于partition的值放在左边;

         (partition一般取最左边的数,也可随机生成)

2:从partition左右两边分割,递归调用自身;

3:当达到递归结束条件是,数组达到有序状态。

Tips:1.为防止出现最坏情况(即所取的哨兵(枢轴)为最大数或最小数),可以随机生成哨兵(枢轴),但会增加开销

           2.当数组基本有序时,快排将退化为冒泡排序。

public class QuickSort {
	public static void quickSort(int[] arr){
		if(arr.length>0)
			quickSort(arr, 0, arr.length -1);
	}
	
	public static void quickSort(int[] arr, int left,int right){
		if(left > right){
			return;
		}		
		int i = left;
		int j = right;
		int partition = arr[left];
		while(i < j){			
			while(j > i && arr[j] > partition){
				j--;
			}
			while(j > i && arr[i] <= partition){
				i++;
			}
			if(i<j){
				SwapArr.swap(arr, i, j);
			}
		}
		SwapArr.swap(arr, i, left);
		quickSort(arr, left, i-1);
		quickSort(arr,i+1, right);
	}
}

6、归并排序(MergeSort)

1:把数据分为两个部分,分别进行排序,最后合并;

2:递归调用自身,以实现排序。

Tips:1.将待排数组分为若干个有序的子序列,再进行合并操作,使其达到有序状态。

public class MergeSort {
	public static void mergeSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		mergeSort(arr, 0, arr.length - 1);
	}

	public static void mergeSort(int[] arr, int left, int right) {
		if (left == right) {
			return;
		}
		int mid = left + ((right - left) / 2);
		mergeSort(arr, left, mid);
		mergeSort(arr, mid + 1, right);

		merge(arr, left, mid, right);
	}

	private static void merge(int[] arr, int left, int mid, int right) {
		int[] temp = new int [right - left + 1];
		int l = left;
		int r = mid + 1;
		int index = 0;
		while(l <= mid && r <= right){
			temp[index++] = arr[l] < arr[r] ? arr[l++] :arr[r++];			
		}
		while(l <= mid){
			temp[index++] = arr[l++];
		}
		while(r <= right){
			temp[index++] = arr[r++];		
		}
		for(int i = 0; i < temp.length; i++){
			arr[left+i] = temp[i];
		}
		
	}	
}

7、堆排序(HeapSort)

1:将数组构造成一个大顶堆;

2:将堆顶的数据与最后一个数进行交换,然后调整剩下的(n - 1)个数据,使其仍为大顶堆;

3:最后实现排序。

Tips:1.大顶堆指根结点大于叶子结点的完全二叉树,整个算法的实现在于如何构建大顶堆和如何调整,

2.完全二叉树中,{(n - 1)/ 2 } 可以找到叶子结点的根结点在数组中的索引,(n * 2 + 1)为根结点的左子树结点在数组中的结点位置,再加 1 则为右结点。

public class HeapSort {
	private static void insertHeap(int[] arr, int i) {//构建大顶堆
		while (arr[i] > arr[(i - 1) / 2]) {
			SwapArr.swap(arr, i, (i - 1) / 2);
			i = (i - 1) / 2;
		}
	}
	
	private static void heapAdjust(int[] arr,int index, int size){
		int left = index * 2 + 1;
		while(left < size){
			int largest = (left+1) < size && arr[left] < arr[left+1] ? left+1: left;
			if(arr[index] > arr[largest]){
				return;
			}
			SwapArr.swap(arr, index, largest);
			index = largest;
			left = index * 2 +1;
		}
	}
	
	public static void heapSort(int[] arr){
		if(arr == null || arr.length < 2){
			return ;
		}
		for(int i = 1; i < arr.length; i++){ //将数组整理为大顶堆
			insertHeap(arr, i);
		}
		int size = arr.length - 1;
		while (size > 0){
			SwapArr.swap(arr, 0, size);//交换堆顶和最后一位
			heapAdjust(arr, 0, size--);//重新整理数组形成大顶堆
		}
		
	}
}

 

8、桶排序(BucketSort)/基数排序(RadixSort)/计数排序(CountSort)

1:桶排序的实现方法为:找到数组中的最大数max,创建(max + 1) 个“桶”(数组),遍历一遍待排序的数组,将数据放入对应的“桶”,最后按顺序遍历一遍所有“桶”,将有数据的桶的编号按顺序放入数组中,最后数组达到有序。

2:基数排序的实现方法有两种:

MSD(最高位优先 Most Significant Digit first),找到数组中数据最高位,对最高位进行排序,依次进行到个位数。

LSD(最低位优先 Least Significant Digit first),从个位数开始排序,直到最高位结束。

桶排序:

public class BucketSort {
	public static void bucketSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		int max = Integer.MIN_VALUE;
		for (int i : arr) {
			max = Math.max(max, i);
		}

		int[] bucket = new int[max + 1];
		for(int i : arr){
			bucket[i] ++;		
		}
		int i = 0;
		for(int j = 0; j < bucket.length;j++){
			while(bucket[j]-- >0){
				arr[i++] = j;
			}
		}
		
	}
}

基数排序:

public class RadixSort {
	public static void radixSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		radixSort(arr, 0, arr.length - 1, maxbit(arr));
	}

	private static void radixSort(int[] arr, int begin, int end, int digit) {
		int[] bucket = new int[end - begin + 1];
		final int radix = 10;
		int i, j = 0;
		int[] count = new int[radix];
		for (int d = 1; d <= digit; d++) {
			for(i = 0; i < radix ; i++){
				count[i] = 0;
			}
			for (i = begin; i <= end; i++) {
				j = getDigit(arr[i], d);
				count[j]++;
			}		
			for (i = 1; i < radix; i++) {//count用来归位数组,此处加上前一个是往后增加
				count[i] += count[i - 1];
			}		
			for (i = end; i >= begin; i--) {
				j = getDigit(arr[i], d);
				bucket[count[j] - 1] = arr[i];
				count[j] -- ;
			}
			for(i = begin ,j = 0; i<= end;i++,j++){
				arr[i] = bucket[j];
			}
		}
	}

	private static int maxbit(int[] arr) {
		int max = Integer.MIN_VALUE;
		int res = 0;
		for (int i : arr) {
			max = Math.max(max, i);
		}
		while (max != 0) {
			res++;
			max /= 10;
		}
		return res;
	}

	private static int getDigit(int num, int d) {
		return (num / (int) Math.pow(10, d - 1) % 10);
	}

}

本文章借鉴博主ChaosofStars的博文“常用排序算法整理”,如有侵权请联系本人删除。

猜你喜欢

转载自blog.csdn.net/noob_Cliam/article/details/82793027