Java 实现各种排序算法以及性能分析

排序算法是编程中最常用的算法

下面将一一实现并分析各种排序算法

冒泡排序:

算法

private static void sort(Comparable[] a){
        int length=a.length;
        for(int i = 0;i<length;i++){
            for(int j = 1;j<length-i;j++){
                if(less(a[j],a[j-1])){
                    exch(a,j-1,j);
                }
            }
        }

    }
    注:exch(a,j-1,j):交换a[j-1],a[j]的位置
       less(a[j],a[j-1]) :比较a[j],a[j-1]的大小,前者小于后者则返回true,反之返回false【以下代码都一样,就不做重复解释】

每一次遍历都将最大的元素找出并放到最后,所以冒泡从而得名。复杂度可以说是是有点高了,但是冒泡排序很稳定.
冒泡排序:时间复杂度为O(n^2),

选择排序:

这种排序比较简单,每次找出最小或者最大的即可,就不多解释,直接贴出代码:

private static void selectSort(Comparable[] a){
        int length = a.length;
        for(int i = 0;i<length;i++){
            int min = i;
            for(int j = i+1;j<length;j++){
                if(less(a[j],a[min]))
                    min = j;
                exch(a,i,min);
            }
        }
    }

插入排序:

  • 直接插入排序:
    先看算法
 public static void insertSort(Comparable[] a){
        int length = a.length;
        for(int i = 1; i<length; i++){
            for(int j = i;j>0 && less(a[j],a[j-1]);j--){//保证[0-i]有序。可以采用折半插入减少一定的比较次数(下面会讲,这里先用原始的一个一个元素的比较)
                exch(a, j, j-1);
            }
        }
    }
  • 折半插入排序:
    当要插入的序列是有序时,我们可以采用折半插入排序来插入,可以很大程度上减少运算次数,下面我对上面的直接插入排序算法进行改进
public static void binaryInsertSort(int[] data) {
        for (int i = 1; i < data.length; i++) {
            if (data[i] < data[i - 1]) {
                // 缓存i处的元素值
                int tmp = data[i];
                // 记录搜索范围的左边界
                int low = 0;
                // 记录搜索范围的右边界
                int high = i - 1;
                while (low <= high) {
                    // 记录中间位置
                    int mid = (low + high) / 2;
                    // 比较中间位置数据和i处数据大小,以缩小搜索范围
                    if (data[mid] < tmp) {
                        low = mid + 1;
                    } else {
                        high = mid - 1;
                    }
                }
                //将low~i处数据整体向后移动1位
                for (int j = i; j > low; j--) {
                    data[j] = data[j - 1];
                }
                data[low] = tmp;
                print(data);
            }
        }
    }

堆排序

堆排序,我们需要引入一个新的概念:优先队列

许多应用程序都需要处理有序,但不一定要求他们都全部有序,或是不一定要一次就将他们排序。很多情况下,我们会收集一些,处理当前键值最大的元素,然后再收集更多的元素,再处理当前键值最大的元素,如此这般。例如,你有可能有一台同时运行多个应用程序的电脑。这是通过为每个应用程序的事件分配一个优先级,,并总是处理下一个优先级最高的事件来实现。此时,并可以引用优先队列这种数据结构来实现:只支持两种操作:删除最大元素和插入元素。

优先队列可以采用数组或者链表来实现(利用栈的特性)
算法如下:

public class Solution {

    Stack<Integer> dataStack = new Stack<>();//数据栈
    Stack<Integer> minStack = new Stack<>();//辅助栈,每次将最小和次小的元素存在此栈中
    public void push(int node) {
        dataStack.push(node);
        //if(minStack.isEmpty()||minStack.peek()>node){
         //   minStack.push(node);
        //}else {
          //  minStack.push(minStack.top());
        //}
        minStack.push(minStack.isEmpty() ? node : Math.min(minStack.peek(),node));
    }

    public void pop() {
        dataStack.pop();
        minStack.pop();
    }

    public int top() {
        //peek()返回栈顶的值但不删除pop()会删除
        return dataStack.peek();
    }

    public int min() {
        return minStack.peek();
    }
}

通过上面的描述,我们发现操作起来还是不很流畅,插入和删除需要线性时间才能完成,这还是不够快,因此,我们引入新的数据结构–堆。我们构建基于二叉堆的优先队列
先来看看堆的两种算法:
- 下沉

//从上至下的下沉算法
private void sink(int k){
        while(2*k <= N){//
            int j = 2*k;
            if(j<N && less(j,j+1)) j++;
            if(!less(k,j)) break;
            k=j;
        }
    }

上浮算法

 private void swim(int k){
        while(k>1 && less(k/2,k)){
            swap(k/2,k);
            k = k/2;
        }
    }

基于堆的优先队列:

public class Heap<T extends Comparable<T>> {
    private T[] heap;
    private int N=0;

    public Heap(int maxN) {
        this.heap = (T[]) new Comparable[maxN+1];
    }
    public boolean isEmpty() {
        return N==0;

    }
    public int size() {
        return N;
    }

    private boolean less(int i,int j){
        return heap[i].compareTo(heap[j])<0;

    }
    private void swap(int i, int j){
        T t = heap[i];
        heap[i] = heap[j];
        heap[j] = t;
    }

    private void swim(int k){
        while(k>1 && less(k/2,k)){
            swap(k/2,k);
            k = k/2;
        }
    }

    private void sink(int k){
        while(2*k <= N){
            int j = 2*k;
            if(j<N && less(j,j+1)) j++;
            if(!less(k,j)) break;
            k=j;
        }
    }

    /**
     * 向二叉堆里插入新元素
     * @param v
     */
    public void insert(Comparable v){
        heap[++N] = (T) v;
        swim(N);
    }

    /**
     * 删除最大元素
     * @return
     */
    public T delMax() {
        T max = heap[1];
        swap(1,N--);
        heap[N+1] = null;
        sink(1);
        return max;
    }



}

有了堆的优先队列,我们就来看看堆排序的实

 public static void heapSort(Comparable[] a){
        int length = a.length-1;
        for(int k = length/2;k>=1;k--){//下沉排序
            sink(a, k ,length);
        }
        while (length > 1) {
            exch(a,1,length--);
            sink(a,1,length);
        }
    }

快速排序:快速排序是一种分治的思想

先来看看快速排序切分的思想:

 private static int partition(Comparable[] a,int start,int end){
        int i = start,j=end+1;
        Comparable p = a[start];
        while (true) {
            while(less(a[++i],p) && i != end);//从后往前扫
            while(less(p,a[--j]) && j != start);//从前往后扫
            if (i >=j)
                break;
            exch(a,i,j);

        }
        exch(a,start,j);
        return j;
    }

快速排序

private static void quickSort(Comparable[] a,int start,int end){
        if(end <= start)
            return ;
        int j = partition(a,start,end);
        quickSort(a,start,j-1);
        quickSort(a,j+1,end);
    }

希尔排序

希尔排序又叫“`缩小增量排序。其本质其实还是插入排序,但是这里选取了一个增量,来加速排序进程。

public static void shellSort(Comparable[] a ){
    int length = a.length;//eg.a={1,2,3,52,21,11,5,9,15}
    int h = 1;//增量
    while(h<length/3)
        h = h*3+1;//h=4
    while(h>=1)
{
    //将数组有序
    for(int i=h;i<length;i++){
        for(int j=i;j>=h && less(a[j],a[j-h]);j-=h){
            exch(a,j,j-h)//对增量h有序化
            }
        }
        h = h/3;//h=4/3=1
    }
}

归并排序:

归并排序是指将每种排序分别排序,然后不断选取合适的范围进行整合排序,这里采用了动态规划的分治思想:分而治之。

 private static void merge(Comparable[] a,int start,int m,int end){
        int i = start,j = m + 1;
        for (int k = start; k <= end; k++){
            aux[k] = a[k];
        }
        for (int k = start; k <= end; k++){
            if(i> m) a[k] = aux[j++];
            else if(j>end) a[k] = aux[i++];
            else if(less(aux[j],aux[i])) a[k] = aux[j++];
            else a[k] = aux[i++];
        }
    }
    private static void mergeSort(Comparable[] a, int start,int end){
        if (end <= start) return;
        int m = start + (end-start)/2;
        mergeSort(a,start,m);
        mergeSort(a,m+1,end);
        merge(a,start,m,end);
    }

桶排序:

这是一个比叫有趣的排序,也称基数排序。基数排序的思想是:“多关键字排序”

public static void bucketSort(int[] arr){
    //分桶,这里采用映射函数f(x)=x/10。
    //输入数据为0~99之间的数字
    int bucketCount =10;
    Integer[][] bucket = new Integer[bucketCount][arr.length];  //Integer初始为null,以与数字0区别。
    for (int i=0; i<arr.length; i++){
        int quotient = arr[i]/10;   //这里即是使用f(x)
        for (int j=0; j<arr.length; j++){
            if (bucket[quotient][j]==null){
                bucket[quotient][j]=arr[i];
                break;
            }
        }
    }
    //小桶排序
    for (int i=0; i<bucket.length; i++){
            //insertion sort
            for (int j=1; j<bucket[i].length; ++j){
                if(bucket[i][j]==null){
                    break;
                }
                int value = bucket[i][j];
                int position=j;
                while (position>0 && bucket[i][position-1]>value){
                    bucket[i][position] = bucket[i][position-1];
                    position--;
                }
                bucket[i][position] = value;
            }

    }
    //输出
    for (int i=0, index=0; i<bucket.length; i++){
        for (int j=0; j<bucket[i].length; j++){
            if (bucket[i][j]!=null){
                arr[index] = bucket[i][j];
                index++;
            }
            else{
                break;
            }
        }
    }
}

排序算法总结:

算法稳定时间复杂度空间复杂度备注
选择排序noN21
冒泡排序yesN21
插入排序yesN \~ N21时间复杂度和初始顺序有关
希尔排序noN 的若干倍乘于递增序列的长度1
快速排序noNlogNlogN
三向切分快速排序noN \~ NlogNlogN适用于有大量重复主键
归并排序yesNlogNN
堆排序noNlogN1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值