算法学习——排序

附上wiki参考链接:https://zh.wikipedia.org/wiki/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95#简要比较

关于排序算法的稳定性:https://zhuanlan.zhihu.com/p/36120420

冒泡排序

描述:它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。

时间复杂度:最好=O(n)(本身已经排好了,只要比较O(n-1)次,这是改进版的),最坏=平均=O(n²)

空间复杂度:O(1)

稳定性:可以实现

java实现

import java.util.Arrays;

public class BubbleSort {


    public static void main(String[] args){

        BubbleSort bubbleSort = new BubbleSort();
        int[] arr = new int[]{1,2,3,4,5};
        bubbleSort.bubbleSort(arr);
        System.out.println(Arrays.toString(arr));
    }

    public void bubbleSort(int[] arr){

        for(int i = 0;i < arr.length-1;i++){
            // 改进,若某轮没有交换,说明已经排序好了,可以不用比较下面的
            boolean ischange = false;
            // 两个两个元素比较,若前一个元素比后面的元素要大,就交换,这样较小的元素就会浮到前面
            // 有i个元素已经排好在最后,所以后面的arr.length-i - arr.length 就不用比较了
            for(int j = i+1;j < arr.length-i;j++){
                if(arr[i] > arr[j]){
                    ischange = true;
                    swap(arr,i,j);
                }
            }
            if(!ischange){
                break;
            }
        }
    }

    public void swap(int[] arr,int aIndex,int bIndex){
        int temp = arr[aIndex];
        arr[aIndex] = arr[bIndex];
        arr[bIndex] = temp;

    }
}


 

 

快速排序

描述:(详见描述分治的另一篇文章)

时间复杂度:最好=平均=O(nlogn),最坏=O(n²),与划分的对称性有关。(随机取枢轴元可避免最坏)

空间复杂度:O(logn) 

主体函数:partition(),时间复杂度为O(n)

最坏例子:以第一个元素为枢轴元,有12345/54321.

稳定性:不可实现

 

java实现(随机快排)

import java.util.Arrays;
import java.util.Random;

public class QuickSort {

    /**
     * 划分主体
     * @param a
     * @param l
     * @param r
     * @return
     */
    public int partition(int[] a,int l,int r) {
        // 生成一个随机下标,随机选择基准元,可期望划分是比较对称的。
        int k = getRandomIndex(l,r);
        // 这里一定要swap,否则每一次递归生成的k都不一样,会导致结果错误
        swap(a,l,k);
        int i = l, j = r, x = a[l];
        while (i < j) {
            while (i < j && a[j] >= x) {
                j--;
            }
            if (i < j) {
                a[i] = a[j];
            }
            while (i < j && a[i] < x) {
                i++;
            }
            if (i < j) {
                a[j] = a[i];
            }
        }
        a[i] = x;
        return i;
    }


    /**
     * 快排主体
     * @param a
     * @param l
     * @param r
     */
    public void quickSort(int[] a,int l,int r){

            if(l < r){
                int i = partition(a,l,r);
                quickSort(a,l,i-1);
                quickSort(a,i+1,r);
            }
        }



    public int getRandomIndex(int l,int r){
        return new Random().nextInt(r-l+1) + l;
}

    public void swap(int[] a,int i,int j){
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}

 

归并排序

描述:将待排序元素分成大小大致相同的两个子集合,分别对两个子集排序,最终将排序好的子集合并成所需要的排好序的集合。

时间复杂度:最好=平均=最坏=O(nlogn)

空间复杂度:O(n)

稳定性:可以实现

java实现

import java.util.Arrays;

public class MergeSort {


    public static void main(String[] args){
        new MergeSort().MergeSort(new int[]{1,5,4,3,7,6,8,4});
    }


    /**
     * 入口函数
     * @param a
     */
    public void MergeSort(int[] a){
        int[] temp = new int[a.length];
        mergeSort(a,0,a.length-1,temp);
        System.out.println(Arrays.toString(a));
    }


    /**
     * 主处理函数
     * @param a 输入的数组
     * @param start 开始index
     * @param end 结束index
     * @param temp 临时数组temp
     */
    public void mergeSort(int[] a,int start,int end,int[] temp){

        // 直到start >= end 递归结束
          if(start < end){
//        if(start < end - 1){
            int mid = (end+start) >> 1;
            // 递归排序左半部分
//            mergeSort(a,start,mid-1,temp);
            mergeSort(a,start,mid,temp);
            //递归排序由半部分
//            mergeSort(a,mid,end,temp);
            mergeSort(a,mid+1,end,temp);
            //拍好后把temp的结果更改放到原数组中
            mergeArr(a,start,mid,end,temp);
        }
    }

    /**
     * 把temp数组中的结果放到a中
     * @param a
     * @param start
     * @param mid
     * @param end
     * @param temp
     */
    public void mergeArr(int[] a,int start,int mid,int end,int[] temp){
        int i = start;
        int j = mid+1;
        int k = 0;
        // 所有小的元素放到前面,大的元素放到后面
        while(i <= mid && j <= end){
            if(a[i] <= a[j]){
                temp[k++] = a[i++];
            }else{
                temp[k++] = a[j++];
            }
        }
        while(i <= mid){
            temp[k++] = a[i++];
        }
        while(j <= end){
            temp[k++] = a[j++];
        }
        for(i = 0;i < k;i++){
            a[i+start] = temp[i];
        }
    }
}

 

堆排序

描述:建立在堆(二叉堆)的基础上,对堆进行排序。

二叉堆的特点:1)二叉堆的节点的值总是>=(或<=)任何一个子节点的值;2)每个节点的左右子树均为二叉堆。

时间复杂度:最好=最坏=平均=O(nlogn)

空间复杂度:O(1)

稳定性:不可实现

关于其复杂度讨论可参考blog:https://chihminh.github.io/2016/08/08/heap-sort/

java实现

import java.util.Arrays;

public class HeapSort {

    public static void main(String[] args){

        HeapSort heapSort = new HeapSort();
        int[] arr = new int[]{1,5,9,8,4,5,2};
        heapSort.minHeapSort(arr);
        System.out.println(Arrays.toString(arr));
    }


    public void minHeapSort(int[] arr){

        // 1.构造大根堆

        for(int i = arr.length-1;i >= 0;i--){
            heapInsert(arr,i);
        }

        // 2.调整堆结构,并且把每次调整得到的最大的元素a[0]与末尾元素交换

        int length = arr.length;
        swap(arr,0,--length);
        while (length > 0){
            heapify(arr,0,--length);
            swap(arr,0,length);
        }

    }


    /**
     * 建立大根堆
     */
    public void heapInsert(int[] arr,int i){
        int fIndex = (i-1) >> 1;
        while (fIndex >= 0 && arr[i] > arr[fIndex]){
            swap(arr,i,fIndex);
            i = fIndex;
            fIndex = (i-1) >> 1;
        }
    }


    public void swap(int[] arr,int aIndex,int bIndex){
        int temp = arr[aIndex];
        arr[aIndex] = arr[bIndex];
        arr[bIndex] = temp;

    }

    /**
     * 就是把index结点调整(下沉)到合适位置的函数
     */
    public void heapify(int[] arr,int index,int size){

        // 找到index的左孩子坐标
        int leftChild = index << 1+1;
        while(leftChild < size){
            // 把左右孩子比较,取最大值的那个坐标
            int largest = leftChild+1 < size && arr[leftChild+1] > arr[leftChild] ? leftChild+1 : leftChild;
            // 孩子比父要小,那就不用再往上走了
            if(arr[largest] <= arr[index]){
                break;
            }
            // 交换父与孩子位置
            swap(arr,largest,index);
            // 从父亲的新位置开始继续往上走
            index = largest;
            leftChild = leftChild << 1+1;
        }
    }
}

 

插入排序

描述:它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

时间复杂度:最好=O(n)(升序,只需要比较n-1次),最坏=O(n²)(降序,需要比较1/2n(n-1)),平均=O(n²)

空间复杂度:O(1)(in-place)

稳定性:可实现

 

java实现

import java.util.Arrays;

public class InsertSort {

    public static void main(String[] args){

        InsertSort insertSort = new InsertSort();
        int[] arr = new int[]{1,3,9,8,4,5,2,7};
        insertSort.insertSort(arr);
        System.out.println(Arrays.toString(arr));
    }

    public void insertSort(int[] arr){
        // index=0为最小的有序区
        for(int i = 1;i < arr.length;i++){
            // 把当前元素arr[i]插入到前面的有序区域
            for(int j = i-1;j >= 0 && arr[j] > arr[j+1];j--){
                // 如果后面的数比前面的数大,那就让它们交换位置,直到插入到正确的位置
                swap(arr,j,j+1);
            }
        }
    }

    public void swap(int[] arr,int i,int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

 

选择排序

描述:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

时间复杂度:最好=最坏=平均=O(n²)(因为它要跟后面的所有未排序元素比较)

空间复杂度:O(1)

稳定性:不能实现(数组)

java实现

import java.util.Arrays;

public class SelectSort {

    public static void main(String[] args){

        SelectSort selectSort = new SelectSort();
        int[] arr = new int[]{1,3,9,8,4,5,2,7};
        selectSort.selectSort(arr);
        System.out.println(Arrays.toString(arr));
    }


    public void selectSort(int[] arr){

        for(int i = 0;i < arr.length-1;i++){
            int max = i;
            // 从后面的未排序区中选取一个最大的值,然后放到当前位置
            for(int j = i+1;j < arr.length;j++){
                if(arr[j] > arr[i]){
                    max = j;
                }
            }
            swap(arr,max,i);
        }
    }

    public void swap(int[] arr,int i,int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

 

计数排序

描述:计数排序使用一个额外的数组C,其中第i个元素是待排序数组A中值等于a的元素的个数。然后根据数组来将其中的元素排到正确的位置。

时间复杂度:平均=最坏=最好=O(n)

空间复杂度:O(n)

稳定性:可实现

计数排序为桶排序的落地实现之一。只不过桶里面装的是数字出现的次数。

java实现

import java.util.Arrays;

public class CountSort {


    public static void main(String[] args){

        CountSort countSort = new CountSort();
        int[] arr = new int[]{1,2,3,9,8,4,5,2,7,8};
        countSort.countSort(arr);
        // Arrays.toString()会把重复元素删掉给,我才发现....
        for(int i = 0;i < arr.length;i++){
            System.out.print(arr[i] + " ");
        }
    }

    public void countSort(int[] arr){
        int max = Integer.MIN_VALUE;
        for(int i = 0;i < arr.length;i++){
            max = Math.max(arr[i],max);
        }
        // 创建一个能容纳0-max的桶
        int[] bucket = new int[max+1];
        // 纪录每一个arr[i]出现的次数
        for(int i = 0;i < bucket.length;i++){
            bucket[arr[i]]++;
        }
        int j = 0;
        // 根据纪录的次数依次把数据倒出来
        for(int i = 0;i < bucket.length;i++){
            while(bucket[i] != 0){
                arr[j++] = i;
                bucket[i]--;
            }
        }
    }
}

 

希尔排序

描述:直接插入算法的一种改进算法,每一轮按照事先决定的间隔进行插入排序,间隔会依次缩小,最后一次一定要是1。(1的时候就是直接插入排序,直接插入排序在数组基本有序的时候会非常高效)

时间复杂度:平均=O(nlog²n),最坏=O(n²)(步长的选择为关键)

空间复杂度:O(1)

稳定性:不可实现

java实现

public class ShellSort {

    public static void main(String[] args){
            int[] arr = new int[]{9,2,5,4,3,6,6,8,7,1};
            new ShellSort().shellSort(arr);
            System.out.println(Arrays.toString(arr));
    }

    public void shellSort(int[] arr){
        // 此处步长的选择为n/2 ... n/4 ... 1
        for(int gap = arr.length >> 1 ; gap > 0 ; gap = gap >> 1 ){
            // 直接插入排序
            for(int i = gap;i < arr.length;i++){
                for(int j = i-gap;j >= 0 && arr[j] > arr[j+gap];j-=gap){
                    swap(arr,j,j+gap);
                }
            }
        }

    }

    public void swap(int[] arr, int aIndex, int bIndex){
        int temp = arr[aIndex];
        arr[aIndex] = arr[bIndex];
        arr[bIndex]  = temp;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值