[面试必备]重温数据结构中经典排序算法

191 阅读2分钟

一些经典的排序算法

关于排序算法相信各位同学都不陌生吧,从最基础的冒泡排序,快速排序,归并排序等,都是在算法面试中经常出现的问题,下面是对一些常见的排序算法进行的coding

比较排序

冒泡排序

重复地走访过要排序的数列,每次比较相邻两个元素,如果它们的顺序错误就把它们交换过来,越大的元素会经由交换慢慢“浮”到数列的尾端。

  • java版本

public void bubbleSort(int[] arr) {

    int temp = 0;

    boolean swap;

    for (int i = arr.length - 1; i > 0; i--) { // 每次需要排序的长度

        // 增加一个swap的标志,当前一轮没有进行交换时,说明数组已经有序

        swap = false;

        for (int j = 0; j < i; j++) { // 从第一个元素到第i个元素

            if (arr[j] > arr[j + 1]) {

                temp = arr[j];

                arr[j] = arr[j + 1];

                arr[j + 1] = temp;

                swap = true;

            }

        }

        if (!swap){

            break;

        }

    }

}

  • kotlin版本
  fun bubbleSort(array: IntArray) {
        var temp = 0
        var swap: Boolean
        for (i in array.size - 1 downTo 0) { //需要排序的序列
            swap = false
            for (j in 0..i) {
                if (array[j] > array[j + 1]) {
                    temp = array[j];
                    array[j] = array[j + 1]
                    array[j + 1] = temp
                    swap = true
                }
            }
            if (!swap) {
                break
            }
        }
    }
  • 演示如下

归并排序

分解待排序的数组成两个各具 n/2 个元素的子数组,递归调用归并排序两个子数组,合并两个已排序的子数组成一个已排序的数组。

  • Java版本

public void mergeSort(int[] arr) {

    int[] temp = new int[arr.length];

    internalMergeSort(arr, temp, 0, arr.length - 1);

}



private void internalMergeSort(int[] arr, int[] temp, int left, int right) {

    // 当left == right时,不需要再划分

    if (left < right) {

        int mid = (left + right) / 2;

        internalMergeSort(arr, temp, left, mid);

        internalMergeSort(arr, temp, mid + 1, right);

        mergeSortedArray(arr, temp, left, mid, right);

    }

}



// 合并两个有序子序列

public void mergeSortedArray(int[] arr, int[] temp, int left, int mid, int right) {

    int i = left;

    int j = mid + 1;

    int k = 0;

    while (i <= mid && j <= right) {

        temp[k++] = arr[i] < arr[j] ? arr[i++] : arr[j++];

    }

    while (i <= mid) {

        temp[k++] = arr[i++];

    }

    while (j <= right) {

        temp[k++] = arr[j++];

    }

    // 把temp数据复制回原数组

    for (i = 0; i < k; i++) {

        arr[left + i] = temp[i];

    }

}

  • Kotlin版本
 fun mergeSort(array: IntArray) {
        val temp = IntArray(array.size)
        internalMergeSort(array, temp, 0, array.size - 1)
    }


    private fun internalMergeSort(array: IntArray, temp: IntArray, left: Int, right: Int) {
        if (left < right) {
            val mid = (left + right) / 2
            internalMergeSort(array, temp, left, mid) //左边
            internalMergeSort(array, temp, mid + 1, right) //右边
            mergeSorted(array, temp, left, mid, right)

        }
    }

    //合并两个有序子序列
    private fun mergeSorted(array: IntArray, temp: IntArray, left: Int, mid: Int, right: Int) {
        var i = left         //左 [left,mid]
        var j = mid + 1      //右[mid+1,right]
        var k = 0
        while (i <= mid && j <= right) {
            temp[k++] = if (array[i] < array[j]) {
                array[i++]
            } else {
                array[j++]
            }
        }
        while (i <= mid) {
            temp[k++] = array[i++]
        }
        while (j <= right) {
            temp[k++] = array[j++]
        }

        // 把temp数据复制回原数组
        for (i in 0 until k) {
            array[left + i] = temp[i]
        }
    }
  • 演示如下

快速排序

在待排序的数组选取一个元素作为基准,将待排序的元素进行分区,比基准元素大的元素放在一边,比其小的放另一边,递归调用快速排序对两边的元素排序。选取基准元素并分区的过程采用双指针左右交换。

  • Java版本

public void quickSort(int[] arr){

    quickSort(arr, 0, arr.length-1);

}



private void quickSort(int[] arr, int low, int high){

    if (low >= high)

        return;

    int pivot = partition(arr, low, high);        //将数组分为两部分

    quickSort(arr, low, pivot - 1);                   //递归排序左子数组

    quickSort(arr, pivot + 1, high);                  //递归排序右子数组

}



private int partition(int[] arr, int low, int high){

    int pivot = arr[low];     //基准

    while (low < high){

        while (low < high && arr[high] >= pivot) {

            high--;

        }

        arr[low] = arr[high];             //交换比基准大的记录到左端

        while (low < high && arr[low] <= pivot) {

            low++;

        }

        arr[high] = arr[low];           //交换比基准小的记录到右端

    }

    //扫描完成,基准到位

    arr[low] = pivot;

    //返回的是基准的位置

    return low;

}

  • Kotlin版本
fun quickSort(array: IntArray) {
        quickSort(array, 0, array.size - 1)
    }

    private fun quickSort(array: IntArray, low: Int, high: Int) {
        if (low <= high)
            return
        partition(array, low, high)
    }

    private fun partition(array: IntArray, low: Int, high: Int): Int {
        val pivot = array[low]
        var low = low
        var high = high
        while (low < high) {
            while (low < high && array[high] >= pivot) {
                high--
            }
            array[low] = array[high]  //交换比基准大的记录到左端

            while (low < high && array[low] <= pivot) {
                low++
            }
            array[high] = array[low] //交换比基准小的记录到右端
        }
        //扫描完成
        array[low] = pivot
        //基准到位
        return low
    }
  • 演示版本

线性排序

计数排序

根据待排序的数组中最大和最小的元素,统计数组中每个值为i的元素出现的次数,存入数组C的第i项,对所有的计数累加,然后反向填充目标数组。

  • Java版本

public void countSort(int[] arr) {

    int max = Integer.MIN_VALUE;
    int min = Integer.MAX_VALUE;

    for(int i = 0; i < arr.length; i++){

        max = Math.max(max, arr[i]);
        min = Math.min(min, arr[i]);

    }



    int[] b = new int[arr.length]; // 存储数组

    int[] count = new int[max - min + 1]; // 计数数组



    for (int num = min; num <= max; num++) {

        // 初始化各元素值为0,数组下标从0开始因此减min

        count[num - min] = 0;

    }



    for (int i = 0; i < arr.length; i++) {

        int num = arr[i];

        count[num - min]++; // 每出现一个值,计数数组对应元素的值+1

        // 此时count[i]表示数值等于i的元素的个数

    }



    for (int i = min + 1; i <= max; i++) {

        count[i - min] += count[i - min - 1];

        // 此时count[i]表示数值<=i的元素的个数

    }



    for (int i = 0; i < arr.length; i++) {

            int num = arr[i]; // 原数组第i位的值

            int index = count[num - min] - 1; //加总数组中对应元素的下标

            b[index] = num; // 将该值存入存储数组对应下标中

            count[num - min]--; // 加总数组中,该值的总和减少1。

    }



    // 将存储数组的值替换给原数组

    for(int i=0; i < arr.length;i++){

        arr[i] = b[i];

    }

}

  • Kotlin版本
fun countSort(array: IntArray) {
        var max = Int.MIN_VALUE
        var min = Int.MAX_VALUE
        for (i in array.indices) {
            max = max.coerceAtLeast(array[i])
            min = min.coerceAtMost(array[i])
        }
        val b = IntArray(array.size) // 存储数组
        val count = IntArray(max - min + 1) // 计数数组

        for (num in min..max) {
            // 初始化各元素值为0,数组下标从0开始因此减min
            count[num - min] = 0
        }

        for (i in array.indices) {
            val num: Int = array[i]
            count[num - min]++ // 每出现一个值,计数数组对应元素的值+1
            // 此时count[i]表示数值等于i的元素的个数
        }

        for (i in min + 1..max) {
            count[i - min] += count[i - min - 1]
            // 此时count[i]表示数值<=i的元素的个数
        }

        for (i in array.indices) {
            val num: Int = array[i] // 原数组第i位的值
            val index = count[num - min] - 1 //加总数组中对应元素的下标
            b[index] = num // 将该值存入存储数组对应下标中
            count[num - min]-- // 加总数组中,该值的总和减少1。
        }

        // 将存储数组的值替换给原数组
        for (i in array.indices) {
            array[i] = b[i]
        }
    }
  • 演示图

桶排序

找出待排序数组中的最大值max、最小值min,数组ArrayList作为桶,桶里放的元素用ArrayList存储。计算每个元素 arr[i] 放的桶,每个桶各自排序,遍历桶数组,把排序好的元素放进输出数组。

  • Java版本

public static void bucketSort(int[] arr){

    int max = Integer.MIN_VALUE;

    int min = Integer.MAX_VALUE;

    for(int i = 0; i < arr.length; i++){

        max = Math.max(max, arr[i]);

        min = Math.min(min, arr[i]);

    }

    // 桶数

    int bucketNum = (max - min) / arr.length + 1;

    ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum);

    for(int i = 0; i < bucketNum; i++){

        bucketArr.add(new ArrayList<Integer>());

    }

    // 将每个元素放入桶

    for(int i = 0; i < arr.length; i++){

        int num = (arr[i] - min) / (arr.length);

        bucketArr.get(num).add(arr[i]);

    }

    // 对每个桶进行排序

    for(int i = 0; i < bucketArr.size(); i++){

        Collections.sort(bucketArr.get(i));

        for (int j = 0; j < bucketArr.get(i).size(); j++) {

            arr[j] = bucketArr.get(i).get(j);

        }

    }

}

  • Kotlin版本
fun bucketSort(arr: IntArray) {
        var max = Int.MIN_VALUE
        var min = Int.MAX_VALUE
        for (i in arr.indices) {
            max = max.coerceAtLeast(arr[i]) //最大值
            min = min.coerceAtMost(arr[i]) //最小值
        }
        // 桶数
        val bucketNum = (max - min) / arr.size + 1
        val bucketArr: ArrayList<ArrayList<Int>> = ArrayList(bucketNum)
        for (i in 0 until bucketNum) {
            bucketArr.add(ArrayList())
        }
        // 将每个元素放入桶
        for (i in arr.indices) {
            val num = (arr[i] - min) / arr.size
            bucketArr[num].add(arr[i])
        }
        // 对每个桶进行排序
        for (i in 0 until bucketArr.size) {
            bucketArr[i].sort()
            for (j in 0 until bucketArr[i].size) {
                arr[j] = bucketArr[i][j]
            }
        }
    }