Browse Source

新增排序算法

ChenGanBin 3 years ago
parent
commit
2507b6dca5

+ 33 - 0
src/main/java/com/cyl/algorithrms/sort/Counting.java

@@ -0,0 +1,33 @@
+package com.cyl.algorithrms.sort;
+
+/**
+ * 计算排序
+ */
+public class Counting {
+
+    public static void sort(int[] a, boolean ascending) {
+        int maxValue = getMaxValue(a);
+        int[] aux = new int[maxValue + 1];
+        for(int v : a) {
+            aux[v]++;
+        }
+        int ptr = ascending ? -1 : a.length;
+        int adder = ascending ? 1 : -1;
+        for(int i=0; i < aux.length; i++) {
+            for(int j=0; j < aux[i]; j++) {
+                ptr = ptr + adder;
+                a[ptr] = i;
+            }
+        }
+    }
+
+    public static int getMaxValue(int[] a) {
+        int maxValue = a[0];
+        for (int v : a) {
+            if(v > maxValue) {
+                maxValue = v;
+            }
+        }
+        return maxValue;
+    }
+}

+ 1 - 1
src/main/java/com/cyl/algorithrms/sort/HeapSort.java → src/main/java/com/cyl/algorithrms/sort/Heap.java

@@ -4,7 +4,7 @@ package com.cyl.algorithrms.sort;
  * 堆排序
  * @author cyl
  */
-public class HeapSort {
+public class Heap {
 
     public static void sort(Comparable[] a) {
         int N = a.length;

+ 59 - 37
src/main/java/com/cyl/algorithrms/sort/Insertion.java

@@ -1,52 +1,74 @@
 package com.cyl.algorithrms.sort;
 
+import java.util.Arrays;
+
 /**
  * 插入排序
  */
 public class Insertion {
 
-    public static void sort(Comparable[] a) {
-        //将a[]按升序排列
-        int N = a.length;
-        for (int i = 0; i < N; i++) {
-            for (int j = i; j > 0 && less(a[j], a[j-1]); j--) {
-                exch(a, j, j-1);
+    /**
+     * version 2.0
+     * @param a
+     * @param ascending
+     */
+    public static void sort(Comparable[] a, boolean ascending) {
+        int len = a.length;
+        // 排序方向 升序 1,降序 -1
+        int compVal = ascending ? 1 : -1;
+        // 第一个循环:控制遍历次数
+        //     i 指示待排序的元素
+        //    [0, i-1] 即已排序列表
+        //    [i, len - 1] 即待排序列表
+        //    i = 1 是因为第一个元素认为是已排序元素,从第二个元素开始进行排序
+        for (int i = 1; i <= len - 1; i++) {
+            // 第二个循环:控制待排序元素与已排序元素列表进行比较(排序),寻找可插入的位置
+            //     j 指示待排序元素的位置(i),也指示当前能够插入元素的位置
+            //    待比较序列的范围:[0, j-1]
+            //    tmp 记录当前待排序的元素
+            //    当元素 j-1、tmp 不符合排序要求时(升序、降序)进行交换
+            //        即 当升序时,tmp小于a[j-1] 等价于 tmp.compareTo(a[j-1]) == -1 不是合适的插入位置,发生元素的移动
+            //        即 当降序时,tmp大于[j-1]  等价于 tmp.compareTo(a[j-1]) == 1 不是合适的插入位置,发生元素的移动
+            //    由上面可知,j与前一个元素相比较,因此 j不能等于0 即 j>0
+            int j = i;
+            Comparable tmp = a[i];
+            for(; j>=1 && tmp.compareTo(a[j-1]) == -compVal; j--) {
+                a[j] = a[j-1];
+            }
+            // 在位置j插入待排序元素
+            if(j != i) {
+                a[j] = tmp;
             }
         }
     }
 
-    private static boolean less(Comparable v, Comparable w) {
-        return v.compareTo(w) < 0;
-    }
-
-    private static void exch(Comparable[] a, int i, int j) {
-        Comparable t = a[i];
-        a[i] = a[j];
-        a[j] = t;
-    }
-
-    private static void show(Comparable[] a) {
-        //    在单行中打印数组
-        for (int i = 0; i < a.length; i++) {
-            System.out.print(a[i] + " ");
-        }
-        System.out.println();
-    }
-
-    public static boolean isSorted(Comparable[] a) {
-        //    测试数组元素是否有序
-        for (int i = 1; i < a.length; i++) {
-            if(less(a[i], a[i-1])) {
-                return false;
+    /**
+     * 错误版:比较后多次插入元素,与插入排序的思想不符,更接近冒泡排序
+     * @param a
+     * @param ascending
+     */
+    public static void sortV0(Comparable[] a, boolean ascending) {
+        int len = a.length;
+        // 排序方向 升序 1,降序 -1
+        int compVal = ascending ? 1 : -1;
+        // 第一个循环:控制遍历次数
+        //     i 指示待排序的元素
+        //    [0, i-1] 即已排序列表
+        //    [i, len - 1] 即待排序列表
+        for (int i = 0; i < len; i++) {
+            // 第二个循环:控制待排序元素与已排序元素列表进行比较(排序)
+            //     j 指示待排序元素的位置
+            //    待比较序列的范围:[0, j-1]
+            //    当相邻元素 j-1、j 不符合排序要求时(升序、降序)进行交换
+            //        即 当升序时,a[j]小于a[j-1] 等价于 a[j].compareTo(a[j-1]) == -1
+            //        即 当降序时,a[j]大于[j-1]  等价于 a[j].compareTo(a[j-1]) == 1
+            //    由上面可知,j与前一个元素相比较,因此 j不能等于0 即 j>0
+            for (int j = i; j > 0 && a[j].compareTo(a[j-1]) == -compVal; j--) {
+                Comparable tmp = a[j];
+                a[j] = a[j-1];
+                a[j-1] = tmp;
             }
         }
-        return true;
     }
 
-    public static void main(String[] args) {
-        String[] a = "SORTEXAMPLE".split("");
-        sort(a);
-        assert isSorted(a);
-        show(a);
-    }
-}
+}

+ 74 - 0
src/main/java/com/cyl/algorithrms/sort/Merge.java

@@ -0,0 +1,74 @@
+package com.cyl.algorithrms.sort;
+
+/**
+ * 归并排序
+ */
+public class Merge {
+
+    private static Comparable[] aux;
+
+    private static int compVal;
+
+    public static void sort(Comparable[] a, boolean ascending) {
+        int len = a.length;
+        //排序方向:1 升序;-1 降序
+        compVal = ascending ? 1 : -1;
+        // 创建辅助数组
+        aux = new Comparable[len];
+        sort(a, 0, len-1);
+    }
+
+    /**
+     * 排序
+     * @param a 待排序数组
+     * @param sl 待排序的子数组开始位置
+     * @param el 待排序的子数组结束位置
+     */
+    private static void sort(Comparable[] a, int sl, int el) {
+        // 当sl=el时即只剩一个数组,无法分割和排序了
+        if(sl >= el) {
+            return;
+        }
+        int ml = sl + (el - sl)/ 2;
+        // 排序 左子数组
+        sort(a, sl, ml);
+        // 排序 右子数组
+        sort(a, ml+1, el);
+        // 对 左右子数组执行归并操作
+        merge(a, sl, ml, el);
+    }
+
+    /**
+     * 归并操作
+     * @param a 待排序数组
+     * @param sl 开始位置
+     * @param ml 分割左右子数组的位置
+     * @param el 结束位置
+     */
+    private static void merge(Comparable[] a, int sl, int ml, int el) {
+        // 把待排序的元素复制到辅助数组(每次归并都要复制一遍,因为不断的归并过程中原待排序数组的元素位置在不断改变)
+        System.arraycopy(a, sl, aux, sl, el-sl+1);
+        //左子数组范围:[sl, ml]
+        //右子数组范围:[ml, el]
+        int leftPtr=sl, rightPtr=ml+1;
+        // 两个有序子数组相比较(以升序为例)
+        // 1. 左 小于 右 => 选中左元素,左指针移动
+        // 2. 左 大于 右 => 选中右元素,右指针移动
+        // 3. (特殊情况) 左 等于 右 => 随机选择一则元素并移动对应的指针,这里选择了右子数组
+        // 子数组耗尽
+        // 1. 左指针大于ml,即左子数组已耗尽,全用右子数组
+        // 2. 右指针大于el,即右子数组已耗尽,全用左子数组
+        // 归并结束条件:归并完成,数组已排完序,也可以理解成:左指针大于 ml 且 右指针大于el,即左子数组已耗尽 且 右子数组已耗尽
+        for(int k=sl; k <= el; k++) {
+            if(leftPtr > ml) {
+                a[k] = aux[rightPtr++];
+            } else if(rightPtr > el) {
+                a[k] = aux[leftPtr++];
+            } else if (aux[leftPtr].compareTo(aux[rightPtr]) == -compVal) {
+                a[k] = aux[leftPtr++];
+            } else {
+                a[k] = aux[rightPtr++];
+            }
+        }
+    }
+}

+ 68 - 63
src/main/java/com/cyl/algorithrms/sort/Quick.java

@@ -5,92 +5,97 @@ package com.cyl.algorithrms.sort;
  */
 public class Quick {
 
-    public static void sort(Comparable[] a) {
-        sort(a, 0, a.length - 1);
+    private static int compVal;
+
+    public static void sort(Comparable[] a, boolean ascending) {
+        int len = a.length;
+        //排序方向:1 升序;-1 降序
+        compVal = ascending ? 1 : -1;
+
+        sort(a, 0, len-1);
     }
 
-    public static void sort(Comparable[] a, int lo, int hi) {
-        if(hi <= lo) {
+    /**
+     * 排序
+     * @param a 待排序数组
+     * @param sl 开始位置
+     * @param el 结束位置(包含)
+     */
+    private static void sort(Comparable[] a, int sl, int el) {
+        // 递归结束条件
+        if(sl >= el){
             return;
         }
-
-        int j = partition(a, lo, hi);
-
-        sort(a, lo, j - 1);
-        sort(a, j + 1, hi);
+        // 切分位置
+        int p = partition(a, sl, el);
+        // 排序 切分位置的左侧数组
+        sort(a, sl, p-1);
+        // 排序 切分位置的右侧数组
+        sort(a, p+1, el);
     }
 
     /**
      * 切分
+     * @param a 待排序数组
+     * @param sl 开始位置
+     * @param el 结束位置
+     * @return 切分位置
      */
-    private static int partition(Comparable[] a, int lo, int hi) {
-        //将数组切分为a[lo..i-1], a[i+1..hi]
-        //左右扫描指针
-        int i = lo, j=hi+1;
-        //切分元素
-        Comparable v = a[lo];
+    public static int partition(Comparable[] a, int sl, int el) {
+        // 记录切分元素 ,一般用第一个元素
+        Comparable p = a[sl];
+        // 注:下面以升序为例
+        // 左指针 寻找比切分元素更大(或等于)的元素,从数组最左侧开始,即 sl
+        int leftPtr = sl;
+        // 右指针 寻找比分区元素更小(或等于)的元素,从数组最右侧开始,即 el+1
+        int rightPtr = el + 1;
+        // 循环:根据 切分元素
+        //   把小于等于切分元素的都放到左侧
+        //   把大于等于切分元素的都放到左侧
         while (true) {
-            //扫描左右,检查扫描是否结束并交换位置
-
-            //寻找小于切分元素的元素
-            while (less(a[++i], v)) {
-                if (i == hi) {
+            // 从数组最左侧开始,寻找 大于或等于 切分元素 的元素
+            //  比较元素 大于等于 切分元素 暂停寻找等待交换
+            //  比较元素 小于 切分元素 继续下一个
+            // 遍历边界是右边界 el
+            while (a[++leftPtr].compareTo(p) == -compVal) {
+                // 边界判断
+                if(leftPtr == el) {
                     break;
                 }
             }
-
-            //寻找大于切分元素的元素
-            while (less(v, a[--j])) {
-                if (j == lo) {
+            // 从数组最右侧侧开始,寻找 小于或等于 切分元素 的元素
+            // 比较元素 小于等于 切分元素 暂停寻找等待交换
+            // 比较元素 大于 切分元素 继续下一个
+            // 遍历边界是左边界 sl
+            while (a[--rightPtr].compareTo(p) == compVal) {
+                if(rightPtr == sl) {
                     break;
                 }
             }
-
-            if(i >= j) {
+            // 左指针 大于等于 右指针 即 遍历结束,元素均已被放置到合适的位置
+            if (leftPtr >= rightPtr) {
                 break;
             }
-
-            exch(a, i, j);
+            //交换左右指针找到的元素
+            exch(a, leftPtr, rightPtr);
         }
-
-        //将v=a[j]放入正确的位置
-        exch(a, lo, j);
-        //a[lo..j-1] <= a[j] <= a[j+1..hi]达成
-        return j;
-    }
-
-    private static boolean less(Comparable v, Comparable w) {
-        return v.compareTo(w) < 0;
+        //把切分元素放到合适的位置
+        exch(a, sl, rightPtr);
+        //返回切分元素的位置
+        return rightPtr;
     }
 
+    /**
+     * 因为上面需要多次交换数组,因此额外提供交换元素方法
+     * 交换数组元素
+     * @param a 待交换数组
+     * @param i 待交换元素下标
+     * @param j 待交换元素下标
+     */
     private static void exch(Comparable[] a, int i, int j) {
-        Comparable t = a[i];
+        Comparable tmp = a[i];
         a[i] = a[j];
-        a[j] = t;
-    }
-
-    private static void show(Comparable[] a) {
-        //    在单行中打印数组
-        for (int i = 0; i < a.length; i++) {
-            System.out.print(a[i] + " ");
-        }
-        System.out.println();
+        a[j] = tmp;
     }
 
-    public static boolean isSorted(Comparable[] a) {
-        //    测试数组元素是否有序
-        for (int i = 1; i < a.length; i++) {
-            if(less(a[i], a[i-1])) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    public static void main(String[] args) {
-        String[] a = "QUICKSORTEXAMPLE".split("");
-        sort(a);
-        assert isSorted(a);
-        show(a);
-    }
 }

+ 27 - 48
src/main/java/com/cyl/algorithrms/sort/Shell.java

@@ -5,59 +5,38 @@ package com.cyl.algorithrms.sort;
  */
 public class Shell {
 
-    public static void sort(Comparable[] a) {
-        //将a[]按升序排列
-        int N = a.length;
-        System.out.println("N="+N);
+    public static void sort(Comparable[] a, boolean ascending) {
+        int len = a.length;
+        // 排序方向 true - 1 升序,false - -1 降序
+        int compVal = ascending ? 1 : -1;
+
+        // 递增序列:h=3*h+1 => 1,4,13,40......
+        // 递增序列的最大值小于 数组的1/3
+        // 按照递增序列来构成子数组,并对子数组进行排序
+        // 当h=1时,即为全局的插入排序
         int h = 1;
-        while (h < N / 3) {
-            // 1, 4, 13, 40, 121, 364, 1093......
+        while (h < len / 3) {
             h = 3 * h + 1;
         }
-        while (h >= 1) {
-            //将数组变为h有序
-            for (int i = h; i < N; i++) {
-                //将a[i]插入到a[i-h], a[i-2h], a[i-3*h]...之中
-                for (int j = i; j >= h && less(a[j], a[j-h]); j -= h) {
-                    exch(a, j, j-h);
-                }
-            }
-            h = h / 3;
-        }
-    }
-
-    private static boolean less(Comparable v, Comparable w) {
-        return v.compareTo(w) < 0;
-    }
-
-    private static void exch(Comparable[] a, int i, int j) {
-        Comparable t = a[i];
-        a[i] = a[j];
-        a[j] = t;
-    }
-
-    private static void show(Comparable[] a) {
-        //    在单行中打印数组
-        for (int i = 0; i < a.length; i++) {
-            System.out.print(a[i] + " ");
-        }
-        System.out.println();
-    }
 
-    public static boolean isSorted(Comparable[] a) {
-        //    测试数组元素是否有序
-        for (int i = 1; i < a.length; i++) {
-            if(less(a[i], a[i-1])) {
-                return false;
+        // 第一个循环:根据递增序列的个数,控制循环次数,根据递增序列构建子序列并进行局部排序
+        // h为两元素间的距离,按照h的距离来构成若干个子数组
+        for(; h>=1; h=h/3) {
+            // 以下的都是插入排序的内容
+            // 与插入排序相比有区别的部分:
+            //     i的起始值应为 i=1,但在希尔排序中0的下一个元素是h 即 i=h (子序列:0,h,2h,3h,4h....)
+            //     j的上一个元素位置是 j-h
+            //    即 i、j 的移动距离由1改为了h
+            for (int i=h; i < len; i++) {
+                int j=i;
+                Comparable tmp = a[i];
+                for (; j>=h && tmp.compareTo(a[j-h])==-compVal; j=j-h) {
+                    a[j]=a[j-h];
+                }
+                if(j!=i){
+                    a[j]=tmp;
+                }
             }
         }
-        return true;
-    }
-
-    public static void main(String[] args) {
-        String[] a = "SHELLSORTEXAMPLE".split("");
-        sort(a);
-        assert isSorted(a);
-        show(a);
     }
 }

+ 41 - 0
src/test/java/com/cyl/algorithrms/sort/CountingTest.java

@@ -0,0 +1,41 @@
+package com.cyl.algorithrms.sort;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.*;
+
+public class CountingTest {
+
+    private int[] origArr = null;
+
+    @Before
+    public void prepare() {
+        origArr = new int[]{1,3,4,6,7,8,7,2,5,2,4,4,3,10,20,8,9,8,2,5,0,0};
+    }
+
+    @Test
+    public void testSortAsc() {
+        int[] ascArr = new int[]{0,0,1,2,2,2,3,3,4,4,4,5,5,6,7,7,8,8,8,9,10,20};
+        Counting.sort(origArr, true);
+        assertArrayEquals(String.format("测试升序,排序结果:%s", Arrays.toString(origArr)), ascArr, origArr);
+    }
+
+    @Test
+    public void testSortDesc() {
+        int[] descArr = new int[]{20,10,9,8,8,8,7,7,6,5,5,4,4,4,3,3,2,2,2,1,0,0};
+        Counting.sort(origArr, false);
+        assertArrayEquals(String.format("测试降序,排序结果:%s", Arrays.toString(origArr)), descArr, origArr);
+    }
+
+    @Test
+    public void testGetMaxValue() {
+        int[] a = new int[]{1,2,3,10,10,6};
+        int maxValue = Counting.getMaxValue(a);
+        Assert.assertEquals(String.format("找到的最大值:%s", maxValue), 10, maxValue);
+    }
+
+}

+ 32 - 0
src/test/java/com/cyl/algorithrms/sort/InsertionTest.java

@@ -0,0 +1,32 @@
+package com.cyl.algorithrms.sort;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.*;
+
+public class InsertionTest {
+
+    private Comparable[] origArr = null;
+
+    @Before
+    public void prepare() {
+        origArr = new Comparable[]{1,3,4,6,7,8,9,8,2,5,0};
+    }
+
+    @Test
+    public void testSortAsc() {
+        Comparable[] ascArr = new Comparable[]{0,1,2,3,4,5,6,7,8,8,9};
+        Insertion.sort(origArr, true);
+        assertArrayEquals(String.format("测试升序,排序结果:%s", Arrays.toString(origArr)), ascArr, origArr);
+    }
+
+    @Test
+    public void testSortDesc() {
+        Comparable[] descArr = new Comparable[]{9,8,8,7,6,5,4,3,2,1,0};
+        Insertion.sort(origArr, false);
+        assertArrayEquals(String.format("测试降序,排序结果:%s", Arrays.toString(origArr)), descArr, origArr);
+    }
+}

+ 32 - 0
src/test/java/com/cyl/algorithrms/sort/MergeTest.java

@@ -0,0 +1,32 @@
+package com.cyl.algorithrms.sort;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.*;
+
+public class MergeTest {
+
+    private Comparable[] origArr = null;
+
+    @Before
+    public void prepare() {
+        origArr = new Comparable[]{1,3,4,6,7,8,9,8,2,5,0};
+    }
+
+    @Test
+    public void testSortAsc() {
+        Comparable[] ascArr = new Comparable[]{0,1,2,3,4,5,6,7,8,8,9};
+        Merge.sort(origArr, true);
+        assertArrayEquals(String.format("测试升序,排序结果:%s", Arrays.toString(origArr)), ascArr, origArr);
+    }
+
+    @Test
+    public void testSortDesc() {
+        Comparable[] descArr = new Comparable[]{9,8,8,7,6,5,4,3,2,1,0};
+        Merge.sort(origArr, false);
+        assertArrayEquals(String.format("测试降序,排序结果:%s", Arrays.toString(origArr)), descArr, origArr);
+    }
+}

+ 33 - 0
src/test/java/com/cyl/algorithrms/sort/QuickTest.java

@@ -0,0 +1,33 @@
+package com.cyl.algorithrms.sort;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.*;
+
+public class QuickTest {
+
+    private Comparable[] origArr = null;
+
+    @Before
+    public void prepare() {
+        origArr = new Comparable[]{1,3,4,6,7,8,9,8,2,5,0};
+    }
+
+    @Test
+    public void testSortAsc() {
+        Comparable[] ascArr = new Comparable[]{0,1,2,3,4,5,6,7,8,8,9};
+        Quick.sort(origArr, true);
+        assertArrayEquals(String.format("测试升序,排序结果:%s", Arrays.toString(origArr)), ascArr, origArr);
+    }
+
+    @Test
+    public void testSortDesc() {
+        Comparable[] descArr = new Comparable[]{9,8,8,7,6,5,4,3,2,1,0};
+        Quick.sort(origArr, false);
+        assertArrayEquals(String.format("测试降序,排序结果:%s", Arrays.toString(origArr)), descArr, origArr);
+    }
+}

+ 33 - 0
src/test/java/com/cyl/algorithrms/sort/ShellTest.java

@@ -0,0 +1,33 @@
+package com.cyl.algorithrms.sort;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.*;
+
+public class ShellTest {
+
+    private Comparable[] origArr = null;
+
+    @Before
+    public void prepare() {
+        origArr = new Comparable[]{1,3,4,6,7,8,9,8,2,5,0};
+    }
+
+    @Test
+    public void testSortAsc() {
+        Comparable[] ascArr = new Comparable[]{0,1,2,3,4,5,6,7,8,8,9};
+        Shell.sort(origArr, true);
+        assertArrayEquals(String.format("测试升序,排序结果:%s", Arrays.toString(origArr)), ascArr, origArr);
+    }
+
+    @Test
+    public void testSortDesc() {
+        Comparable[] descArr = new Comparable[]{9,8,8,7,6,5,4,3,2,1,0};
+        Shell.sort(origArr, false);
+        assertArrayEquals(String.format("测试降序,排序结果:%s", Arrays.toString(origArr)), descArr, origArr);
+    }
+
+}