Java排序算法总结

点击进入尚硅谷数据结构和算法Java代码导航

排序算法总览

自己总结的一个判断排序算法稳定性的方法:如果存在跨下标的数据交换,则算法不稳定。如选择排序、希尔排序、快速排序都存在跨下标的数据交换,它们都是不稳定的。而如果不存在跨下标的数据交换的算法不一定是稳定的,如将冒泡算法的大小判断条件加上等号,就是不稳定的,去掉等号是稳定的。

举个例子:在下面的表格中,如果下标为0的数据和下标为2的的数据进行交换,则第二个3就会跑到第一个3前面,这样的算法肯定是不稳定的。

数据43309
下标01234

以下Java代码实现的都是从小到大的排序。

算法平均时间复杂度最好情况最坏情况空间复杂度排序方式稳定性
冒泡排序 O ( n 2 ) O(n^2) O(n2) O ( n ) O(n) O(n) O ( n 2 ) O(n^2) O(n2) O ( 1 ) O(1) O(1)In-place稳定
选择排序 O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( 1 ) O(1) O(1)In-place不稳定
插入排序 O ( n 2 ) O(n^2) O(n2) O ( n ) O(n) O(n) O ( n 2 ) O(n^2) O(n2) O ( 1 ) O(1) O(1)In-place稳定
希尔排序 O ( n log ⁡ n ) O(n\log n) O(nlogn) O ( n log ⁡ 2 n ) O(n\log^2 n^) O(nlog2n) O ( n log ⁡ 2 n ) O(n\log^2 n) O(nlog2n) O ( 1 ) O(1) O(1)In-place不稳定
归并排序 O ( n log ⁡ n ) O(n\log n) O(nlogn) O ( n log ⁡ n ) O(n\log n) O(nlogn) O ( n log ⁡ n ) O(n\log n) O(nlogn) O ( n ) O(n) O(n)Out-place稳定
快速排序 O ( n log ⁡ n ) O(n\log n) O(nlogn) O ( n log ⁡ n ) O(n\log n) O(nlogn) O ( n 2 ) O(n^2) O(n2) O ( log ⁡ n ) O(\log n) O(logn)In-place不稳定
堆排序 O ( n log ⁡ n ) O(n\log n) O(nlogn) O ( n log ⁡ n ) O(n\log n) O(nlogn) O ( n log ⁡ n ) O(n\log n) O(nlogn) O ( 1 ) O(1) O(1)In-place不稳定
基数排序 O ( n × k ) O(n\times k) O(n×k) O ( n × k ) O(n\times k) O(n×k) O ( n × k ) O(n\times k) O(n×k) O ( n + k ) O(n+k) O(n+k)Out-place稳定
桶排序 O ( n + k ) O(n+k) O(n+k) O ( n + k ) O(n+k) O(n+k) O ( n 2 ) O(n^2) O(n2) O ( n + k ) O(n+k) O(n+k)Out-place稳定
计数排序 O ( n + k ) O(n+k) O(n+k) O ( n + k ) O(n+k) O(n+k) O ( n + k ) O(n+k) O(n+k) O ( k ) O(k) O(k)Out-place稳定

1. 冒泡排序

//冒泡排序及优化算法
public int[] bubble(int[] arr) {
	int len = arr.length;
	boolean flag;
	int temp;
	for(int i=1; i<len; i++) {
		flag = false;
		for(int j=0; j<len-i; j++) {
			if(arr[j] > arr[j+1]) {
				flag = true;
				temp = arr[j];
				arr[j] = arr[j+1];
				arr[j+1] = temp;
			}
		}
		//如果在一趟排序中没有交换过数据的位置,说明已完成排序,跳出循环
		if(!flag) {
			break;
		}
	}
	return arr;
}

2. 选择排序

//选择排序
public static int[] select(int[] arr) {
	int len = arr.length;
	int k,temp;
	for(int i=1; i<len; i++) {
		k = 0;
		for(int j=0; j<len-i; j++) {
			if(arr[k] < arr[j+1]) {
				k = j+1;
			}
		}
		if(k != len-i) {
			temp = arr[k];
			arr[k] = arr[len-i];
			arr[len-i] = temp;
		}
	}
	return arr;
}

3. 直接插入排序

//直接插入排序
public static int[] insert(int[] arr) {
	int len = arr.length;
	int k,temp;
	for(int i=1; i<len; i++) {
		k = i-1;
		temp = arr[i];
		while(k>=0 && arr[k]>temp) {
			arr[k+1] = arr[k];
			k--;
		}
		arr[k+1] = temp;
	}
	return arr;
}

4. 希尔排序

要点1:值得注意的一点是第二个for循环的意思是每个组轮流排序,而不是排完一组再排下一组。

要点2:while循环中要隔一个gap的距离移位。

//希尔排序,移位式
public static int[] shell2(int[] arr) {
	int len = arr.length;
	int temp,j;
	for(int gap = len/2; gap>0; gap /= 2) {
		for(int i=gap; i<len; i++) {
			j = i - gap;
			temp = arr[i];
			while(j>=0 && arr[j] > temp) {
				arr[j+gap] = arr[j];
				j -= gap;
			}
			arr[j+gap] = temp;
		}
	}
	return arr;
}

5. 快速排序

要点1:从小到大排序时,当选择数组中第一个数为基点时,要先移动右指针再移动左指针,这样可以保证两个指针相交时的数据小于基点数据。相反,以最后一个数据为基点时,先移动左指针再移动右指针,可以保证两个指针相交时的数据大于基点数据。总之,选择两端数据为基点时,要先移动离基点远的数据,从大到小排序是也一样。

要点2:第二层两个while循环中判断语句不要忘记等号,因为不需要对基点数据进行判断,加上等号可以跳过基点数据。

//快速排序
public static void quick(int[] arr, int left, int right) {
	if(left >= right) {
		return ;
	}
	int t;
	int i = left;
	int j = right;
	int temp = arr[left];
	while(i < j) {
		while(i < j && arr[j] >= temp) {
			j--;
		}
		//不要忘记等号
		while(i < j && arr[i] <= temp) {
			i++;
		}
		if(i < j) {
			t = arr[i];
			arr[i] = arr[j];
			arr[j] = t;			}
	}
	arr[left] = arr[i];
	arr[i] = temp;
	quick(arr, left, i-1);
	quick(arr, i+1,right);
}

6. 归并排序

//分
public static void merge(int[] arr, int left, int right, int[] temp) {
	if(left < right) {
		int mid = (left + right) / 2;
		merge(arr, left, mid, temp);
		merge(arr, mid+1, right, temp);
		mergeSort(arr, left, mid, right, temp);
	} 
}
//两个有序数列的合并,
public static void mergeSort(int[] arr, int left, int mid, int right, int[] temp) {
	int i = left;
	int j = mid + 1;
	int t = 0;
	while(i <= mid && j<=right) {
		if(arr[i] <= arr[j]) {
			temp[t++] = arr[i++];
		}else {
			temp[t++] = arr[j++];
		}
	}
	while(i <= mid) {
		temp[t++] = arr[i++];
	}
	while(j <= right) {
		temp[t++] = arr[j++];
	}
	t = 0;
	while(left <= right) {
		arr[left++] = temp[t++];
	}
}

7. 堆排序

//堆排序
public static void heapSort(int[] arr) {
	//经过这一轮循环后,调整为大顶堆
	for(int i=arr.length/2-1; i>=0; i--) {
		//从最后一个非叶子节点开始,从右向左,从下到上
		ajustHeap(arr, i, arr.length);
	}
	int temp;
	//将堆顶元素放到数组最后
	for(int i=arr.length-1; i>0; i--) {
		temp = arr[i];
		arr[i] = arr[0];
		arr[0] = temp;
		ajustHeap(arr, 0, i);
	}
}
/**
 * 将以i为根节点的二叉子树调整为大顶堆
 * @param arr 目标数组
 * @param i 要调整的位置
 * @param length 要调整的数组长度
 */
public static void ajustHeap(int[] arr, int i, int length) {
	int temp = arr[i];
	for(int k=i*2+1; k<length ;k=k*2+1) {
		if(k+1 < length && arr[k+1] > arr[k]) {
			k++;
		}
		if(arr[k] > temp) {
			arr[i] = arr[k];
			i = k;
		}else {
			//如果arr[i]比两个子节点的值要大,说明已完成调整
			break;
		}
	}
	
	arr[i] = temp;
}

8. 基数排序

//基数排序
public static void radix(int[] arr) {
	int len = arr.length;
	int max = arr[0];
	for(int i=1; i<len; i++) {
		if(arr[i] > max) {
			max = arr[i];
		}
	}
	int maxLen = (max + "").length();
	int[][] bucket = new int[10][len];
	//记录每个桶中数据的个数
	int[] bucketCount = new int[10];
	int temp;
	int index = 0;
	for(int i=0, n=1; i<maxLen; i++, n *= 10) {
		//将数据放到桶里
		for(int j=0; j<len; j++) {
			temp = arr[j] / n % 10;
			bucket[temp][bucketCount[temp]] = arr[j];
			bucketCount[temp]++;
		}
		index = 0;
		//将桶中数据放到数组中
		for(int j=0; j<10; j++) {
			for(int k=0; k<bucketCount[j]; k++) {
				arr[index++] = bucket[j][k];
			}
			bucketCount[j] = 0;
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值