常用排序方法

 

排序方法

时间复杂度

 

 

 

平均情况

最坏情况

最好情况

空间复杂度

稳定性

复杂性

Bubble

O(n*n)

O(n*n)

O(n)

O(1)

稳定

简单

Select

O(n*n)

O(n*n)

O(n*n)

O(1)

不稳定

简单

Merge

O(n*logn)

O(n*logn)

O(n*logn)

O(n)

稳定

较复杂

Quick

O(n*logn)

O(n*n)

O(n*logn)

O(n*logn)

不稳定

较复杂

Radix

O(n*d)

O(n*d)

O(n*d)

O(n*d)

稳定

较复杂


1,冒泡排序:

基本思想:比较相邻的元素,如果符合条件换位,一次比较完毕后,最值像气泡一样到了到了最下面。

稳定性:排序过程只需要交换相邻元素的位置,当相等时没必要交换位置,所以是稳定的。

Java代码:

public void bubbleSort(int[] arr){
	for(int i=0; i<arr.length-1; i++){
		//j<arr.length-1-i: 减一为了控制数组越界,减i为了下次比较时,比上一回少一次
		for(int j=0; j<arr.length-1-i; j++){
			//相邻两个元素进行比较,满足条件的最值往下沉
			if(arr[j] > arr[j+1]){
				swap(arr, j, j+1);
			}
		}
	}
}


对于冒泡算法,如果数据量比较大,而数据存在堆内存中,如果每次都交换位置,可能影响性能,因此不必每次都交换位置,当

一轮比较完毕后才交换位置,这里提供一种改进的方法:

/*
 * 在堆内存中每次进行交换的时候比在耗时
 * 如果元素个数比较多,这样性能会受影响
 * 解决办法是:当一轮回比较完毕后才把两个元素的值交换
 */
public void improvedBubbleSort(int[] arr){
	int index = 0;
	for(int i=0; i<arr.length-1; i++){
		for(int j=0; j<arr.length-i; j++){
			if(j==0){
				index = (arr[j] > arr[j+1]) ? j : j+1;
			}
			else if(arr[j] > arr[index]){
				index = j;
			}
			//每一轮回比较完毕后,才开始交换位置
			if(j==arr.length-1-i){
				swap(arr, j, index);
			}
		}
	}
}



2,选择排序:

基本思想:将第一个元素分别和后面的每一个元素进行比较,找到最值后,交换第一个元素和最值的位置

稳定性:每次选择的是未排序序列中最值和第一个元素交换,跨距离了,可能破坏稳定性,所以不稳定。

java代码:

public void selectedSort(int[] arr){
	for(int i=0; i<arr.length-1; i++)
		for(int j=i+1; j<arr.length; j++){
			//每一轮回开始时,把第一个元素分别后后面的每一个元素进行比较
			//第一个元素保存的是最值
			if(arr[j] < arr[i]){
				/*int temp = arr[j];
				arr[j] = arr[i];
				arr[i] = temp;*/
				swap(arr, i, j);
			}
		}
}

3,归并排序:

基本思想:将一个无序序列从中间划开,对前半部分归并排序,对后半部分归并排序,然后将这两个有序序列(分别是[start,mid]和[mid+1,end])合并成一个有序序列。

稳定性:归并排序会利用递归方法,递归条件结束是每个序列只有一个元素,所以他也是相邻元素进行比较,因此稳定

Java代码:

public void mergeSort(int [] arr, int start, int end){
	if(start < end){
		int mid = (start + end)/2;
		mergeSort(arr, start, mid);
		mergeSort(arr, mid+1, end);
		merge(arr, start, mid, end);
	}
}
/*
 * 数组的前半部分和后半部分有序
 * 将这个数组的合并成一个有序数组
 */
public void merge(int[] arr, int start, int mid, int end){
	int i=start, j = mid+1, k = 0;
	int[] tempArr = new int[end-start+1];
	
	//将两个有序的数组合并成一个有序的数组
	while(i <= mid && j <= end){
		if(arr[i]>arr[j])
			tempArr[k++] = arr[j++];
		else
			tempArr[k++] = arr[i++];
	}
	//将前半部分剩下的元素加到tempArr末尾
	while(i<=mid){
		tempArr[k++] = arr[i++];
	}
	//将后半部分剩下的元素加到tempArr末尾
	while(j<=end){
		tempArr[k++] = arr[j++];
	}
	printArray(tempArr);
	//将有序的临时数组元素赋值到arr数组中
	for(i=start, j=0; i<=end; i++,j++){
		arr[i] = tempArr[j];
	}
}

4,快速排序:

基本思想:是冒泡排序的一种改进,待排序序列中任选一个中枢值(pivotKey),一般是第一个元素,比这个中枢值小的都在他的前面,比他大的都在后面;然后分别对前后两个无序序列继续使用快速排序递归调用,直到有序。

稳定性:快速排序虽然是冒泡算法的一种改进,却破坏了冒泡算法的稳定性,因为他可能存在跨距离交换元素位置,因此不稳定

Java代码:

public void quickSort(int[] arr, int low, int high){
	if(low < high){
		int pivotIndex = partition(arr, low, high);
		quickSort(arr, low, pivotIndex-1);
		quickSort(arr, pivotIndex+1, high);
	}
}
int partition(int[] arr, int low, int high){
	int pivotValue = arr[low];
	
	while(low < high){
		//从后往前遍历,直到找到比中枢值小的元素
		while(low < high && arr[high]>=pivotValue) high--;
		//将比中枢值小的元素放到前面去,即arr[low]所在的位置
		arr[low] = arr[high];
		//从前往后遍历,直到找到比中枢值大的元素
		while(low < high && arr[low]<=pivotValue) low++;
		//将比中枢值大的元素放到后面去,即arr[high]所在的位置
		arr[high] = arr[low];
	}
	//找到了中枢值的位置,即low(或者high)
	arr[low] = pivotValue;
	
	return low;
}

5,基数排序:

基本思想:将无序序列按关键字分别进行排序,最终得到有序序列。比如对一个无序的十进制数进行排序,先对个位排序,在个位有序的基础上对十位排序,以此类推下去。

稳定性:基数排序不会交换元素的位置,因此是稳定的

Java代码:

public void radixSort(int[] arr, int radix, int distance){
<pre name="code" class="java">        //distance表示待排序列最大的位数
        //radix表示基数(i.e 十进制就是10)
//count数组用来计算arr数组中每一个元素在数组的位置,以达到对每一关键字(i.e 按个位)排序int count[] = new int[radix];int[] tempArr = new int[arr.length];int subKey;int divide = 1;for(int j=0; j<distance; j++){//每次对subKey进行排序的时候都要对count数组清零Arrays.fill(count, 0);//下一次排序基于上一次个位排序好的数组System.arraycopy(arr, 0, tempArr, 0, arr.length);//统计每一个关键字(i.e 个位,十位...)的个数for(int i=0; i<tempArr.length; i++){subKey = (tempArr[i]/divide)%radix;count[subKey]++;} //因为count的值会用来当着关键字在数组中的位置//所以要保证比它小的关键字要在他的前面,比如数组中有10,20,30,21,21//那么count[0]=3,count[1]=2,将count[1]赋值成5,那么就可以保证以个位为1的元素一定//在个位为0的后面,从而达到排序的功能for(int i=1; i<radix; i++){count[i] = count[i] + count[i-1];}//对subKey进行排序for(int i=tempArr.length-1; i>=0; i--){subKey = (tempArr[i]/divide)%radix;//count每一个元素保存的是subKey的个数,计算其在数组arr中的位置需要减一count[subKey]--;arr[count[subKey]] = tempArr[i];}divide *= radix;}}
 

上述基数排序没有考虑负数的情况,进行改进:

public void radixSort(int[] arr, int radix, int distance){
	//distance表示待排序列最大的位数
        //radix表示基数(i.e 十进制就是10)
	//count数组用来计算arr数组中每一个元素在数组的位置,以达到对每一关键字(i.e 按个位)排序
	int count[] = new int[radix];
	//negativVount用来计算arr数组中负数在数组中的位置
	int[] negativCount = new int[radix];
	int[] tempArr = new int[arr.length];
	int subKey;
	int divide = 1;
	
	for(int j=0; j<distance; j++){
		//每次对subKey进行排序的时候都要对count数组清零
		Arrays.fill(count, 0);
		Arrays.fill(negativCount, 0);
		//下一次排序基于上一次个位排序好的数组
		System.arraycopy(arr, 0, tempArr, 0, arr.length);
		
		//统计每一个关键字(i.e 个位,十位...)的个数
		for(int i=0; i<tempArr.length; i++){
			subKey = (tempArr[i]/divide)%radix;
			if(tempArr[i] < 0){
				negativCount[radix-1+subKey]++;
			}
			else{
				count[subKey]++;
			}
		}
		 
		//因为count的值会用来当着关键字在数组中的位置
		//所以要保证比它小的关键字要在他的前面,比如数组中有10,20,30,21,21
		//那么count[0]=3,count[1]=2,将count[1]赋值成5,那么就可以保证以个位为1的元素一定
		//在个位为0的后面,从而达到排序的功能
		for(int i=1; i<radix; i++){
			negativCount[i] = negativCount[i] + negativCount[i-1];
		}
		//如果按照升序排列的话,负数出现的位置一定在正数前面
		count[0] += negativCount[radix-1];</span>
		for(int i=1; i<radix; i++){
			count[i] = count[i] + count[i-1];
		}
		//对subKey进行排序
		for(int i=tempArr.length-1; i>=0; i--){
			subKey = (tempArr[i]/divide)%radix;
			//count每一个元素保存的是在subKey前面有多少别的subKey的个数,计算其在数组arr中的位置需要减一
			if(tempArr[i] < 0){
				negativCount[radix-1+subKey]--;
				arr[negativCount[radix-1+subKey]] = tempArr[i];
			}
			else{
				count[subKey]--;
				arr[count[subKey]] = tempArr[i];
			}
			
		}
		
		divide *= radix;
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值