内部排序算法的性能分析_十大经典排序算法的分析与实现

十大经典排序算法的分析与实现

排序算法实乃面试以及日常工作学习所必备。本文细致总结了十大经典排序算法,详细讲解了原理并用三种主流语言进行实现。建议点击收藏关注后慢慢阅读!


绪论

在具体介绍算法前,我们首先来了解一下排序算法的评价标准。

  1. 时间复杂度:从序列的初始状态到经过排序算法的变换移位等操作变到最终排序好的结果状态的过程所花费的时间度量。

  2. 空间复杂度:从序列的初始状态经过排序移位变换的过程一直到最终的状态所花费的空间开销。

  3. 排序方式:根据是否使用额外的空间可以分为 in-place 和 out-place 两种,具体的分类如下所示:

e94173d5d8c42dfbd7f8645258c1de11.png

  1. 稳定性:元素彼此的位置的是否发生改变是决定算法稳定性的决定性因素。若相对位置发生改变,则为不稳定;反之,则为稳定。

3a680be49ec00c3ea8dfdb2b988a2951.png

=============================================================

用表格对算法的整体进行总结:

内部排序算法的性能分析_十大经典排序算法的分析与实现_第1张图片

*希尔排序的平均时间复杂度仍是一个不确定的问题。本文接下来介绍的排序算法都将默认将数列由小到大排序。


1.冒泡排序(Bubble Sort)

冒泡排序一般是作为排序的入门算法来讲解的,因为相对来说它的算法较为简单直观。在该算法中,我们需要不断地遍历需要被排序地数列,在遍历过程中每次只比较相邻地两个元素,若顺序错误则进行交换。如此这边,符合排序要求地元素不断被排至数列的一端,最终完成排序。由于元素移动地过程与冒泡十分相像,故成为冒泡排序。

  • 算法解析

    (1) 比较相邻两元素,若前一数字大于后一数字则交换位置。

    (2)由第一对数字重复(1)至最后一个非“泡泡”的数字

    (3)重复(1)(2)直至排序完成

  • 算法演示

  • 代码实现

  • Python:

    def bubbleSort(arr):
        for i in range(1, len(arr)):
            for j in range(0, len(arr)-i):
                if arr[j] > arr[j+1]:
                    arr[j], arr[j + 1] = arr[j + 1], arr[j]
        return arr
  • Java:

    public static int[] bubbleSort(int[] array) {
            
        if (array.length == 0)
            return array;
        for (int i = 0; i         for (int j = 0; j 1 - i; j++)
                if (array[j + 1]                 int temp = array[j + 1];
                    array[j + 1] = array[j];
                    array[j] = temp;
                }
        return array;
    }
  • C++:

    int[] bubbleSort(int[] array) {
            
        if(array.length == 0){
            return array;
        }
        int temp;
        for (i = 0; i array.length;i++) {                 
            for (j = 0; j array.length - i - 1;j++) {      
                if (array[j] > array[j + 1]) {
                    temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                }
            }
        }
        return array; 
    }
  • 复杂度分析:

  • 时间复杂度:最情况为数列已被排序,则只需要进行(n - 1)次比较,则复杂度为O(n);最坏情况则数列为逆序,由等差数列求和公式可得比较次数为(n - 1) * n / 2,则复杂度为O(n^2)。一般情况为O(n^2).

  • 空间复杂度:没有使用额外的空间,则空间复杂度为O(1)。


2.选择排序(Selection Sort)

简单直观的排序,不断选择最小的数字放在数列的最前端。针对任何数据时间复杂度均为O(n^2) ,一般用来进行小规模数据的处理。

  • 算法解析:

    (1)在未排序的数列中寻找最小元素,放置在数列的起始端。

    (2)不断重复(1)直至排序完成。

  • 算法演示:

  • 代码实现:

  • Python: 

    def selectionSort(arr):
        for i in range(len(arr) - 1):
            minIndex = i
            for j in range(i + 1, len(arr)):
                if arr[j]                 minIndex = j
            if i != minIndex:
                arr[i], arr[minIndex] = arr[minIndex], arr[i]
        return arr
  • Java: 

    public static int[] selectionSort(int[] array) {
            
            if (array.length == 0)
                return array;
            for (int i = 0; i             int minIndex = i;
                for (int j = i; j                 if (array[j]                     minIndex = j; 
                }
                int temp = array[minIndex];
                array[minIndex] = array[i];
                array[i] = temp;
            }
            return array;
        }
  • C++: 

    int[] selectionSort(int[] array) {
            
            if (array.length == 0)
                return array;
            for (int i = 0; i array.length; i++) {
                int minIndex = i;
                for (int j = i; j array.length; j++) {
                    if (array[j] array[minIndex])
                        minIndex = j; 
                }
                int temp = array[minIndex];
                array[minIndex] = array[i];
                array[i] = temp;
            }
            return array;
        }
  • 复杂度分析:

  • 时间复杂度:任何情况下都要进行嵌套式的遍历故为O(n^2)。

  • 空间复杂度:没有使用额外的空间,故为O(1)。


3. 插入排序(Insertion Sort)

插入排序从某种角度上来说是最为直观的而又容易理解的排序。从原理上来说,是对未排序数列从左到右逐渐建立起有序排列;每扫描一个元素便查询其在有序排列中应处于的位置。可能说到这里你也并不觉得直观,单请将其与你打扑克牌抽牌并将手中的牌排序的过程联系起来,我相信你一定会豁然开朗。

  • 算法解析:

(1)将第一个元素视为有序序列,从第二个元素开始逐个当作未排序元素

(2)扫描排序序列寻找插入位置

  • 算法演示:

  • 代码实现:

  • Python:

    def insertionSort(arr):
        for i in range(len(arr)):
            preIndex = i-1
            current = arr[i]
            while preIndex >= 0 and arr[preIndex] > current:
                arr[preIndex+1] = arr[preIndex]
                preIndex-=1
            arr[preIndex+1] = current
        return arr
  • Java:

    public static int[] insertionSort(int[] array) {
            
            if (array.length == 0)
                return array;
            int current;
            for (int i = 0; i 1; i++) {
                current = array[i + 1];
                int preIndex = i;
                while (preIndex >= 0 && current                 array[preIndex + 1] = array[preIndex];
                    preIndex--;
                }
                array[preIndex + 1] = current;
            }
            return array;
        }
  • C++:

    int[] insertionSort(int[] array) {
            
            if (array.length == 0)
                return array;
            int current;
            for (int i = 0; i array.length - 1; i++) {
                current = array[i + 1];
                int preIndex = i;
                while (preIndex >= 0 && current array[preIndex]) {
                    array[preIndex + 1] = array[preIndex];
                    preIndex--;
                }
                array[preIndex + 1] = current;
            }
            return array;
        }
  • 复杂度分析:

  • 时间复杂度:最佳情况下,数列已经有序数列则此时的复杂度为O(n);最差的情况为数列完全逆序,此时时间复杂度为O(n^2);平均情况也应为O(n^2)。

  • 空间复杂度:未使用额外的空间故应为O(1)。


4.希尔排序(Shell Sort)

希尔排序也称递减增量排序算法,是插入排序的一种更高效的改进版本,但改进之后算法的稳定性也发生了改变——希尔算法为非稳定排序算法。

由于插入排序对基本有序的数列排序效果较好(时间复杂度基本为线性),但整体效率较低(每次只能将数据移动一位)。

故希尔排序的核心思想为:先将整个待排序的记录序列分割为若干个子序列分别进行插入排序,待整个序列变得基本有序后,再对整体进行插入排序。

实际上,希尔排序是把数组按一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

  • 算法解析:

(1)选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1

(2)按增量序列个数k,对序列进行k 趟排序

(3)每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度

  • 算法演示:

内部排序算法的性能分析_十大经典排序算法的分析与实现_第2张图片

  • 代码实现:

  • Python:

    def shellSort(arr):
        import math
        gap=1
        while(gap 3):
            gap = gap*3+1
        while gap > 0:
            for i in range(gap,len(arr)):
                temp = arr[i]
                j = i-gap
                while j >=0 and arr[j] > temp:
                    arr[j+gap]=arr[j]
                    j-=gap
                arr[j+gap] = temp
            gap = math.floor(gap/3)
        return arr
  • Java:

        public static int[] ShellSort(int[] array) {
            
            int len = array.length;
            int temp, gap = len / 2;
            while (gap > 0) {
                for (int i = gap; i                 temp = array[i];
                    int preIndex = i - gap;
                    while (preIndex >= 0 && array[preIndex] > temp) {
                        array[preIndex + gap] = array[preIndex];
                        preIndex -= gap;
                    }
                    array[preIndex + gap] = temp;
                }
                gap /= 2;
            }
            return array;
        }
  • C++:

      int[] ShellSort(int[] array) {
            
            int len = array.length;
            int temp, gap = len / 2;
            while (gap > 0) {
                for (int i = gap; i                 temp = array[i];
                    int preIndex = i - gap;
                    while (preIndex >= 0 && array[preIndex] > temp) {
                        array[preIndex + gap] = array[preIndex];
                        preIndex -= gap;
                    }
                    array[preIndex + gap] = temp;
                }
                gap /= 2;
            }
            return array;
        }
  • 复杂度分析:

  • 时间复杂度:最好的情况为均衡分治的情况,时间复杂度为O(n log^2 (n))。最差的情况为最不均衡分治,为O(n^2)。平均复杂度的主流推测为O(n log n),但这仍是一个需要证明的问题。详细的希尔排序的时间复杂度推导我们会在后续的文章中给出详细解释。

  • 空间复杂度:并未占用额外的空间故为O(1)。


5. 归并排序(Merge Sort)

归并排序是分治算法(Divide and Conquer)的一个经典应用,将整个数列分至最小(单个元素)而后进行排序并组合。

  • 算法解析:

(1)Base Case:只有单个元素,返回

(2)将数组分为尽可能等长的两个部分

(3)对分开的两个部分进行排序合并

  • 算法演示:

  • 代码实现:

  • Python:

    def mergeSort(arr):
        import math
        if(len(arr)<2):
            return arr
        middle = math.floor(len(arr)/2)
        left, right = arr[0:middle], arr[middle:]
        return merge(mergeSort(left), mergeSort(right))

    def merge(left,right):
        result = []
        while left and right:
            if left[0] <= right[0]:
                result.append(left.pop(0));
            else:
                result.append(right.pop(0));
        while left:
            result.append(left.pop(0));
        while right:
            result.append(right.pop(0));
        return result
  • Java:

    public static int[] MergeSort(int[] array) {
            
            if (array.length 2) return array;
            int mid = array.length / 2;
            int[] left = Arrays.copyOfRange(array, 0, mid);
            int[] right = Arrays.copyOfRange(array, mid, array.length);
            return merge(MergeSort(left), MergeSort(right));
        }
        public static int[] merge(int[] left, int[] right) {
            int[] result = new int[left.length + right.length];
            for (int index = 0, i = 0, j = 0; index             if (i >= left.length)
                    result[index] = right[j++];
                else if (j >= right.length)
                    result[index] = left[i++];
                else if (left[i] > right[j])
                    result[index] = right[j++];
                else
                    result[index] = left[i++];
            }
            return result;
        }
  • C++:

    void merge(int arr[], int l, int m, int r) { 
        int i, j, k; 
        int n1 = m - l + 1; 
        int n2 =  r - m; 
        int L[n1], R[n2]; 
        for (i = 0; i         L[i] = arr[l + i]; 
        for (j = 0; j         R[j] = arr[m + 1+ j]; 
        i = 0; 
        j = 0; 
        k = l; 
        while (i     { 
            if (L[i] <= R[j]) 
            { 
                arr[k] = L[i]; 
                i++; 
            } 
            else
            { 
                arr[k] = R[j]; 
                j++; 
            } 
            k++; 
        } 
        while (i     { 
            arr[k] = L[i]; 
            i++; 
            k++; 
        } 
        while (j     { 
            arr[k] = R[j]; 
            j++; 
            k++; 
        } 


    void mergeSort(int arr[], int l, int r) { 
        if (l     { 
            int m = l+(r-l)/2; 
            mergeSort(arr, l, m); 
            mergeSort(arr, m+1, r); 
            merge(arr, l, m, r); 
        } 

  • 复杂度分析:

  • 时间复杂度:最好最坏和平均的情况均为O(n log n),因为无论在什么情况数列都需要被拆至单个元素再进行排序组合。

  • 空间复杂度:空间复杂度为O(n)。需要额外的栈空间储存数组,且需要进行递归操作,故为O(n)+O(log n)也就是O(n)。


6.快速排序(Quick Sort)

快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要 Ο(n log n) 次比较。在最坏状况下则需要 Ο(^2) 次比较,但这种状况并不常见。事实上,快速排序通常明显比其他 Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。

  • 算法解析:

(1)从数列中选取一个数作为基准数(pivot)

(2)对数列进行排序,大于基准数的值放在基准数之后,小于基准数的值放在基准数之前。操作完成后,基准值会处在数列的中间。此步称为分区(partition)。

(3)递归地把小于、大于基准值的两个子数列进行排序。

  • 算法演示:

  • 代码实现:

  • Python:

    def quickSort(arr, left=None, right=None):
        left = 0 if not isinstance(left,(int, float)) else left
        right = len(arr)-1 if not isinstance(right,(int, float)) else right
        if left         partitionIndex = partition(arr, left, right)
            quickSort(arr, left, partitionIndex-1)
            quickSort(arr, partitionIndex+1, right)
        return arr

    def partition(arr, left, right):
        pivot = left
        index = pivot+1
        i = index
        while  i <= right:
            if arr[i]             swap(arr, i, index)
                index+=1
            i+=1
        swap(arr,pivot,index-1)
        return index-1

    def swap(arr, i, j):
        arr[i], arr[j] = arr[j], arr[i]
  • Java:

     public static int[] QuickSort(int[] array, int start, int end) {
            
            if (array.length 1 || start 0 || end >= array.length || start > end)                 return null;
            int smallIndex = partition(array, start, end);
            if (smallIndex > start)
                QuickSort(array, start, smallIndex - 1);
            if (smallIndex             QuickSort(array, smallIndex + 1, end);
            return array;
        }
        public static int partition(int[] array, int start, int end) {
            int pivot = (int) (start + Math.random() * (end - start + 1));
            int smallIndex = start - 1;
            swap(array, pivot, end);
            for (int i = start; i <= end; i++)
                if (array[i] <= array[end]) {
                    smallIndex++;
                    if (i > smallIndex)
                        swap(array, i, smallIndex);
                }
            return smallIndex;
        }
        public static void swap(int[] array, int i, int j) {
            int temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }
  • C++:

    int[] QuickSort(int[] array, int start, int end) {
            
            if (array.length 1 || start 0 || end >= array.length || start > end)                 return null;
            int smallIndex = partition(array, start, end);
            if (smallIndex > start)
                QuickSort(array, start, smallIndex - 1);
            if (smallIndex             QuickSort(array, smallIndex + 1, end);
            return array;
        }

    int partition(int[] array, int start, int end) {
            int pivot = (int) (start + Math.random() * (end - start + 1));
            int smallIndex = start - 1;
            swap(array, pivot, end);
            for (int i = start; i <= end; i++)
                if (array[i] <= array[end]) {
                    smallIndex++;
                    if (i > smallIndex)
                        swap(array, i, smallIndex);
                }
            return smallIndex;
        }

    void swap(int[] array, int i, int j) {
            int temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }
  • 复杂度分析:

  • 时间复杂度:最好的情况为抽取的基准值刚好能平分数组,此时时间复杂度为O(n log n)。最差的情况为每一次抽取的基准值都在数列的最边上,此时快速排序就变为了冒泡排序,时间复杂度为O(n^2)。平均情况下时间复杂度为O(n log n)。

  • 空间复杂度:递归操作需要使用额外的空间,故为O(log n)。


7.堆排序(Heap Sort)

利用堆性质实现排序的一种算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

  • 算法分析:

(1)创建一个堆

(2)将堆首(最大值)和堆尾互换

(3)将堆的尺寸缩小一,重新heapify

(4)重复(2)(3)直至堆的尺寸变为1

  • 算法演示:

  • 代码实现:

  • Python:

    def buildMaxHeap(arr):
        import math
        for i in range(math.floor(len(arr)/2),-1,-1):
            heapify(arr,i)

    def heapify(arr, i):
        left = 2*i+1
        right = 2*i+2
        largest = i
        if left and arr[left] > arr[largest]:
            largest = left
        if right and arr[right] > arr[largest]:
            largest = right
        if largest != i:
            swap(arr, i, largest)
            heapify(arr, largest)

    def swap(arr, i, j):
        arr[i], arr[j] = arr[j], arr[i]

    def heapSort(arr):
        global arrLen
        arrLen = len(arr)
        buildMaxHeap(arr)
        for i in range(len(arr)-1,0,-1):
            swap(arr,0,i)
            arrLen -=1
            heapify(arr, 0)
        return arr
  • Java:

    static int len;
        public static int[] HeapSort(int[] array) {
            len = array.length;
            if (len 1) return array;
            //1.构建一个最大堆
            buildMaxHeap(array);
            //2.循环将堆首位(最大值)与末位交换,然后在重新调整最大堆
            while (len > 0) {
                swap(array, 0, len - 1);
                len--;
                adjustHeap(array, 0);
            }
            return array;
        }
        public static void buildMaxHeap(int[] array) {
            //从最后一个非叶子节点开始向上构造最大堆
            for (int i = (len - 1) / 2; i >= 0; i--) {
                adjustHeap(array, i);
            }
        }
        public static void adjustHeap(int[] array, int i) {
            int maxIndex = i;
            //如果有左子树,且左子树大于父节点,则将最大指针指向左子树
            if (i * 2 2] > array[maxIndex])
                maxIndex = i * 2;
            //如果有右子树,且右子树大于父节点,则将最大指针指向右子树
            if (i * 2 + 1 2 + 1] > array[maxIndex])
                maxIndex = i * 2 + 1;
            //如果父节点不是最大值,则将父节点与最大值交换,并且递归调整与父节点交换的位置。
            if (maxIndex != i) {
                swap(array, maxIndex, i);
                adjustHeap(array, maxIndex);
            }
        }
  • C++:

    void heapify(int arr[], int n, int i) { 
        int largest = i; // 将最大元素设置为堆顶元素
        int l = 2*i + 1; // left = 2*i + 1 
        int r = 2*i + 2; // right = 2*i + 2 
        // 如果 left 比 root 大的话
        if (l  arr[largest]) 
            largest = l; 
        // I如果 right 比 root 大的话
        if (r  arr[largest]) 
            largest = r; 
        if (largest != i) 
        { 
            swap(arr[i], arr[largest]); 
            // 递归地定义子堆
            heapify(arr, n, largest); 
        } 


    void heapSort(int arr[], int n) { 
        // 建立堆
        for (int i = n / 2 - 1; i >= 0; i--) 
            heapify(arr, n, i); 
        // 一个个从堆顶取出元素
        for (int i=n-1; i>=0; i--) 
        { 
            swap(arr[0], arr[i]);  
            heapify(arr, i, 0); 
        } 

  • 复杂度分析:

  • 时间复杂度:每一次heapify的复杂度为O(log n),故排序的复杂度为O(n log n)。

  • 空间复杂度:未使用额外的空间故为O(1)。


8.计数排序(Counting Sort)

计数排序并不基于元素的比较,而是一种利用数组下标来确定元素正确位置的算法。计数排序的核心在于将输入的数据值转化为键值存储在额外开辟的数组空间中。

  • 算法分析:

(1)找出待排序的数组中最大和最小的元素;
(2)统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
(3)对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
(4)反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。

  • 算法演示:

  • 代码实现:

  • Python:

    def countingSort(arr, maxValue):
        bucketLen = maxValue+1
        bucket = [0]*bucketLen
        sortedIndex =0
        arrLen = len(arr)
        for i in range(arrLen):
            if not bucket[arr[i]]:
                bucket[arr[i]]=0
            bucket[arr[i]]+=1
        for j in range(bucketLen):
            while bucket[j]>0:
                arr[sortedIndex] = j
                sortedIndex+=1
                bucket[j]-=1
        return arr
  • Java:

    public static int[] CountingSort(int[] array) {
            
            if (array.length == 0) return array;
            int bias, min = array[0], max = array[0];
            for (int i = 1; i             if (array[i] > max)
                    max = array[i];
                if (array[i]                 min = array[i];
            }
            bias = 0 - min;
            int[] bucket = new int[max - min + 1];
            Arrays.fill(bucket, 0);
            for (int i = 0; i             bucket[array[i] + bias]++;
            }
            int index = 0, i = 0;
            while (index             if (bucket[i] != 0) {
                    array[index] = i - bias;
                    bucket[i]--;
                    index++;
                } else
                    i++;
            }
            return array;
        }
  • C++:

    public:
        void coutSort(int* data, int length){
            if (data == nullptr || length <= 0)
                return;
            //确定数列最大值
            int max = data[0];
            for (int i = 1; i         {
                if (data[i] > max)
                    max = data[i];
            }
            // 确定统计数组长度并进行初始化
            int* coutData = new int[max + 1];
            for (int i = 0; i <= max; ++i)
                coutData[i] = 0;
            // 遍历数组,统计每个数出现的次数
            for (int i = 0; i             ++coutData[data[i]];
            // 排序数组,某个数出现了几次,便在data里累计输出几次
            int index = 0;
            for (int i = 0; i <= max; ++i)
            {
                for (int j = 0; j             {
                    data[index++] = i;
                }
            }
        }
  • 复杂度分析:

  • 时间复杂度:对于给定的n个d位数,取值范围为[0,k],我们使用计数排序比较元素的每一位,基数排序耗时O(n+k),那么基数排序的复杂度为O(n+k)。

  • 空间复杂度:对于取值范围为[0,k]的数组,空间复杂度应该为 n + O(k)。


9.桶排序(Bucket Sort)

桶排序的原理是将数组分到有限数量的桶中,再对每个桶子再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序),最后将各个桶中的数据有序的合并起来。

  • 算法分析:

(1)假设待排序的一组数统一的分布在一个范围中,并将这一范围划分成几个子范围,也就是桶
(2)将待排序的一组数,分档规入这些子桶,并将桶中的数据进行排序
(3)将各个桶中的数据有序的合并起来

  • 算法演示:

    内部排序算法的性能分析_十大经典排序算法的分析与实现_第3张图片

  • 代码实现:

  • Python:

    def bucket_sort(s):
        """桶排序"""
        min_num = min(s)
        max_num = max(s)
        # 桶的大小
        bucket_range = (max_num-min_num) / len(s)
        # 桶数组
        count_list = [ [] for i in range(len(s) + 1)]
        # 向桶数组填数
        for i in s:
            count_list[int((i-min_num)//bucket_range)].append(i)
        s.clear()
        # 回填,这里桶内部排序直接调用了sorted
        for i in count_list:
            for j in sorted(i):
                s.append(j)
  • Java:

    public static ArrayList BucketSort(ArrayList array, int bucketSize) {
            
            if (array == null || array.size() 2)
                return array;
            int max = array.get(0), min = array.get(0);
            // 找到最大值最小值
            for (int i = 0; i             if (array.get(i) > max)
                    max = array.get(i);
                if (array.get(i)                 min = array.get(i);
            }
            int bucketCount = (max - min) / bucketSize + 1;
            ArrayList> bucketArr = new ArrayList<>(bucketCount);
            ArrayList resultArr = new ArrayList<>();for (int i = 0; i             bucketArr.add(new ArrayList());
            }for (int i = 0; i             bucketArr.get((array.get(i) - min) / bucketSize).add(array.get(i));
            }for (int i = 0; i             if (bucketCount == 1)
                    bucketSize--;
                ArrayList temp = BucketSort(bucketArr.get(i), bucketSize);for (int j = 0; j                 resultArr.add(temp.get(j));
            }return resultArr;
        }
  • C++:

    void bucketSort(float arr[], int n) { 
        // 1) Create n empty buckets 
        vector b[n]; 
        // 2) Put array elements in different buckets 
        for (int i=0; i    { 
           int bi = n*arr[i]; // Index in bucket 
           b[bi].push_back(arr[i]); 
        } 
        // 3) Sort individual buckets 
        for (int i=0; i       sort(b[i].begin(), b[i].end()); 
        // 4) Concatenate all buckets into arr[] 
        int index = 0; 
        for (int i = 0; i         for (int j = 0; j           arr[index++] = b[i][j]; 
    }
  • 复杂度分析:

  • 时间复杂度:桶排序的平均时间复杂度为线性的O(n+k),其中C=n*(log n-log k)。如果相对于同样的n,桶数量k越大,其效率越高,最好的时间复杂度达到O(n),而最坏的情况是只有一个桶此时复杂度为O(n^2)。

  • 空间复杂度:桶的数量为k,则空间复杂度为O(n + k)。


10.基数排序(Radix Sort)

基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。排序过程是将所有待比较数值统一为同样的数位长度,数位较短的数前面补零,然后从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。

  • 算法分析:

(1)取得数组中的最大数,并取得位数
(2)arr为原始数组,从最低位开始取每个位组成radix数组
(3)对radix进行计数排序(利用计数排序适用于小范围数的特点)

  • 算法演示:

  • 代码实现:

  • Python:

    def RadixSort(list):
        i = 0                                    #初始为个位排序
        n = 1                                     #最小的位数置为1(包含0)
        max_num = max(list) #得到带排序数组中最大数
        while max_num > 10**n: #得到最大数是几位数
            n += 1
        while i         bucket = {} #用字典构建桶
            for x in range(10):
                bucket.setdefault(x, []) #将每个桶置空
            for x in list: #对每一位进行排序
                radix =int((x / (10**i)) % 10) #得到每位的基数
                bucket[radix].append(x) #将对应的数
                组元素加入到相 #应位基数的桶中
            j = 0
            for k in range(10):
                if len(bucket[k]) != 0: #若桶不为空
                    for y in bucket[k]: #将该桶中每个元素
                        list[j] = y #放回到数组中
                        j += 1
            i += 1
    return  list
  • Java:

    public static int[] RadixSort(int[] array) {
            
            if (array == null || array.length 2)
                return array;
            // 1.先算出最大数的位数;
            int max = array[0];
            for (int i = 1; i             max = Math.max(max, array[i]);
            }
            int maxDigit = 0;
            while (max != 0) {
                max /= 10;
                maxDigit++;
            }
            int mod = 10, div = 1;
            ArrayList> bucketList = new ArrayList>();for (int i = 0; i 10; i++)
                bucketList.add(new ArrayList());for (int i = 0; i 10, div *= 10) { for (int j = 0; j                 int num = (array[j] % mod) / div;
                    bucketList.get(num).add(array[j]);
                }int index = 0;for (int j = 0; j                 for (int k = 0; k                     array[index++] = bucketList.get(j).get(k);
                    bucketList.get(j).clear();
                }
            }return array;
        }
  • C++:

    int getMax(int arr[], int n) { 
        int mx = arr[0]; 
        for (int i = 1; i         if (arr[i] > mx) 
                mx = arr[i]; 
        return mx; 


    void countSort(int arr[], int n, int exp) { 
        int output[n]; 
        int i, count[10] = { 0}; 
          for (i = 0; i         count[ (arr[i]/exp)%10 ]++; 
        for (i = 1; i 10; i++) 
            count[i] += count[i - 1]; 
        for (i = n - 1; i >= 0; i--) 
        { 
            output[count[ (arr[i]/exp)%10 ] - 1] = arr[i]; 
            count[ (arr[i]/exp)%10 ]--; 
        } 
        for (i = 0; i         arr[i] = output[i]; 


    void radixsort(int arr[], int n) { 
        int m = getMax(arr, n); 
        for (int exp = 1; m/exp > 0; exp *= 10) 
            countSort(arr, n, exp); 

  • 复杂度分析:

  • 时间复杂度:每一次关键字的桶分配都需要O(n)的时间复杂度,而且分配之后得到新的关键字序列又需要O(n)的时间复杂度。假如待排数据可以分为k个关键字,则基数排序的时间复杂度将是O(k x 2 n) ,当然k要远远小于n,因此基本上还是线性级别的。系数2可以省略,且无论数组是否有序,都需要从个位排到最大位数,所以时间复杂度始终为O(nk) 。

  • 空间复杂度:每一次都要保存待排数值,n为序元素个数,k为数字位数,空间复杂度为O(nk)。

你可能感兴趣的:(内部排序算法的性能分析)