排序方法 | 时间复杂度 |
|
|
| ||
平均情况 | 最坏情况 | 最好情况 | 空间复杂度 | 稳定性 | 复杂性 | |
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;
}
}