承接上篇文章,本文章主要为后续的三种排序:
- 冒泡排序
- 快速排序
- 归并排序
冒泡排序
冒泡排序的原理是:
通过遍历无序区间,比较相邻的数,将较大的放在后面,通过多段交换,使其达成一种无序区在前,有序区在后的形态,直至完全有序为止。
时间复杂度:
最好O(n) 平均O(n^2) 最坏O(n^2)
空间复杂度:O(1)
稳定性:稳定
代码实现:
private static void swap(int[] array, int i, int j) {//交换部分
int t = array[i];
array[i]= array[j];
array[j]= array[i];
}
public static void bubbleSort(int[] array){//冒泡部分
for (int i = 0;i<array.length-1;i++){
for (int j=0;j<array.length-i-1;j++){
if (array[j]>array[j+1]){
swap(array,j,j+1);
}
}
}
}
快速排序
快速排序属于分治排序,我们可以采用递归的思想,将一个无序的大数组,分割成若干个小数组,再对小数组进行排序,以达到整体有序的目的。
步骤:
1.在整个待排序区间中确认一个基准值
2.遍历整个待排序区间,将所有数据和基准值进行比较,最终实现:
比基准值小的在基准值的左边
比基准值大的在基准值的右边
3.用同样的方法去处理左右两个小的待排序区间,直到小的待排序区间内没有数据(size0)或者已经有序了(size1)为止
代码实现:
public static void quickSort(int[] array){//快速排序
quickSortInteral(array,0,array.length-1);
}
private static void quickSortInteral(int[] array, int left, int right) {
if (left>=right){
return;
}
int pivotindex = partition(array,left,right);
quickSortInteral(array,left,pivotindex-1);
quickSortInteral(array,pivotindex+1,right);
}
代码中的 partition 即为具体的处理方法,在此主要有这四种方法:
1.Hover 左右靠近,不满足条件则交换
2.挖坑 左右靠近,不满足条件则埋坑
3.前后下标 从左到右,满足条件则交换
4.会把和基准值相等的情况单独处理
代码如下:
private static int partition(int[] array, int left, int right) {
int pivot = array[right];
int less = left;
int great = right;
while (less<great){
while (less <great&&array[less]<=pivot){
less++;
}
while (less <great&&array[great]>=pivot){
great--;
}
swap(array,less,great);
}
swap(array,right,less);
return less;
}
private static int partition2(int[] array, int left, int right){
int pivot = array[right];
int less = left;
int great = right;
while (less<great){
while (less <great&&array[less]<=pivot){
less++;
}
array[great]=array[less];
while (less <great&&array[great]>=pivot){
great--;
}
array[less]=array[great];
}
array[less]=pivot;
return less;
}
public static int partition3(int[] array,int left,int right){
int blue = left;
int pivot = array[right];
for (int i = left;i<right;i++){
if(array[i]<pivot){
swap(array,blue,i);
blue++;
}
}
swap(array,blue,right);
return blue;
}
也可以用非递归的方式实现快速排序:
如何使用非递归实现快速排序?
使用栈记录,待排序区间的左右边界
public static void quickSortNoR(int[] array){
Stack<Integer> stack = new Stack<>();
stack.push(array.length-1);
stack.push(0);
while (!stack.isEmpty()){
int left =stack.pop();
int right = stack.pop();
int pivot = partition(array,left,right);
stack.push(right);
stack.push(pivot+1);
}
}
快速排序时间复杂度:
最好/平均 O(n*log(n))
最坏:O(n^2)
空间复杂度:O(N)
归并排序(合并排序)
合并两个有序数组的过程:
平均切分待排序区间,如果待排序区间的左右两个小区间有序了,则按照合并有序数组的方式使其最终有序。
这种方法是最常见的一种外部排序。
如果数据量大到内存中存放不下,就可以使用归并排序来处理
注:外部排序:不是所有的操作都需要借助内存的排序
代码如下:
public static void mergeSort(int[] array){
internal(array,0,array.length);
}
private static void internal(int[] array, int low, int high) {
if (low+1>=high){
return;
}
int mid = (low + high)/2;
internal(array,low,mid);
internal(array,mid,high);
merge(array,low,mid,high);
}
private static void merge(int[] array, int low, int mid, int high) {
int length = high - low;
int[] extra = new int[length];
int iLeft= low;
int iRight = mid;
int iE = 0;
while(iLeft<mid&&iRight<high){
if (array[iLeft]<=array[iRight]){
extra[iE++]=array[iLeft++];
}else {
extra[iE++]=array[iRight++];
}
}
while (iLeft<mid){
extra[iE++]=array[iLeft++];
}
while (iRight<high){
extra[iE++]=array[iRight++];
}
for (int i = 0;i < length;i++){
array[low+i] = extra[i];
}
}
时间复杂度 O(n*log(n)) 数据不敏感
空间福再度O(n)
稳定性:稳定
优化点:
减少搬移次数
总结
七大基于比较的排序
时间复杂度:
1.O(n^2) 插排/选择/冒泡
2.O(n*log(n))堆排/快排/归并
3.O(n^1.2)
空间复杂度
O(1)插排/选择/冒泡/希尔/堆排
O(log(n))快排
O(n) 归并
稳定性:
稳定: 插排/冒泡/归并
数据不敏感:选择/堆排/归并
最好O(n) 插排/冒泡