常见排序算法比较
常见的排序算法可以分为以下几种:
- 冒泡排序(Bubble Sort):重复遍历待排序的序列,比较相邻的两个元素大小,如果前一个元素比后一个元素大,则交换它们的位置,直到整个序列排序完成为止。
- 选择排序(Selection Sort):重复遍历待排序的序列,每次选择一个最小的元素,放到已排序的序列的末尾,直到整个序列排序完成为止。
- 插入排序(Insertion Sort):将待排序的序列分为已排序和未排序两个部分,从未排序的部分中取出第一个元素,将它插入到已排序部分的合适位置,直到整个序列排序完成为止。
- 快速排序(Quick Sort):选择一个基准元素,将序列中小于基准元素的放在它的左边,大于基准元素的放在它的右边,再递归地对左右两个子序列进行同样的操作,直到整个序列排序完成为止。
- 归并排序(Merge Sort):将待排序的序列分成两个部分,对每个部分递归地进行归并排序,然后将两个有序的子序列合并成一个有序的序列。
- 堆排序(Heap Sort):将待排序的序列构建成一个最大堆或最小堆,每次从堆中取出堆顶元素(最大值或最小值),然后重新构建堆,直到整个序列排序完成为止。
- 计数排序(Counting Sort):计数排序是一种非比较排序算法,它的基本思想是统计序列中每个元素出现的次数,然后根据每个元素的计数值来排序。
- 桶排序(Bucket Sort):桶排序是一种非比较排序算法,它的基本思想是将序列中的元素分配到若干个桶中,然后对每个桶中的元素进行排序,最后按照桶的顺序将所有桶中的元素依次连接起来,形成一个有序序列。
- 基数排序(Radix Sort):基数排序是一种非比较排序算法,它的基本思想是按照低位到高位的顺序,对序列中的元素进行排序。具体地,先根据元素的个位数字进行排序,然后根据十位数字进行排序,以此类推,直到最高位排序完成为止。
| 排序算法 | 平均时间复杂度 | 最优时间复杂度 | 最差时间复杂度 | 空间复杂度 | 排序方式 |
|---|---|---|---|---|---|
| 冒泡排序 | O(n^ | O(n) | O(n^2) | O(1) | In- |
| 选择排序 | O | O(n^2) | O(n^2) | O(1) | In |
| 插入排序 | O | O(n) | O(n^2) | O(1) | In- |
| 快速排序 | O(nlogn) | O(nlogn) | O(n^2) | O(logn) | In- |
| 归并排序 | O(nlogn | O(nlogn | O(nlogn) | O(n) | Out- |
| 堆 | O(nlogn) | O(nlogn) | O(nlogn) | O(1) | In- |
| 计 | O(n+k) | O(n+k) | O(n | O(n+k) | Out- |
| 桶排序 | O(n+k) | O(n) | O(n^2) | O | Out- |
| 基 | O(d*(n+k)) | O(d*(n | O(d*(n+k)) | O(n+k) | Out- |
在排序算法中,如果经过排序后,两个相等的元素在排序前的相对位置没有发生改变,那么这个排序算法就是稳定排序算法。反之,如果经过排序后,两个相等的元素在排序前的相对位置发生了改变,那么这个排序算法就是不稳定排序算法。
| 排序算法 | 稳定性 |
|---|---|
| 冒 | 稳 |
| 选择排 | 不 |
| 插入排序 | 稳 |
| 快 | 不 |
| 归并 | 稳 |
| 堆排序 | 不稳定 |
| 计数排序 | 稳定 |
| 桶排序 | 稳定 |
| 基数排序 | 稳定 |
具体算法举例
冒泡排序
/**
* 一旦某一轮循环后一次调换都没有发生,证明已经有序
*/
//冒泡优化1
public static void bubbleSort(int[] array){
if(array!=null&&array.length<2){
return;
}
Boolean flag=false;
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]){
flag= swopBubble(array,j,j+1);
}
}
if(flag==false){
break;
}
}
}
插入排序
/**
* j固定在i的上一位,边界+条件 交换 左至右,首次大循环只看一次
*/
public static void insertSort(int[] array){
if(array!=null&&array.length<2){
return;
}
//j固定在i的上一位,边界+条件 交换 --
for (int i = 1; i < array.length; i++) {
for (int j = i-1; j >=0&&array[j]>array[j+1] ;j--) {
swop(array,j,j+1);
}
}
}
快速排序
/**
*
* 快排思路 portion
* 当前数小于目标目标数,和小于区做交换,小于区右扩,当前位置跳下一个
* 当前数等于目标数 直接跳下一个
* 当前数大于目标,和大于区前一个做交换,大于区左扩 当前位置不变
*
*/
public class QuickSort {
public static void quick(int l,int r,int []arr){
if(l>=r){ //比如 小于区没有右扩导致edges[0]=-1
return;
}
int[] edges = quickPortion(l, r, arr);//edges 小于区最后一个 和 大于区第一个
quick(l,edges[0],arr);
quick(edges[1],r,arr);
}
public static int[] quickPortion(int l,int r,int[]arr){
int less=l-1;
int more=r;
int cur=l;
//从l到r上随机选一个值作为目标值,从而在数学期望上达到时间复杂度log(n)
int random=(int)(Math.random()*(r-l+1))+l;
swap(r,random,arr);
while (cur<more){
if(arr[cur]<arr[r]){
swap(++less,cur++,arr);
}else if(arr[cur]==arr[r]){
cur++;
}else {
swap(--more,cur,arr);
}
}
swap(r,more,arr);
return new int[]{less,more+1};
}
归并排序
/**
* MergeSort 流程
* f函数 递归调用
* 1求M 2左递归 3右递归 4Merge
* Merge 函数
* 1l->m m+1->r 共同范围内
* 2l->m 范围内 /m+1->r 范围内
* 3help数组替换
*/
public static void mergeSort1(int l, int r, int[] arr) {
if (l >= r) {
return;
}
int m = l + ((r - l) >> 1);
mergeSort1(l, m, arr);
mergeSort1(m + 1, r, arr);
merge(l, m, r, arr);
}
public static void merge(int l, int m, int r, int[] arr) {
int length = r - l + 1;
int[] help = new int[length];
int left = l;
int right = m + 1;
int index = 0;
while (left <= m && right <= r) {
//从l 到r 以 m分为两组,各自从左向右 谁小先拷贝谁
help[index++] = arr[left] <= arr[right] ? arr[left++] : arr[right++];
}
while (left <= m) { //right越界
help[index++] = arr[left++];
}
while (right <= r) {//left越界
help[index++] = arr[right++];
}//merge后 拷贝回原数组
System.arraycopy(help, 0, arr, l, length);
}
加强堆及堆排序
package review;
import java.util.*;
/**
* 加强堆排序
* 经典操作 heapyIfy 和 heapInsert
*
* @author wangyiming
*/
public class Heap<T> {
public ArrayList<T> heap;
public HashMap<T, Integer> indexMap;
public int heapSize;
public Comparator<? super T> comparator;
public Heap(Comparator<? super T> comparator) {
this.comparator = comparator;
heapSize = 0;
heap = new ArrayList<>();
indexMap = new HashMap<>();
}
//is empty size contains peek
public boolean isEmpty() {
return heap.isEmpty();
}
public int size() {
return heap.size();
}
public boolean contains(T key) {
return indexMap.containsKey(key);
}
public T peek() {
return heap.get(0);
}
public void heapInsert(int index) {
//这里 如果当index向上跳 跳到了root节点,由于两者比较==0 也会停止循环
while (comparator.compare(heap.get(index), heap.get((index - 1) / 2)) > 0) {
swap(index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
public void heapIfy(int index) {
int left = 2 * index + 1;
while (left < heapSize) {
//largest 是left 和 right 中的大的那个
int largest = left + 1 < heapSize ? (comparator.compare(heap.get(left), heap.get(left + 1)) > 0 ? left : left + 1) : left;
if(comparator.compare(heap.get(index),heap.get(largest))>0){
//最大的是夫节点
break;
}
swap(index,largest);
index=largest;
left=2*index+1;
}
}
public void arrayHeapIfy(int[]arr,int index,int heapSize){
int left=2*index+1;
while (left<heapSize){
int largest=left+1<heapSize && arr[left+1]>arr[left]?left+1:left;
largest=arr[largest]>arr[index]?largest:index;
if(largest==index){
break;
}
swap(arr,index,largest);
index=largest;
left=2*index+1;
}
}
public void swap(int[]arr,int i,int j){
int t=arr[i];
arr[i]=arr[j];
arr[j]=t;
}
public void swap(int i, int j) {
T t1 = heap.get(i);
T t2 = heap.get(j);
indexMap.put(t1, j);
indexMap.put(t2, i);
heap.set(i, t2);
heap.set(j, t1);
}
public T pop(){
T t=heap.get(0);
//头尾互换
swap(0,heapSize-1);
indexMap.remove(t);
heap.remove(--heapSize);
heapIfy(0);
return t;
}
public void resign(T obj){
heapIfy(indexMap.get(obj));
heapInsert(indexMap.get(obj));
}
public void push(T obj){
heap.add(obj);
indexMap.put(obj,heapSize);
heapInsert(heapSize++);
}
public void remove(T obj){
Integer index = indexMap.get(obj); //要删除的位置
T t = heap.get(--heapSize); //末尾的值
indexMap.remove(obj); //删除目标的索引
heap.remove(heapSize); //删除末尾
if(t!=obj){ //如果要删除的就是最后一个,就不用调整了
indexMap.put(t,index); //t插入到index
heap.set(index,t);
resign(t);
}
}
public static void main(String[] args) {
Heap<Integer> heap=new Heap<>((o1, o2) -> o1-o2);
int []arr={1,2,6,7,2,4,8,1};
//构建堆结构,⚠️堆结构直接打印还是无序 应该使用pop一次弹出才有序
for (int i = arr.length-1; i>=0; i--) {
heap.arrayHeapIfy(arr,i,arr.length);
}
//根据堆->做堆排序
int heapSize=arr.length;
while (heapSize>0){
heap.swap(arr,0,--heapSize);//大顶堆,每次把最大的放到最后,再对新的堆做调整,排序结果由小到大
heap.arrayHeapIfy(arr,0,heapSize);
}
System.out.println(Arrays.toString(arr));
}
}
计数排序
/**基于非比较排序 计数排序
* 对于给定范围的一些数 这个范围不能太大 创建help 数组从 【rangeMin rangeMax】
*遍历原数组 每遇到一个数,在 help数组上+1 然后基于help数组再排序
*/
public static void countSort(int[] arr,int rangeMin,int rangeMax){
int[] help=new int[rangeMax-rangeMin+1];
for (int i = 0; i < help.length; i++) {
help[i]=0;
}
for (int i = 0; i < arr.length; i++) {
help[arr[i]-rangeMin]++;
}
int i=0;
int j=0;
while(i<arr.length && j<help.length){
while ((help[j])>0){
arr[i++]=j;
help[j]--;
}
j++;
}
}