美文网首页
算法(01)排序

算法(01)排序

作者: 迷心迷 | 来源:发表于2020-01-11 16:33 被阅读0次

    //工具类
    //断言类

    public class Asserts {
        public static void test(boolean value) {
            try {
                if (!value) throw new Exception("测试未通过");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    //数组生成类

    public class Integers {
        public static Integer[] random(int count, int min, int max) {
            if (count <= 0 || min > max) return null;
            Integer[] array = new Integer[count];
            int delta = max - min + 1;
            for (int i = 0; i < count; i++) {
                array[i] = min + (int)(Math.random() * delta);
            }
            return array;
        }
        
        public static Integer[] combine(Integer[] array1, Integer[] array2) {
            if (array1 == null || array2 == null) return null;
            Integer[] array = new Integer[array1.length + array2.length];
            for (int i = 0; i < array1.length; i++) {
                array[i] = array1[i];
            }
            for (int i = 0; i < array2.length; i++) {
                array[i + array1.length] = array2[i];
            }
            return array;
            
        }
        
        public static Integer[] same(int count, int unsameCount) {
            if (count <= 0 || unsameCount > count) return null;
            Integer[] array = new Integer[count];
            for (int i = 0; i < unsameCount; i++) {
                array[i] = unsameCount - i;
            }
            for (int i = unsameCount; i < count; i++) {
                array[i] = unsameCount + 1;
            }
            return array;
        }
        
        public static Integer[] headTailAscOrder(int min, int max, int disorderCount) {
            Integer[] array = ascOrder(min, max);
            if (disorderCount > array.length) return array;
            
            int begin = (array.length - disorderCount) >> 1;
            reverse(array, begin, begin + disorderCount);
            return array;
        }
        
        public static Integer[] centerAscOrder(int min, int max, int disorderCount) {
            Integer[] array = ascOrder(min, max);
            if (disorderCount > array.length) return array;
            int left = disorderCount >> 1;
            reverse(array, 0, left);
            
            int right = disorderCount - left;
            reverse(array, array.length - right, array.length);
            return array;
        }
        
        public static Integer[] headAscOrder(int min, int max, int disorderCount) {
            Integer[] array = ascOrder(min, max);
            if (disorderCount > array.length) return array;
            reverse(array, array.length - disorderCount, array.length);
            return array;
        }
        
        public static Integer[] tailAscOrder(int min, int max, int disorderCount) {
            Integer[] array = ascOrder(min, max);
            if (disorderCount > array.length) return array;
            reverse(array, 0, disorderCount);
            return array;
        }
        
        public static Integer[] ascOrder(int min, int max) {
            if (min > max) return null;
            Integer[] array = new Integer[max - min + 1];
            for (int i = 0; i < array.length; i++) {
                array[i] = min++;
            }
            return array;
        }
        
        public static Integer[] descOrder(int min, int max) {
            if (min > max) return null;
            Integer[] array = new Integer[max - min + 1];
            for (int i = 0; i < array.length; i++) {
                array[i] = max--;
            }
            return array;
        }
        
        /**
         * 反转一个数组,索引范围是[begin, end)
         */
        private static void reverse(Integer[] array, int begin, int end) {
            int count = (end - begin) >> 1;
            int sum = begin + end - 1;
            for (int i = begin; i < begin + count; i++) {
                int j = sum - i;
                int tmp = array[i];
                array[i] = array[j];
                array[j] = tmp;
            }
        }
        
        public static Integer[] copy(Integer[] array) {
            return Arrays.copyOf(array, array.length);
        }
        
        public static boolean isAscOrder(Integer[] array) {
            if (array == null || array.length == 0) return false;
            for (int i = 1; i < array.length; i++) {
                if (array[i - 1] > array[i]) return false;
            }
            return true;
        }
        
        public static void println(Integer[] array) {
            if (array == null) return;
            StringBuilder string = new StringBuilder();
            for (int i = 0; i < array.length; i++) {
                if (i != 0) string.append("_");
                string.append(array[i]);
            }
            System.out.println(string);
        }
    }
    
    

    //时间测试类

    public class Times {
        private static final SimpleDateFormat fmt = new SimpleDateFormat("HH:mm:ss.SSS");
        
        public interface Task {
            void execute();
        }
        
        public static void test(String title, Task task) {
            if (task == null) return;
            title = (title == null) ? "" : ("【" + title + "】");
            System.out.println(title);
            System.out.println("开始:" + fmt.format(new Date()));
            long begin = System.currentTimeMillis();
            task.execute();
            long end = System.currentTimeMillis();
            System.out.println("结束:" + fmt.format(new Date()));
            double delta = (end - begin) / 1000.0;
            System.out.println("耗时:" + delta + "秒");
            System.out.println("-------------------------------------");
        }
    }
    
    

    //排序

    @SuppressWarnings("unchecked")
    public abstract class Sort<T extends Comparable<T>> implements Comparable<Sort<T>> {
        protected T[] array;
        private int cmpCount;
        private int swapCount;
        private long time;
        private DecimalFormat fmt = new DecimalFormat("#.00");
        
        public void sort(T[] array) {
            if (array == null || array.length < 2) return;
            
            this.array = array;
            
            long begin = System.currentTimeMillis();
            sort();
            time = System.currentTimeMillis() - begin;
        }
        
        @Override
        public int compareTo(Sort<T> o) {
            int result = (int)(time - o.time);
            if (result != 0) return result;
            
            result = cmpCount - o.cmpCount;
            if (result != 0) return result;
            
            return swapCount - o.swapCount;
        }
        
        protected abstract void sort();
        
        /*
         * 返回值等于0,代表 array[i1] == array[i2]
         * 返回值小于0,代表 array[i1] < array[i2]
         * 返回值大于0,代表 array[i1] > array[i2]
         */
        protected int cmp(int i1, int i2) {
            cmpCount++;
            return array[i1].compareTo(array[i2]);
        }
        
        protected int cmp(T v1, T v2) {
            cmpCount++;
            return v1.compareTo(v2);
        }
        
        protected void swap(int i1, int i2) {
            swapCount++;
            T tmp = array[i1];
            array[i1] = array[i2];
            array[i2] = tmp;
        }
        
        @Override
        public String toString() { 
            String timeStr = "耗时:" + (time / 1000.0) + "s(" + time + "ms)";
            String compareCountStr = "比较:" + numberString(cmpCount);
            String swapCountStr = "交换:" + numberString(swapCount);
            String stableStr = "稳定性:" + isStable();
            return "【" + getClass().getSimpleName() + "】\n" 
                    + stableStr + " \t"
                    + timeStr + " \t"
                    + compareCountStr + "\t "
                    + swapCountStr + "\n"
                    + "------------------------------------------------------------------";
    
        }
        
        private String numberString(int number) {
            if (number < 10000) return "" + number;
            
            if (number < 100000000) return fmt.format(number / 10000.0) + "万";
            return fmt.format(number / 100000000.0) + "亿";
        }
        
        private boolean isStable() {
            if (this instanceof RadixSort) return true;
            if (this instanceof CountingSort) return true;
            if (this instanceof ShellSort) return false;
            if (this instanceof SelectionSort) return false;
            Student[] students = new Student[20];
            for (int i = 0; i < students.length; i++) {
                students[i] = new Student(i * 10, 10);
            }
            sort((T[]) students);
            for (int i = 1; i < students.length; i++) {
                int score = students[i].score;
                int prevScore = students[i - 1].score;
                if (score != prevScore + 10) return false;
            }
            return true;
        }
    }
    
    

    冒泡

    • 冒泡排序也叫做起泡排序
    • 执行流程(本课程统一以升序为例子)
      1、 从头开始比较每一对相邻元素,如果第1个比第2个大,就交换它们的位置 ✓ 执行完一轮后,最末尾那个元素就是最大的元素
      2 、忽略 1 中曾经找到的最大元素,重复执行步骤 1,直到全部元素有序
    public class BubbleSort1<T extends Comparable<T>> extends Sort<T> {
    
        @Override
        protected void sort() {
            for (int end = array.length - 1; end > 0; end--) {
                for (int begin = 1; begin <= end; begin++) {
                    // if (array[begin] < array[begin - 1]) {
                    if (cmp(begin, begin - 1) < 0) {
                        swap(begin, begin - 1);
                    }
                }
            }
        }
    
    }
    
    public class BubbleSort2<T extends Comparable<T>> extends Sort<T> {
    
        @Override
        protected void sort() {
            for (int end = array.length - 1; end > 0; end--) {
                boolean sorted = true;
                for (int begin = 1; begin <= end; begin++) {
                    // if (array[begin] < array[begin - 1]) {
                    if (cmp(begin, begin - 1) < 0) {
                        swap(begin, begin - 1);
                        sorted = false;
                    }
                }
                if (sorted) break;
            }
        }
    
    }
    
    public class BubbleSort3<T extends Comparable<T>> extends Sort<T> {
    
        @Override
        protected void sort() {
            for (int end = array.length - 1; end > 0; end--) {
                int sortedIndex = 1;
                for (int begin = 1; begin <= end; begin++) {
                    // if (array[begin] < array[begin - 1]) {
                    if (cmp(begin, begin - 1) < 0) {
                        swap(begin, begin - 1);
                        sortedIndex = begin;
                    }
                }
                end = sortedIndex;
            }
        }
    
    }
    

    选择

    • 执行流程
      1 从序列中找出最大的那个元素,然后与最末尾的元素交换位置 ✓ 执行完一轮后,最末尾的那个元素就是最大的元素
      2 忽略 1 中曾经找到的最大元素,重复执行步骤 1

    • 思考
      选择排序是否还有优化的空间? ✓ 使用堆来选择最大值

    • 选择排序的交换次数要远远少于冒泡排序,平均性能优于冒泡排序

    • 最好、最坏、平均时间复杂度:O(n2),空间复杂度:O(1),属于不稳定排序

    public class SelectionSort<T extends Comparable<T>> extends Sort<T> {
    
        @Override
        protected void sort() {
            
    //      for (int end = array.length - 1; end > 0; end--) {
    //          int max = 0;
    //          for (int begin = 1; begin <= end; begin++) {
    //              if (cmp(max, begin) <= 0) {
    //                  max = begin;
    //              }
    //          }
    //          swap(max, end);
    //      }
            
            for (int end = array.length - 1; end > 0; end--) {
                int max = 0;
                for (int begin = 1; begin <= end; begin++) {
                    if (cmp(max, begin) < 0) {
                        max = begin;
                    }
                }
                swap(max, end);
            }
            
            // 7 5 10 1 4 2 10 
        }
    
    }
    
    

    堆排序

    • 堆排序可以认为是对选择排序的一种优化
    • 执行流程
      1 对序列进行原地建堆(heapify)
      2 重复执行以下操作,直到堆的元素数量为 1
      ✓ 交换堆顶元素与尾元素
      ✓堆的元素数量减 1
      ✓对 0 位置进行 1 次 siftDown 操作
    public class HeapSort<T extends Comparable<T>> extends Sort<T> {
        private int heapSize;
    
        @Override
        protected void sort() {
            // 原地建堆
            heapSize = array.length;
            for (int i = (heapSize >> 1) - 1; i >= 0; i--) {
                siftDown(i);
            }
            
            while (heapSize > 1) {
                // 交换堆顶元素和尾部元素
                swap(0, --heapSize);
    
                // 对0位置进行siftDown(恢复堆的性质)
                siftDown(0);
            }
        }
        
        private void siftDown(int index) {
            T element = array[index];
            
            int half = heapSize >> 1;
            while (index < half) { // index必须是非叶子节点
                // 默认是左边跟父节点比
                int childIndex = (index << 1) + 1;
                T child = array[childIndex];
                
                int rightIndex = childIndex + 1;
                // 右子节点比左子节点大
                if (rightIndex < heapSize && 
                        cmp(array[rightIndex], child) > 0) { 
                    child = array[childIndex = rightIndex];
                }
                
                // 大于等于子节点
                if (cmp(element, child) >= 0) break;
                
                array[index] = child;
                index = childIndex;
            }
            array[index] = element;
        }
    }
    
    

    插入排序

    • 插入排序非常类似于扑克牌的排序
    • 执行流程
      1 在执行过程中,插入排序会将序列分为2部分
      ✓ 头部是已经排好序的,尾部是待排序的
      2 从头开始扫描每一个元素
      ✓ 每当扫描到一个元素,就将它插入到头部合适的位置,使得头部数据依然保持有序
    public class InsertionSort1<T extends Comparable<T>> extends Sort<T> {
    
        @Override
        protected void sort() {
            for (int begin = 1; begin < array.length; begin++) {
                int cur = begin;
                while (cur > 0 && cmp(cur, cur - 1) < 0) {
                    swap(cur, cur - 1);
                    cur--;
                }
            }
        }
    
    }
    
    
    public class InsertionSort2<T extends Comparable<T>> extends Sort<T> {
    
        @Override
        protected void sort() {
            for (int begin = 1; begin < array.length; begin++) {
                int cur = begin;
                T v = array[cur];
                while (cur > 0 && cmp(v, array[cur - 1]) < 0) {
                    array[cur] = array[cur - 1];
                    cur--;
                }
                array[cur] = v;
            }
        }
    
    }
    
    public class InsertionSort3<T extends Comparable<T>> extends Sort<T> {
    
        
    //  protected void sort() {
    //      for (int begin = 1; begin < array.length; begin++) {
    //          T v = array[begin];
    //          int insertIndex = search(begin);
    //          // 将 [insertIndex, begin) 范围内的元素往右边挪动一个单位
    ////            for (int i = begin - 1; i >= insertIndex; i--) {
    ////                
    ////            }
    //          for (int i = begin; i > insertIndex; i--) {
    //              array[i] = array[i - 1];
    //          }
    //          array[insertIndex] = v;
    //      }
    //  }
        
        @Override
        protected void sort() {
            for (int begin = 1; begin < array.length; begin++) {
                insert(begin, search(begin));
            }
        }
        
        /**
         * 将source位置的元素插入到dest位置
         * @param source
         * @param dest
         */
        private void insert(int source, int dest) {
            T v = array[source];
            for (int i = source; i > dest; i--) {
                array[i] = array[i - 1];
            }
            array[dest] = v;
        }
        
        /**
         * 利用二分搜索找到 index 位置元素的待插入位置
         * 已经排好序数组的区间范围是 [0, index)
         * @param index
         * @return
         */
        private int search(int index) {
            int begin = 0;
            int end = index;
            while (begin < end) {
                int mid = (begin + end) >> 1;
                if (cmp(array[index], array[mid]) < 0) {
                    end = mid;
                } else {
                    begin = mid + 1;
                }
            }
            return begin;
        }
    }
    
    

    归并排序

    • 1945年由约翰·冯·诺伊曼(John von Neumann)首次提出

    • 执行流程
      1 不断地将当前序列平均分割成2个子序列
      ✓ 直到不能再分割(序列中只剩1个元素)
      2 不断地将2个子序列合并成一个有序序列
      ✓ 直到最终只剩下1个有序序列

    • 归并排序花费的时间
      T(n) =2∗T(n/2) +O(n)
      T1 =O(1)
      T(n) /n=T(n/2) /(n/2)+O(1)

    • 令S(n) =T(n)/n
      S(1) =O(1)
      S(n) =S(n/2) +O(1)=S(n/4) +O(2)=S(n/8) +O(3)=S(n/2k) +O(k) =S(1)+O(logn)=O(logn)
      T(n) =n∗S(n) =O(nlogn)

    • 由于归并排序总是平均分割子序列,所以最好、最坏、平均时间复杂度都是 O(nlogn) ,属于稳定排序

    • 从代码中不难看出:归并排序的空间复杂度是On/2+logn =O(n)
      n/2 用于临时存放左侧数组,logn 是因为递归调用

    @SuppressWarnings("unchecked")
    public class MergeSort<T extends Comparable<T>> extends Sort<T> {
        private T[] leftArray;
    
        @Override
        protected void sort() {
            leftArray = (T[]) new Comparable[array.length >> 1];
            sort(0, array.length);
        }
        
        // T(n) = T(n/2) + T(n/2) + O(n)
        
        /**
         * 对 [begin, end) 范围的数据进行归并排序
         */
        private void sort(int begin, int end) {
            if (end - begin < 2) return;
            
            int mid = (begin + end) >> 1;
            sort(begin, mid);
            sort(mid, end);
            merge(begin, mid, end);
        }
        
        /**
         * 将 [begin, mid) 和 [mid, end) 范围的序列合并成一个有序序列
         */
        private void merge(int begin, int mid, int end) {
            int li = 0, le = mid - begin;
            int ri = mid, re = end;
            int ai = begin;
            
            // 备份左边数组
            for (int i = li; i < le; i++) {
                leftArray[i] = array[begin + i];
            }
            
            // 如果左边还没有结束
            while (li < le) { 
                if (ri < re && cmp(array[ri], leftArray[li]) < 0) {
                    array[ai++] = array[ri++];
                } else {
                    array[ai++] = leftArray[li++];
                }
            }
        }
    }
    
    
    常见的递推式与复杂度

    递推式 ---------------------> 复杂度
    T(n) = T(n/2) + O(1) -------> O(logn)
    T(n) =T(n−1) +O(1) --------> O(n)
    T(n) = T(n/2) + O(n)-------->O(n)
    T(n )=2∗T(n/2) +O(1)------->O(n)
    T(n )=2∗T(n/2) +O(n)------->O(nlogn)
    T(n)=T(n−1) +O(n)---------->O(n^2)
    T(n) =2∗T(n−1) +O(1)------->O(2^n)
    T(n) =2∗T(n−1) +O(n)------->O(2^n)

    快速排序

    • 1960年由查尔斯·安东尼·理查德·霍尔(Charles Antony Richard Hoare,缩写为C. A. R. Hoare)提出
      昵称为东尼·霍尔(Tony Hoare)

    • 执行流程
      1 从序列中选择一个轴点元素(pivot)
      ✓假设每次选择 0 位置的元素为轴点元素
      2 利用 pivot 将序列分割成 2 个子序列
      ✓将小于 pivot 的元素放在pivot前面(左侧)
      ✓将大于 pivot 的元素放在pivot后面(右侧)
      ✓ 等于pivot的元素放哪边都可以
      3 对子序列进行 1 2 操作
      ✓ 直到不能再分割(子序列中只剩下1个元素)

    • 快速排序的本质
      逐渐将每一个元素都转换成轴点元素

    – 时间复杂度
    ◼ 在轴点左右元素数量比较均匀的情况下,同时也是最好的情况
    T(n) =2∗T(n/2) +O n =O(nlogn)
    ◼ 如果轴点左右元素数量极度不均匀,最坏情况 T(n) =T(n−1) +O(n) =O(n^2)
    ◼ 为了降低最坏情况的出现概率,一般采取的做法是 随机选择轴点元素
    ◼ 最好、平均时间复杂度:O(nlogn)
    ◼ 最坏时间复杂度:O(n^2)
    ◼ 由于递归调用的缘故,空间复杂度:O(logn)
    ◼ 属于不稳定排序

    public class QuickSort<T extends Comparable<T>> extends Sort<T> {
    
        @Override
        protected void sort() {
            sort(0, array.length);
        }
    
        /**
         * 对 [begin, end) 范围的元素进行快速排序
         * @param begin
         * @param end
         */
        private void sort(int begin, int end) { 
            if (end - begin < 2) return;
            
            // 确定轴点位置 O(n)
            int mid = pivotIndex(begin, end);
            // 对子序列进行快速排序
            sort(begin, mid); 
            sort(mid + 1, end); 
        } 
        
        /**
         * 构造出 [begin, end) 范围的轴点元素
         * @return 轴点元素的最终位置
         */
        private int pivotIndex(int begin, int end) {
            // 随机选择一个元素跟begin位置进行交换
            swap(begin, begin + (int)(Math.random() * (end - begin)));
            
            // 备份begin位置的元素
            T pivot = array[begin];
            // end指向最后一个元素
            end--;
            
            while (begin < end) {
                while (begin < end) {
                    if (cmp(pivot, array[end]) < 0) { // 右边元素 > 轴点元素
                        end--;
                    } else { // 右边元素 <= 轴点元素
                        array[begin++] = array[end];
                        break;
                    }
                }
                while (begin < end) {
                    if (cmp(pivot, array[begin]) > 0) { // 左边元素 < 轴点元素
                        begin++;
                    } else { // 左边元素 >= 轴点元素
                        array[end--] = array[begin];
                        break;
                    }
                }
            }
            
            // 将轴点元素放入最终的位置
            array[begin] = pivot;
            // 返回轴点元素的位置
            return begin;
        }
    }
    
    

    希尔排序

    • 1959年由唐纳德·希尔(Donald Shell)提出
    • 希尔排序把序列看作是一个矩阵,分成 𝑚 列,逐列进行排序
      𝑚 从某个整数逐渐减为1
      当 𝑚 为1时,整个序列将完全有序
    • 因此,希尔排序也被称为递减增量排序(Diminishing Increment Sort)
    • 矩阵的列数取决于步长序列(step sequence)
      ✓ 比如,如果步长序列为{1,5,19,41,109,...},就代表依次分成109列、41列、19列、5列、1列进行排序
      ✓ 不同的步长序列,执行效率也不同
    @SuppressWarnings("unused")
    public class ShellSort<T extends Comparable<T>> extends Sort<T> {
    
        @Override
        protected void sort() {
            List<Integer> stepSequence = sedgewickStepSequence();
            for (Integer step : stepSequence) {
                sort(step);
            }
        }
        
        /**
         * 分成step列进行排序
         */
        private void sort(int step) {
            // col : 第几列,column的简称
            for (int col = 0; col < step; col++) { // 对第col列进行排序
                // col、col+step、col+2*step、col+3*step
                for (int begin = col + step; begin < array.length; begin += step) {
                    int cur = begin;
                    while (cur > col && cmp(cur, cur - step) < 0) {
                        swap(cur, cur - step);
                        cur -= step;
                    }
                }
            }
        }
        
        private List<Integer> shellStepSequence() {
            List<Integer> stepSequence = new ArrayList<>();
            int step = array.length;
            while ((step >>= 1) > 0) {
                stepSequence.add(step);
            }
            return stepSequence;
        }
        
        private List<Integer> sedgewickStepSequence() {
            List<Integer> stepSequence = new LinkedList<>();
            int k = 0, step = 0;
            while (true) {
                if (k % 2 == 0) {
                    int pow = (int) Math.pow(2, k >> 1);
                    step = 1 + 9 * (pow * pow - pow);
                } else {
                    int pow1 = (int) Math.pow(2, (k - 1) >> 1);
                    int pow2 = (int) Math.pow(2, (k + 1) >> 1);
                    step = 1 + 8 * pow1 * pow2 - 6 * pow2;
                }
                if (step >= array.length) break;
                stepSequence.add(0, step);
                k++;
            }
            return stepSequence;
        }
    }
    
    
    计数
    • 之前学习的冒泡、选择、插入、归并、快速、希尔、堆排序,都是基于比较的排序
      平均时间复杂度目前最低是 O(nlogn)
    • 计数排序、桶排序、基数排序,都不是基于比较的排序
      它们是典型的用空间换时间,在某些时候,平均时间复杂度可以比 O(nlogn)更低
    • 计数排序于1954年由Harold H. Seward提出,适合对一定范围内的整数进行排序
    • 计数排序的核心思想
      统计每个整数在序列中出现的次数,进而推导出每个整数在有序序列中的索引
    public class CountingSort extends Sort<Integer> {
    
        @Override
        protected void sort() {
            // 找出最值
            int max = array[0];
            int min = array[0];
            for (int i = 1; i < array.length; i++) {
                if (array[i] > max) {
                    max = array[i];
                }
                if (array[i] < min) {
                    min = array[i];
                }
            }
            
            // 开辟内存空间,存储次数
            int[] counts = new int[max - min + 1];
            // 统计每个整数出现的次数
            for (int i = 0; i < array.length; i++) {
                counts[array[i] - min]++;
            }
            // 累加次数
            for (int i = 1; i < counts.length; i++) {
                counts[i] += counts[i - 1];
            }
            
            // 从后往前遍历元素,将它放到有序数组中的合适位置
            int[] newArray = new int[array.length];
            for (int i = array.length - 1; i >= 0; i--) {
                newArray[--counts[array[i] - min]] = array[i];
            }
            
            // 将有序数组赋值到array
            for (int i = 0; i < newArray.length; i++) {
                array[i] = newArray[i];
            }
        }
        
        protected void sort0() {
            // 找出最大值
            int max = array[0];
            for (int i = 1; i < array.length; i++) {
                if (array[i] > max) {
                    max = array[i];
                }
            } // O(n)
            
            // 开辟内存空间,存储每个整数出现的次数
            int[] counts = new int[1 + max];
            // 统计每个整数出现的次数
            for (int i = 0; i < array.length; i++) {
                counts[array[i]]++;
            } // O(n)
            
            // 根据整数的出现次数,对整数进行排序
            int index = 0;
            for (int i = 0; i < counts.length; i++) {
                while (counts[i]-- > 0) {
                    array[index++] = i;
                }
            } // O(n)
        }   
        
        public static void main(String[] args) {
            Person[] persons = new Person[] {
                    new Person(20, "A"),
                    new Person(-13, "B"),
                    new Person(17, "C"),
                    new Person(12, "D"),
                    new Person(-13, "E"),
                    new Person(20, "F")
            };
            
            // 找出最值
            int max = persons[0].age;
            int min = persons[0].age;
            for (int i = 1; i < persons.length; i++) {
                if (persons[i].age > max) {
                    max = persons[i].age;
                }
                if (persons[i].age < min) {
                    min = persons[i].age;
                }
            }
            
            // 开辟内存空间,存储次数
            int[] counts = new int[max - min + 1];
            // 统计每个整数出现的次数
            for (int i = 0; i < persons.length; i++) {
                counts[persons[i].age - min]++;
            }
            // 累加次数
            for (int i = 1; i < counts.length; i++) {
                counts[i] += counts[i - 1];
            }
            
            // 从后往前遍历元素,将它放到有序数组中的合适位置
            Person[] newArray = new Person[persons.length];
            for (int i = persons.length - 1; i >= 0; i--) {
                newArray[--counts[persons[i].age - min]] = persons[i];
            }
            
            // 将有序数组赋值到array
            for (int i = 0; i < newArray.length; i++) {
                persons[i] = newArray[i];
            }
            
            for (int i = 0; i < persons.length; i++) {
                System.out.println(persons[i]);
            }
        }
        
        private static class Person {
            int age;
            String name;
            Person(int age, String name) {
                this.age = age;
                this.name = name;
            }
            @Override
            public String toString() {
                return "Person [age=" + age 
                        + ", name=" + name + "]";
            }
        }
    }
    
    

    基数

    • 基数排序非常适合用于整数排序(尤其是非负整数),因此本课程只演示对非负整数进行基数排序
    • 执行流程:依次对个位数、十位数、百位数、千位数、万位数...进行排序(从低位到高位)
    • 个位数、十位数、百位数的取值范围都是固定的0~9,可以使用计数排序对它们进行排序
    public class RadixSort extends Sort<Integer> {
    
        @Override
        protected void sort() {
            // 找出最大值
            int max = array[0];
            for (int i = 1; i < array.length; i++) {
                if (array[i] > max) {
                    max = array[i];
                }
            }
            
            // 个位数: array[i] / 1 % 10 = 3
            // 十位数:array[i] / 10 % 10 = 9
            // 百位数:array[i] / 100 % 10 = 5
            // 千位数:array[i] / 1000 % 10 = ...
    
            for (int divider = 1; divider <= max; divider *= 10) {
                countingSort(divider);
            }
        }
        
        protected void countingSort(int divider) {
            // 开辟内存空间,存储次数
            int[] counts = new int[10];
            // 统计每个整数出现的次数
            for (int i = 0; i < array.length; i++) {
                counts[array[i] / divider % 10]++;
            }
            // 累加次数
            for (int i = 1; i < counts.length; i++) {
                counts[i] += counts[i - 1];
            }
            
            // 从后往前遍历元素,将它放到有序数组中的合适位置
            int[] newArray = new int[array.length];
            for (int i = array.length - 1; i >= 0; i--) {
                newArray[--counts[array[i] / divider % 10]] = array[i];
            }
            
            // 将有序数组赋值到array
            for (int i = 0; i < newArray.length; i++) {
                array[i] = newArray[i];
            }
        }
    }
    
    

    桶排序

    • 执行流程
      1 创建一定数量的桶(比如用数组、链表作为桶)
      2 按照一定的规则(不同类型的数据,规则不同),将序列中的元素均匀分配到对应的桶
      3 分别对每个桶进行单独排序
      4 将所有非空桶的元素合并成有序序列

    • 元素在桶中的索引
      元素值 * 元素数量

    相关文章

      网友评论

          本文标题:算法(01)排序

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