美文网首页
再来复习一下排序算法吧

再来复习一下排序算法吧

作者: 雅俗共赏M | 来源:发表于2017-04-03 16:18 被阅读97次

    大一下学习的数据结构与算法,如今已经大三了,几乎70%左右的知识都已经又还给了亲爱的老师了。可关键是学费不给退啊,所以是时候要找回一些以前学过的知识了。

    排序算法总结:

    一、冒泡排序(Bubble Sort)

    二、选择排序(Select Sort)

    三、插入排序(Insert Sort)

    四、希尔排序(Shell Sort)

    五、快速排序(Quick Sort)

    六、堆排序(Heap Sort)

    七、归并排序(Merge Sort)

    八、计数排序(Counting Sort)

    九、桶排序(Bucket Sort)

    十、基数排序(Radix Sort)

    一、冒泡排序

    冒泡排序的基本思想就是:每一次遍历的时候,只比较两个相邻的元素大小,最后"冒"出一个最大值/最小值。

    在实现的时候,需要注意每个循环语句的次数。

        /**
         * 原始冒泡排序
         * 时间复杂度为O(n^2)
         * @param args
         */
        public static void bubbleSort(int[] args) {
            if (args == null || args.length == 0) {
                return;
            }
            else {
                for (int i = 0; i < args.length - 1; i++) {
                    for (int j = args.length - 1; j > i; j--) {
                        if (args[j - 1] > args[j]) {
                            swap(args, j-1, j);
                        }
                    }
                }
            }
        }
        public static void swap(int[] args, int small, int big) {
            int temp;
            temp = args[big];
            args[big] = args[small];
            args[small] = temp;
        }
    
    

    ** 冒泡算法的改进: **
    在每次大的循环进行初始,将 isSwap = false。这样每次小的循环进行完之后,进行依次对比,如果isSwapfalse,则可以证明该数组已经有序。

        /**
         * 改进后的算法排序时间复杂度最佳可以达到O(n)
         * @param args
         */
        public static void bubbleSortImprove(int[] args) {
            boolean isSwap;
            if (args == null || args.length == 0) {
                return;
            }
            for (int i =0; i < args.length; i++) {
                isSwap = false;
                for (int j = args.length - 1; j > i; j--) {
                    if (args[j - 1] > args[j]) {
                        swap(args, j-1, j);
                        isSwap = true;
                    }
                }
                if (!isSwap) {
                    return;
                }
            }
        }
    
    

    二、选择排序

    选择排序的主要过程是:从第一个数字开始,将其下标记为minIndex,然后逐个与后边的所有数字进行比较,在这一轮比较结束后,判断minIndex是否和第一个数字的下标一致,如果一致,则第一个数字为数组中最小数,否则,将minIndex所指向的数字与第一个数字交换。接着从第二个数字开始,依次按照上边的流程来进行,大的循环需要进行的次数为:arr.lenth - 1

       /**
         * 选择排序
         * 时间复杂度为O(n^2)
         * @param args
         */
        public static void selectSort(int[] args) {
            int minIndex;
            if (args == null || args.length == 0) {
                return;
            }
            else {
                for (int i = 0; i < args.length - 1; i++) {
                    minIndex = i;
                    for (int j = i + 1; j < args.length - 1; j++) {
                        if (args[j] < args[minIndex]) {
                            minIndex = j;
                        }
                    }
                    //如果minIndex不等于i,说明此时已经找到了最小的值,所以要坐一个交换。
                    if (minIndex != i) {
                        swap(args,minIndex, i);
                    }
                }
            }
        }
        public static void swap(int[] args, int small, int big) {
            int temp;
            temp = args[big];
            args[big] = args[small];
            args[small] = temp;
        }
    

    三、插入排序

    插入排序的主要过程是:先选出数组中的一个数字(一般选择第一个数字),然后接着选择第二个数字,判断第二个数字和第一个数字的大小,如果比第一个数字大,则插入到后边,反之则第一个数字后移一个位置,第二个数字插入到第一个位置。接着选择第三个数字,判断第三个数字与此时位置上的第二个数字的大小,如果比第二个数字大,则插入到最后,反之,则继续判断与第一个数字的大小,如果比第一个数字大,则插入到中间,反之,第一个数字后移,第三个数字插入到第一的位置。后边的数字也是依次按照这样的方式来进行判断,移动,插入。

        /**
         * 插入排序,时间复杂度O(n*2)
         * @param args
         */
        public static void insertSort(int[] args) {
            if (args == null | args.length == 0) {
                return;
            }
            else {
                for (int i = 1; i < args.length; i++) {
                    int j = i;
                    int target = args[i];
                    while (j >= 1 && target < args[j - 1]) {
                        args[j] = args[j - 1];
                        j--;
                    }
                    args[j] = target;
                }
            }
        }
    

    四、希尔排序

    我所理解的希尔排序,其实就是基于插入排序(Insert Sort)来实现的。所谓希尔排序的一大关键实现就是不再需要再从始到末一个个元素挨个去比较、插入,而是根据一个gap变量,来将数组内的元素来进行分组,先是每个小组内进行比较,然后再进一步缩小gap的值,分组来比较。最后当gap的值缩小为1的时候,就俨然成为了我之前所学习的简单插入排序了。不过这个时候再进行简单的插入排序,经过前几轮的分组比较之后,此时大部分的数据都已经有序了,需要比较的元素相对来说就减少很多。

    废话不多说了,直接上代码(注意与插入排序的代码进行比较,观察有哪些异同点。)

    package sortAlgorithm;
    
    /**
     * Created by Max on 2016/10/30.
     */
    public class ShellSort {
    
    
        /**
         * 希尔排序
         * 时间复杂度为0(n^2)
         * @param nums
         */
        public static void hellSort(int[] nums) {
    
            //第一次的gap为数组长度除以2取整
            int gap = nums.length / 2;
            while (gap >= 1) {
                for (int i = gap; i < nums.length; i++) {
                    int j = i;
    
                    //获取到每一次要与小组内其他元素进行比较的基础值nums[i]
                    int temp = nums[i];
    
                    //利用一个while循环,来遍历小组内其他元素,并与基础值比较num[i]
                    while (j >= gap && temp > nums[j - gap]) {
                        nums[j] = nums[j - gap];
                        j -= gap;
                    }
    
                    //如果j != i,则说明在遍历的过程中,发生了交换,故这里也要将num[i]插入到相应的位置
                    if (j != i) {
                        nums[j] = temp;
                    }
                }
    
                //当gap = 1时,则最终完全变成了简单的插入排序。
                if (gap == 2) {
                    gap = 1;
                }
                else {
                    gap /= 2;
                }
            }
            for (int i : nums) {
                System.out.print(i + " ");
            }
        }
    
    
    
        public static void main(String[] args) {
            int[] args1 = {5,3,8,6,4,23,34,12,54};
            hellSort(args1);
        }
    }
    
    

    五、快速排序(快排)

    快速排序的过程是:第一次排序,一般以第一个数字为基础值,在数组两端分别有两个指针:leftright ,然后right 指针首先向左移动,寻找小于基础值的数字,找到便停下,这时left指针开始向右寻找,寻找大于基础值的数字,找到便停下。接着leftright所指向的值便进行swap操作,接着左右指针再接着遍历比较下去,流程如上。不过在每次比较之前需要比较leftright指针是否重合了。重合便结束遍历。结束遍历之后,将指针重合位置所指向的数字与基础值进行swap操作,这时已经初步将小于和大于基础值的数字分布于左右两侧了。接着再进行两个一左一右的递归调用,便可以最终排序成功。

       /**
         * 快速排序
         * 时间复杂度:O(nlgn)
         * @param args
         * @param left
         * @param right
         */
        public static void quickSort(int[] args, int left, int right) {
            if (left >= right) {
                return;
            }
            else {
                int pivotPos = partition(args, left, right);
                quickSort(args, left, pivotPos - 1);
                quickSort(args, pivotPos + 1, right);
            }
        }
        public static int partition(int[] args, int left, int right) {
            int pivotKey = args[left];
            int pivotPointer = left;
            while (left < right) {
                while (left < right && args[right] >= pivotKey) {
                    right--;
                }
                while (left < right && args[left] <= pivotKey) {
                    left++;
                }
                swap(args, left, right);
            }
            swap(args, pivotPointer, left);
            return left;
        }
        public static void swap(int[] args, int small, int big) {
            int temp;
            temp = args[big];
            args[big] = args[small];
            args[small] = temp;
        }
    

    ** 快速排序的优化代码: **
    由于int pivotKey = args[left]; 已经保存过基础值了,所以可以直接将其值给覆盖掉。这样就减少了一个int temp 临时变量的产生了。

       /**
         * 快速排序
         * 时间复杂度:O(nlgn)
         * @param args
         * @param left
         * @param right
         */
        public static void quickSort(int[] args, int left, int right) {
            if (left >= right) {
                return;
            }
            else {
                int pivotPos = partition(args, left, right);
                quickSort(args, left, pivotPos - 1);
                quickSort(args, pivotPos + 1, right);
            }
        }
        public static int partition(int[] args, int left, int right) {
            int pivotKey = args[left];
            while (left < right) {
                while (left < right && args[right] >= pivotKey) {
                    right--;
                }
                args[left] = args[right];
                while (left < right && args[left] <= pivotKey) {
                    left++;
                }
                args[right] = args[left];
            }
            args[left] = pivotKey;
            return left;
        }
        public static void swap(int[] args, int small, int big) {
            int temp;
            temp = args[big];
            args[big] = args[small];
            args[small] = temp;
        }
    

    六、堆排序

    什么是二叉树?

    每个节点最多拥有两个子节点的树状结构

    二叉树有哪些性质?

    • 二叉树的第i层至多有2^(i-1)个节点,
    • 深度为k的二叉树至多有2^k-1个节点,
    • 对任何一颗二叉树T,如果其终端节点数为n0,度为2的节点数为n2,则n0=n2+1

    二叉树分类

    • 完全二叉树(complete binary tree):深度为 k,有 n 个节点的二叉树,当且仅当其每一个节点都与深度为 k 的满二叉树中序号为 1 至 n 的节点对应时,称之为完全二叉树
    • 满二叉树(full binary tree):一颗深度为k,且有2^k-1个节点的二叉树

    那么,堆又是什么呢?

    以二叉堆为例,其可以视为一棵完全的二叉树,完全二叉树的一个“优秀”的性质是,除了最底层之外,每一层都是满的,这使得堆可以利用数组来表示(普通的一般的二叉树通常用链表作为基本容器表示),每一个结点对应数组中的一个元素。

    二叉堆的特性:

    对于给定的某个结点的下标 i,可以很容易的计算出这个结点的父结点、孩子结点的下标:
    Parent(i) = floor(i/2),i 的父节点下标
    Left(i) = 2i,i 的左子节点下标
    Right(i) = 2i + 1,i 的右子节点下标

    二叉堆的分类:

    • 大顶堆:最大堆中的最大元素值出现在根结点(堆顶)堆中每个父节点的元素值都大于等于其孩子结点(如果存在)
    • 小顶堆:最小堆中的最小元素值出现在根结点(堆顶)堆中每个父节点的元素值都小于等于其孩子结点(如果存在)

    堆排序原理

    堆排序就是把最大堆堆顶的最大数取出,将剩余的堆继续调整为最大堆,再次将堆顶的最大数取出,这个过程持续到剩余数只有一个时结束。时间复杂度为(nlogn)。

    过程

    步骤一:
    将数组初始化为大顶堆:

    //将该数组初始化为大顶堆
    public static void heapInit(int[] nums) {
            //获取此二叉树最后一个非叶节点node
            int node = nums.length / 2 - 1;
            int length = nums.length;
            int tempNode;
            for (; node >= 0; node--) {
                tempNode = node;
                //获得该非叶节点的左右孩子节点:lNode,rNode
                int lNode = node * 2 + 1;
                int rNode = node * 2 + 2;
                if (lNode < length && nums[lNode] > nums[tempNode]) {
                    tempNode = lNode;
                }
                if (rNode < length && nums[rNode] > nums[tempNode]) {
                    tempNode = rNode;
                }
                if (tempNode != node) {
                    //如果有子节点比父节点大,就进行交换
                    swap(nums, tempNode, node);
                }
            }
        }
    

    接着:

            for (int j = 0; j < nums.length; j++) {
                swap(nums, 0, nums.length - 1 - j);
                headAdjust(nums, nums.length - j - 1, 0);
            }
    

    循环将第一个元素(即为每一轮的最大值)与最后一个元素进行交换,要注意每一次交换过之后要再对该堆进行一个调整,使其再次成为一个大顶堆。

    调整的函数为:

        public static void headAdjust(int[] nums, int numsLength, int parentNode) {
            int lNode = parentNode * 2 + 1;
            int rNode = parentNode * 2 + 2;
            int maxNode = parentNode;
            if (lNode < numsLength && nums[lNode] > nums[maxNode]) {
                maxNode = lNode;
            }
            if (rNode < numsLength && nums[rNode] > nums[maxNode]) {
                maxNode = rNode;
            }
            if (maxNode != parentNode) {
                swap(nums, maxNode, parentNode);
                //递归调用,直到调整到叶子节点处
                headAdjust(nums, numsLength, maxNode);
            }
        }
    

    附上完整的源码:

    package sortAlgorithm;
    
    import java.util.Arrays;
    
    /**
    * Created by Max on 2016/10/29.
            */
    public class HeapSort {
    
        //将该数组初始化为大顶堆
        public static void heapInit(int[] nums) {
            //获取此二叉树最后一个非叶节点node
            int node = nums.length / 2 - 1;
            int length = nums.length;
            int tempNode;
            for (; node >= 0; node--) {
                tempNode = node;
                int lNode = node * 2 + 1;
                int rNode = node * 2 + 2;
                if (lNode < length && nums[lNode] > nums[tempNode]) {
                    tempNode = lNode;
                }
    
                if (rNode < length && nums[rNode] > nums[tempNode]) {
                    tempNode = rNode;
                }
                if (tempNode != node) {
                    //如果有子节点比父节点大,就进行交换
                    swap(nums, tempNode, node);
                }
            }
        }
    
        public static void headAdjust(int[] nums, int numsLength, int parentNode) {
            int lNode = parentNode * 2 + 1;
            int rNode = parentNode * 2 + 2;
            int maxNode = parentNode;
            if (lNode < numsLength && nums[lNode] > nums[maxNode]) {
                maxNode = lNode;
            }
            if (rNode < numsLength && nums[rNode] > nums[maxNode]) {
                maxNode = rNode;
            }
            if (maxNode != parentNode) {
                swap(nums, maxNode, parentNode);
                //递归调用,直到调整到叶子节点处
                headAdjust(nums, numsLength, maxNode);
            }
        }
    
    
        public static void swap(int[] nums, int start, int end) {
            int temp;
            temp = nums[start];
            nums[start] = nums[end];
            nums[end] = temp;
        }
    
        public static void heapSort(int[] nums) {
            //首先将数组初始化为一个大顶堆
            heapInit(nums);
            for (int j = 0; j < nums.length; j++) {
                swap(nums, 0, nums.length - 1 - j);
                headAdjust(nums, nums.length - j - 1, 0);
            }
            for (int i : nums) {
                System.out.print(i + " ");
            }
        }
    
        public static void main(String[] args) {
            int[] nums = {54, 23, 44, 78, 11, 5};
            System.out.println("排序之后:");
            heapSort(nums);
        }
    }
    

    七、归并排序

    什么是归并排序?

    归并排序的过程可以简单概括为:先将该数组进行一个递归分治的分组,然后再将每个分组的结果进行合并排序,最终形成一个有序的数组。那么,"递归分治"究竟是什么?以后就要多多了解些这方面的东西啦,然后适时地再总结一下。

    下面是利用递归分治思想进行分组的代码:

        /**
         * 归并排序,时间复杂度为O(nlogn),空间复杂度为O(n)。
         * 这里的算法蕴含了一种算法思想:递归分治
         * 何谓递归分治?即将一个大问题分解成n个小问题,然后再采用递归的方式来进行解决。
         * @param nums
         * @param start
         * @param end
         */
        public static void Sort(int[] nums, int start, int end) {
            if (start >= end) {
                return;
            }
            int mid = (start + end) / 2;
            Sort(nums, start, mid);
            Sort(nums, mid + 1, end);
            merge(nums, start, mid, end);
        }
    

    下边是对数组进行合并的代码:

        public static void merge(int[] nums, int start, int mid, int end) {
    
            int[] temp = new int[end - start + 1];
            int i = start;
            int j = mid + 1;
            int k = 0;
            while (i <= mid && j <= end) {
                if (nums[i] <= nums[j]) {
                    temp[k++] = nums[i++];
                }
                else {
                    temp[k++] = nums[j++];
                }
            }
    
            while (i <= mid) {
                temp[k++] = nums[i++];
            }
    
            while (j <= end) {
                temp[k++] = nums[j++];
            }
    
            for(int p = 0; p < temp.length; p++) {
                nums[start + p] = temp[p];
            }
        }
    

    完整代码如下:

    package sortAlgorithm;
    
    /**
     * Created by Max on 2016/10/31.
     */
    public class MergeSort {
    
        /**
         * 归并排序,时间复杂度为O(nlogn),空间复杂度为O(n)。
         * 这里的算法蕴含了一种算法思想:递归分治
         * 何谓递归分治?即将一个大问题分解成n个小问题,然后再采用递归的方式来进行解决。
         * @param nums
         * @param start
         * @param end
         */
        public static void Sort(int[] nums, int start, int end) {
            if (start >= end) {
                return;
            }
            int mid = (start + end) / 2;
            Sort(nums, start, mid);
            Sort(nums, mid + 1, end);
            merge(nums, start, mid, end);
        }
    
        public static void merge(int[] nums, int start, int mid, int end) {
    
            int[] temp = new int[end - start + 1];
            int i = start;
            int j = mid + 1;
            int k = 0;
            while (i <= mid && j <= end) {
                if (nums[i] <= nums[j]) {
                    temp[k++] = nums[i++];
                }
                else {
                    temp[k++] = nums[j++];
                }
            }
    
            while (i <= mid) {
                temp[k++] = nums[i++];
            }
    
            while (j <= end) {
                temp[k++] = nums[j++];
            }
    
            for(int p = 0; p < temp.length; p++) {
                nums[start + p] = temp[p];
            }
        }
    
    
        public static void main(String[] args) {
            int[] args1 = {5,3,8,6,4,23,34,12,54};
            Sort(args1, 0, args1.length - 1);
            for (int i : args1) {
                System.out.print(i + " ");
            }
        }
    }
    
    

    八、计数排序

    说实话,看完计数排序的介绍和算法,只能说代码看懂了,但是还不太理解其内部的原理。先把源码贴上去吧,有关该算法的分析以后补上。

    package sortAlgorithm;
    
    /**
     * Created by Max on 2016/11/1.
     */
    public class CountingSort {
    
        /**
         * 计数排序
         * 时间复杂度O(n + k) ,n为数组长度,k为输入数字的最大范围
         * @param arr
         */
        public static void countingSort(int[] arr) {
            int n = arr.length;
    
            int[] output = new int[n];
    
            int[] count = new int[256];
    
            for (int i = 0; i < 256; ++i) {
                count[i] = 0;
            }
    
            for (int i = 0; i < n; ++i) {
                ++count[arr[i]];
            }
    
            for (int i = 1; i <= 255; ++i) {
                count[i] += count[i - 1];
            }
    
            for (int i = 0; i < n; ++i) {
                output[count[arr[i]] - 1] = arr[i];
                --count[arr[i]];
            }
    
            for (int i = 0; i < n; ++i) {
                arr[i] = output[i];
            }
    
            for (int  i : arr) {
                System.out.print(i + " ");
            }
        }
    
    
        public static void main(String[] args) {
            int[] args1 = {1, 4, 1, 2, 7, 5, 2};
            countingSort(args1);
        }
    }
    
    

    九、桶排序

    什么是桶排序?

    个人感觉桶排序还是比较好理解的。之所以叫"桶排序",就是要根据一定的规则来将所有的数字分成几组装进一个个的桶(数组)中,然后在再每个桶中对所有的数字进行一个排序,这个排序一般为插入排序。

    那么这个规则是什么呢?

    通过一个函数 bindex = f (key ),key为数字大小,然后通过计算,得出每个数组属于索引为bindex的桶。废话不多说,直接上部分代码:

            int range = 10;
            List<List<Integer>> temp = new ArrayList<List<Integer>>(range);
    
            for (int i = 0; i < 10; i++) {
                temp.add(new ArrayList<Integer>());
            }
    
            for (int i = 0; i < arr.length; i++) {
    
                temp.get(arr[i] / range).add(arr[i]);
            }
    
            System.out.println("排序前==============");
            for (List<Integer> array : temp) {
                for (int i : array) {
                    System.out.print(i + " ");
                }
                System.out.println();
            }
    

    这段函数的作用是通过bindex = key / 10来获得每个相应桶的索引bindex。

    然后划分好桶,并把数字装进去之后,再对每个桶进行排序:

        public static void insertSort(List<Integer> arr) {
            arr.sort(new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    if (o1 > o2) {
                        return 1;
                    }
                    else {
                        return -1;
                    }
                }
            });
        }
    

    这样每个桶内部排序完之后,再整体输出,就是一个递增的数组啦!

    完整的代码:

    package sortAlgorithm;
    
    import java.util.*;
    
    /**
     * Created by Max on 2016/11/2.
     */
    public class BucketSort {
    
        /**
          *桶排序
          *时间复杂度O(n + k) ,n为数组长度,k为输入数字的最大范围
          */
        public static void bucketSort(int[] arr) {
            int range = 10;
            List<List<Integer>> temp = new ArrayList<List<Integer>>(range);
    
            for (int i = 0; i < 10; i++) {
                temp.add(new ArrayList<Integer>());
            }
    
            for (int i = 0; i < arr.length; i++) {
    
                temp.get(arr[i] / range).add(arr[i]);
            }
    
            System.out.println("排序前==============");
            for (List<Integer> array : temp) {
                for (int i : array) {
                    System.out.print(i + " ");
                }
                System.out.println();
            }
    
    
            for (int i = 0; i < temp.size(); i++)
    
            {
                insertSort(temp.get(i));
            }
    
            System.out.println("排序后=========");
    
            for (List<Integer> array : temp) {
                for (int i : array) {
                    System.out.print(i + " ");
                }
                System.out.println();
            }
    
    
        }
    
        public static void insertSort(List<Integer> arr) {
            arr.sort(new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    if (o1 > o2) {
                        return 1;
                    }
                    else {
                        return -1;
                    }
                }
            });
        }
    
    
        public static void swap(int[] args, int small, int big) {
            int temp;
            temp = args[big];
            args[big] = args[small];
            args[small] = temp;
        }
    
    
        public static void main(String[] args) {
            int[] nums = {49, 38, 35, 97, 73, 76, 27, 49};
            bucketSort(nums);
            for (int i : nums) {
                System.out.print(i + " ");
            }
    
        }
    }
    

    十、基数排序

    什么是基数排序?

    基数排序的大致过程可以描述为:对于一组数字,首先根据每个数字的个位数字来将其划分到0-9不等的临时数组位置中,然后将划分好的数字依次再写入到一个新的数组中,接着进行与第一步类似的操作,只不过这次是根据数字的十位来继续划分。接下来的过程依次类推,直到进行到原数组中数字最大的那个最大数位。接下来看一下部分源码:

    首先是选择出最大数字的最大位是多少位:

        private static int getMaxBit(int[] arr) {
            int max = Integer.MIN_VALUE;
            for (int ele : arr) {
                int len = (ele + "").length();
                if (len > max) {
                    max = len;
                }
            }
            return max;
        }
    

    然后根据这个最大位 maxBit 来进行循环划分:

        int maxBit = getMaxBit(arr);
        for (int i = 1; i <= maxBit; i++) {
            List<List<Integer>> buf = distribute(arr, i);
            collect(arr, buf);
        }
    

    具体的划分方法:

        private static List<List<Integer>> distribute(int[] arr, int iBit) {
            List<List<Integer>> buf = new ArrayList<List<Integer>>();
            for (int j = 0; j < 10; j++) {
                buf.add(new LinkedList<Integer>());
            }
            for (int i = 0; i < arr.length; i++) {
                System.out.println(getNBit(arr[i], iBit));
                buf.get(getNBit(arr[i], iBit)).add(arr[i]);
            }
            return buf;
        }
    

    每一次划分完之后,都要将划分好的数字再重新写到原来的数组中(或者一个新的数组。新旧无所谓,存储的只是一系列中间值)

        private static void collect(int[] arr, List<List<Integer>> buf) {
            int k = 0;
            for (List<Integer> bucket : buf) {
                for (int ele : bucket) {
                    arr[k++] = ele;
                }
            }
        }
    

    这样整个循环结束之后,最后一次写入到原数组中的数字序列就已经是有序的啦

    最后附上完整的源码:

    package sortAlgorithm;
    
    import java.util.ArrayList;
    import java.util.LinkedList;
    import java.util.List;
    
    /**
     * Created by Max on 2016/11/3.
     */
    public class RadixSort {
    
    
        /**
         * 基数排序
         * 时间复杂度为O(nlogn)
         * @param arr
         */
        public static void radixSort(int[] arr) {
            if (arr == null && arr.length == 0) {
                return;
            } else {
                int maxBit = getMaxBit(arr);
                for (int i = 1; i <= maxBit; i++) {
                    List<List<Integer>> buf = distribute(arr, i);
                    collect(arr, buf);
                }
            }
        }
    
        private static List<List<Integer>> distribute(int[] arr, int iBit) {
            List<List<Integer>> buf = new ArrayList<List<Integer>>();
            for (int j = 0; j < 10; j++) {
                buf.add(new LinkedList<Integer>());
            }
            for (int i = 0; i < arr.length; i++) {
                System.out.println(getNBit(arr[i], iBit));
                buf.get(getNBit(arr[i], iBit)).add(arr[i]);
            }
            return buf;
        }
    
    
        private static void collect(int[] arr, List<List<Integer>> buf) {
            int k = 0;
            for (List<Integer> bucket : buf) {
                for (int ele : bucket) {
                    arr[k++] = ele;
                }
            }
        }
    
    
        private static int getNBit(int x, int n) {
            String sx = x + "";
            if (sx.length() < n) {
                return 0;
            }
            else {
                // 由于charAt()方法返回值为char,要返回的类型为int,所以需要减去字符'0',最后的结果即可int
                return sx.charAt(sx.length() - n) - '0';
            }
        }
    
        private static int getMaxBit(int[] arr) {
            int max = Integer.MIN_VALUE;
            for (int ele : arr) {
                int len = (ele + "").length();
                if (len > max) {
                    max = len;
                }
            }
            return max;
        }
    
        public static void main(String[] args) {
            int[] arr = {278, 109, 63, 930, 589, 184, 505, 269, 8, 83};
            radixSort(arr);
            for (int i : arr) {
                System.out.print(i + " ");
            }
        }
    }
    

    后记:

    算法是我在大一下学习的课程,不过自从学习之后,转而做起了开发,所以算法这一部分都没有被怎么使用上,荒废了许久。临近大三下,为了响应学校号召,也要开始找工作去实习了,所以为了能被公司看上,特意又重新来回顾一下之前所学习过的各种排序算法。不过到目前为止,这一次对算法的复习自我评价为良,还算不上优,因为只是把各个算法的关键过程给重新梳理了一遍,但是针对算法的深入分析,比如时间复杂度,空间复杂度这两块,还没有进行任何的思考和学习。就目前复习过这十个排序算法而言,感觉对算法的兴趣不是特别大,可能对我来说是因为比较难吧。但是我始终应该认为,算法是一种为我提供技术支持的工具,别把它当做奥赛题那样去解答,否则会很乏味。

    相关文章

      网友评论

          本文标题:再来复习一下排序算法吧

          本文链接:https://www.haomeiwen.com/subject/nphoottx.html