【算法】十种排序算法及其实现

1,按照时间复杂度分类

O(n^2):冒泡排序、选择排序、插入排序

O(n^2~nlogn):希尔排序

O(nlogn):快速排序(不稳定)、堆排序(稳定)、归并排序(稳定,需要额外空间)

O(n):基数排序、计数排序、桶排序

2,快速排序

分为单指针算法和双指针算法,我这里主推双指针,比较简洁;

有一点很重要,快排和归排都属于分而治之的算法,前者重分轻合,后者重合请分;

以下代码,是快速排序的实现,供参考;

package _算法._查找与排序2;

import org.junit.Test;

/***
 * 分治法:快排
 *
 * nlogn
 *
 * 工业优化:哨兵取三点中值法,规模较小取插入排序
 * ***/
public class _快速排序 {

    //双指针扫描
    private static void qSort(int[] arr, int start, int end) {
        int pivot = arr[start];  //哨兵
        int i = start;
        int j = end;
        while (i < j) {

            while ((arr[j] > pivot) && (i < j)) {  //找出第一个小的值
                j--;
            }
            while ((arr[i] < pivot) && (i < j)) {  //找出第一个大的值
                i++;
            }
            if ((arr[i] == arr[j]) && (i < j)) i++;  //相等,i++,没必要交换
            else {
                //交换
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
        if (i - 1 > start) qSort(arr, start, i - 1);
        if (j + 1 < end) qSort(arr, j + 1, end);
    }


    //单指针扫描
    private static void qSort2(int[] arr, int start, int end){
        if (start < end){
        int q  = partion(arr,start,end);
        qSort2(arr,start,q-1);
        qSort2(arr,q+1,end);
        }
    }

    private static int partion(int[] arr, int start, int end) {
        int pivot = arr[start];
        int sp = start+1;//扫描指针
        int bigger = end;//尾指针
        while (sp<=bigger){
            if (arr[sp]>pivot){
                int temp = arr[sp];
                arr[sp] = arr[bigger];
                arr[bigger] = temp;
                bigger--;
            }
            else sp++;
        }
        int temp = arr[start];
        arr[start] = arr[bigger];
        arr[bigger] = temp;
        return bigger;
    }
}

3,归并排序

归并排序重于合,最终子问题为两个元素的比较排序;

package _算法._查找与排序2;
/***
 *
 * 分治的完美诠释:归并排序
 *
 * 简分重合
 *
 * 分:数组中间
 * 合:两个有序数组的合并
 *
 * ***/
public class _归并排序 {
    private static void gSort(int[] arr, int[] brr, int start, int end) {
        if (start == end) {//出口
            return;
        }
        int mid = start + ((end - start) >> 1);//中点
        gSort(arr, brr, start, mid);
        gSort(arr, brr, mid + 1, end);
        //合并
        int left = start;//左指针
        int right = mid + 1;//右指针
        int k = 0;
        while (left <= mid && right <= end) {
            if (arr[left] <= arr[right]) {
                brr[k++] = arr[left++];
            } else {
                brr[k++] = arr[right++];
            }
        }
        while (left <= mid && right > end) {
            brr[k++] = arr[left++];
        }
        while (right <= end && left > mid) {
            brr[k++] = arr[right++];
        }
        System.arraycopy(brr, 0, arr, start, end - start + 1);
    }

}

4,堆排序

堆排序属于树状排序,因此时间复杂度为nlogn,通过维护最大堆或者最小堆来实现排序的逻辑;

package _算法._查找与排序2;


/***
 * 二叉树的结构
 * 步骤:1,乱序数组的堆化
 *      2,选出堆顶,数组减1
 *      3,调整堆
 *      反复
 * ***/
public class _堆排序 {
    private static void heapSort(int[] arr) {
        buildMinHeap(arr);//建最小堆
        selectTopToEnd(arr, arr.length - 1);//选取堆顶
        reserveArr(arr);
    }

    private static void reserveArr(int[] arr) {
        //顺序反转
        for (int i = 0, j = arr.length - 1; i < j; i++, j--) {
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }

    private static void selectTopToEnd(int[] arr, int end) {
        if (end == 0) return;
        int temp = arr[0];//交换最后与堆顶元素
        arr[0] = arr[end];
        arr[end] = temp;
        ReBuildMinHeap(arr, 0, end - 1, false);//重新调整堆
        selectTopToEnd(arr, end - 1);//递归实现
    }

    private static void buildMinHeap(int[] arr) {
        for (int i = arr.length / 2 - 1; i >= 0; i--) {
            ReBuildMinHeap(arr, i, arr.length - 1, true);//全局调整堆
        }
    }

    /***
     * @param arr:待排序数组
     * @param i:排序根节点起点
     * @param end:数组长度
     * @param first: 是否第一次建堆
     * ***/
    private static void rebuildMinHeap(int[] arr, int i, int end, boolean first) {
        int left = 2 * i + 1;//左节点
        int right = 2 * i + 2;//右节点

        if (left > end) return;//出口
        if (right <= end) {//有两子节点时
            //选最小元素的下标
            int min = arr[i] > arr[left] ? left : i;
            min = arr[min] > arr[right] ? right : min;
            if (min == i) {
                if (!first) return;//不用调整了
                rebuildMinHeap(arr, left, end, true);
                rebuildMinHeap(arr, right, end, true);
            }

            //交换
            int temp = arr[i];
            arr[i] = arr[min];
            arr[min] = temp;
            if (min == left) rebuildMinHeap(arr, left, end, first);
            else rebuildMinHeap(arr, right, end, first);

        } else {//只有一个节点
            int min = arr[i] > arr[left] ? left : i;
            if (min == i) return;

            //交换
            int temp = arr[i];
            arr[i] = arr[min];
            arr[min] = temp;

        }
    }

}

5,计数排序

典型的空间换时间做法,一般用于元素区间不大的场合,比如高考成绩分数排序;

package _算法._查找与排序2;

import java.util.Arrays;

/***
 * 简知:牺牲空间换取时间的排序算法
 * ***/
public class _计数排序 {

    private static void countSort(int[] arr) {
        int max = max(arr);
        int[] helper = new int[max + 1];
        Arrays.fill(helper,0);
        for (int m : arr) {
            helper[m]++;
        }
        for (int i = 0, k = 0; i < helper.length; ) {
            if (helper[i] == 0) {
                i++;
                continue;
            }
            arr[k++] = i;
            helper[i]--;
        }

    }

    private static int max(int[] arr) {
        int max = 0;
        for (int m : arr) {
            if (max < m) max = m;
        }
        return max;
    }
}

6,桶排序

一种优化的计数排序,当元素区间很大时,可以分区间排序,区间内维护有序链表;

package _算法._查找与排序2;


import java.util.Arrays;

/***
 * 创建桶,个数为n(数组长度),通过某算法,将数组元素入桶,如何取出。
 * **/

//创建链表
class Link {
    private Link next = null;//下一指针
    private int value = 0;

    Link(int value) {
        this.value = value;
    }

    Link() {
        //无参构造方法,head
    }

    void insertNextKeepSort(Link next) {
        //插入新元,且保证有序
        Link t = this;//如果head后没有元素
        if (t.next == null) {
            t.next = next;
            return;
        }
        Link p = t;//前指针
        t = t.next;
        while (t.value > next.value && (t.next != null)) {
            p = t;
            t = t.next;
        }
        if ((t.next == null) && (t.value > next.value)) {
            t.next = next;//最后了
            return;
        }
        p.next = next;
        next.next = t;

    }

    int popValue() {
        //出元
        Link t = this;
        Link p = this;
        while (t.next != null) {
            p = t;
            t = t.next;

        }//移到最后一个

        int temp = t.value;
        p.next = null;//释放
        return temp;
    }

    boolean hasNext() {
        //如果桶内还有元素
        return this.next != null;
    }
}

public class _桶排序 {
    private static void tSort(int[] arr) {
        int max = max(arr);
        Link[] link = new Link[arr.length + 1];
        for (int i = 0; i < link.length; i++) {
            link[i] = new Link();
        }
        int index;
        for (int m : arr) {
            index = m * arr.length / (max + 1);//分桶
            link[index].insertNextKeepSort(new Link(m));
        }
        int k = 0;
        for (Link m : link) {
            while (m.hasNext()) {
                arr[k++] = m.popValue();
            }
        }
    }

    private static int max(int[] arr) {
        int max = 0;
        for (int m : arr) {
            if (m > max) max = m;
        }
        return max;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值