Browse Source

新增:二分查找、桶排序、插入查找、基数排序、顺序查找

ChenGanBin 3 years ago
parent
commit
6c92118789

+ 50 - 0
src/main/java/com/cyl/algorithrms/search/Binary.java

@@ -0,0 +1,50 @@
+package com.cyl.algorithrms.search;
+
+/**
+ * 二分查找
+ */
+public class Binary {
+
+    /**
+     * 查找
+     * @param a 待查找列表
+     * @param key 查找的关键字
+     * @return 查找失败 -1,查找成功返回所在位置
+     */
+    public static int search(Comparable[] a, Comparable key) {
+        //确定待排序序列(有序)是升序还是降序排列
+        int compVal = a[a.length-1].compareTo(a[0]);
+
+        //注释以查找序列是升序序列为例
+
+        // 查找开始位置
+        int sl = 0;
+        // 查找结束位置
+        int el = a.length-1;
+        // 查找的中间位置
+        int ml;
+        while (sl<=el) {
+            //ml = (sl+el)/2 这种计算方法不行,当sl和el的值都很大时会发生溢出
+            // 计算 中间位置,后续关键字就和ml位置对应的元素进行比较
+            ml = sl + (el-sl) / 2;
+            // 关键字与列表中的关键字进行比较
+            if(key.compareTo(a[ml]) == compVal) {
+                // 如果关键字更大 则 开始位置=中间位置+1
+                sl = ml+1;
+            } else if(key.compareTo(a[ml]) == -compVal) {
+                // 如果关键字更小 则 结束位置=中间位置-1
+                el = ml-1;
+            } else {
+                // 如果找到对应元素则结束查找,并返回对应位置
+                // 前面已判断了大于或者小于,只剩下等于这种情况了
+                // key.compareTo(a[ml]) == 0
+                return ml;
+            }
+            // 升序和降序序列中查找时,关键字与ml位置的元素的比较结果对开始位置和结束位置的影响
+            // 升序 k>ml [sl=ml+1,el] k<ml [sl,el=ml-1]
+            // 降序 k>ml [sl, el=ml-1] k<ml [sl=ml+1, el]
+        }
+        // 没有找到符合的元素,查找失败
+        return -1;
+    }
+}

+ 31 - 0
src/main/java/com/cyl/algorithrms/search/Insertion.java

@@ -0,0 +1,31 @@
+package com.cyl.algorithrms.search;
+
+/**
+ * 插值查找
+ */
+public class Insertion {
+
+    public static int search(Comparable[] a, Comparable key) {
+        int compVal = a[a.length-1].compareTo(a[0]);
+
+//        mid=low+(high-low)*(key-a[low])/(a[high]-a[low])
+        int sl = 0;
+        int el = a.length-1;
+        int ml = 0;
+        while (sl<=el) {
+//            ml = sl + (el-sl)/2;
+            ml = sl + (el-sl)*((int)key-(int)a[sl])/((int)a[el]-(int)a[sl]);
+            //Mid = Begin + ( (End - Begin) / (A[End] - A[Begin]) ) * (X - A[Begin])
+//            ml = sl + ((el-sl)/((int)a[el]-(int)a[sl])) * ((int)key-(int)a[sl]);
+            if(key.compareTo(a[ml])==compVal) {
+                sl=ml+1;
+            }else if(key.compareTo(a[ml])==-compVal) {
+                el=ml-1;
+            }else {
+                return ml;
+            }
+        }
+
+        return -1;
+    }
+}

+ 16 - 0
src/main/java/com/cyl/algorithrms/search/Sequence.java

@@ -0,0 +1,16 @@
+package com.cyl.algorithrms.search;
+
+/**
+ * 顺序查找
+ */
+public class Sequence {
+
+    public static int search(Comparable[] a, Comparable key) {
+        for(int i=0; i<a.length; i++) {
+            if(key.compareTo(a[i])==0) {
+                return i;
+            }
+        }
+        return -1;
+    }
+}

+ 86 - 0
src/main/java/com/cyl/algorithrms/sort/Bucket.java

@@ -0,0 +1,86 @@
+package com.cyl.algorithrms.sort;
+
+import java.util.Arrays;
+
+/**
+ * 桶排序(分治思想的应用)
+ */
+public class Bucket {
+
+    /**
+     * 每个桶的范围(这里假定是10,即0-9,10-19,20-29...以此类推)
+     */
+    private static final int BUCKET_RANGE = 10;
+
+    /**
+     * 桶的数量
+     */
+    //private static final int BUCKET_COUNT = 6;
+
+    /**
+     * 辅助数组,用于存放数据分桶结果
+     */
+    private static Comparable[][] buckets;
+
+    public static void sort(Comparable[] a, boolean ascending) {
+
+        // 找到数组中的 最大值和最小值
+        Comparable minValue = a[0];
+        Comparable maxValue = a[0];
+        for (Comparable v : a) {
+            if(v.compareTo(minValue) == -1) {
+                minValue = v;
+            } else if(v.compareTo(maxValue) == 1) {
+                maxValue = v;
+            }
+        }
+
+        // 根据找到的 最大值和最小值 以及 桶的范围,确定每个桶的数量
+        // 桶的数量 = (最大值-最小值)/ 桶的范围
+        int bucketCount = ((int)maxValue - (int)minValue) / BUCKET_RANGE + 1;
+
+        // 根据找到的 最大值和最小值 以及 桶的数量,确定桶的范围
+        // 桶的范围 = (最大值-最小值)/ 桶的数量
+        // int BUCKET_RANGE = ((int)maxValue - (int)minValue) / BUCKET_COUNT;
+
+        // 初始化辅助数组,为啥要加1正在研究
+        buckets = new Comparable[bucketCount][0];
+
+        // 根据元素值进行分桶操作
+        // 公式
+        for (Comparable v : a) {
+            int bucketIdx = ((int)v - (int)minValue) / BUCKET_RANGE;
+            buckets[bucketIdx] = arrAppend(buckets[bucketIdx], v);
+        }
+
+        // 按照桶的顺序和桶中元素的顺序 把元素依次放回原素组中
+        // ptr 代表原数组下标,代表了当前放置位置
+        //     升序时 从 -1 开始
+        //     降序时 从 原数组长度 开始
+        // adder 代表了ptr的变化方向,升序时 +1,降序时 -1
+        // 由于所有桶中的元素个数等于原数组的大小,因此无需特意设置结束判断,只要简单的都遍历一次即可
+        // 注意:下面代码中 桶的读取是升序读取的,每个桶中的元素也是升序的,因此只要简单的控制放回到原数组中的位置顺序,就能实现升序或降序了
+        int ptr = ascending ? -1 : a.length;
+        int adder = ascending ? 1 : -1;
+        for(Comparable[] bucket : buckets) {
+            // 分桶步骤仅仅只是分桶,桶中元素依然是无序的,因此要先对桶中元素进行排序(可以使用任何排序方法)
+            Insertion.sort(bucket, true);
+            for (Comparable v : bucket) {
+                ptr += adder;
+                a[ptr] = v;
+            }
+        }
+    }
+
+    /**
+     * 在数组中追加新元素
+     * @param arr 原数组
+     * @param v 追加值
+     * @return 新数组
+     */
+    public static Comparable[] arrAppend(Comparable[] arr, Comparable v) {
+        arr = Arrays.copyOf(arr, arr.length + 1);
+        arr[arr.length - 1] = v;
+        return arr;
+    }
+}

+ 100 - 0
src/main/java/com/cyl/algorithrms/sort/Radix.java

@@ -0,0 +1,100 @@
+package com.cyl.algorithrms.sort;
+
+import java.util.Arrays;
+
+/**
+ * LSD基数排序(下面是以10为基数)
+ */
+public class Radix {
+
+    /**
+     * 排序
+     * @param a 待排序数组
+     * @param ascending 排序方向 true-升序,false-降序
+     */
+    public static void sort(int[] a, boolean ascending) {
+
+        // 根据排序方向,决定存放数组元素时的放置方向
+        int adder = ascending ? 1 : -1;
+        // 找到待排序数组中的最大值
+        int maxValue = getMaxValue(a);
+        // 根据最大值确定是几位数(例如:1001是4位数)
+        int len = getDigitLen(maxValue);
+        // 分桶步骤:使用LSD,即从低位到高位
+        // 根据计算出的位数长度,进行遍历
+        // 关键-获取位上的值的方法:( 数值 / 基数 ^k) % 基数,可是指:个位k取0,十位k取1,百位k取2...以此类推
+        // i 即指示当前排序使用的是什么位,例如:个位-0、十位-1、百位-2..以此类推
+        //   注:如下代码仅把i用于用于控制循环的次数(根据数位长度排序次数)
+        // mod即用于获取当前位的值,即上述公式中 基数 ^k 的部分,mod=mod*10,mod初始值是1,
+        for(int i=0, mod=1; i<len; i++, mod*=10) {
+            //辅助数组,每次都要重建,否则会残留上一次遍历的结果
+            int[][] aux = new int[10][0];
+            //遍历待排序数组,根据公式计算结果,把元素放到对应的桶(0-9)中
+            for(int v : a) {
+                //公式:( 数值 / 基数 ^k) % 基数; 个位k取0,十位k取1,百位k取2。。。; 个位k取0,十位k取1,百位k取2。。。
+                int digit = (v / mod) % 10;
+                aux[digit] = arrAppend(aux[digit], v);
+            }
+
+            // 排序步骤:根据分桶的结果,要多次迭代排序才有最终的效果!
+            // 把辅助数组中的元素按顺序放回到原数组中,按顺序是指按照桶的循序和桶中元素的放入顺序(先进先出)放回到原数组中
+            // k 用于控制读取辅助数组元素的下标,取值范围[0, 辅助数组长度-1]
+            //   当升序时,从辅助数组的 0 开始读取
+            //   当降序时,从辅助数组的 辅助数组长度-1 开始读取
+            // adder 代表了k的变化方向,升序时 +1,降序时 -1
+            // 通过 k 和 adder 的方向控制,即通过控制桶的读取方向来影响排序的方向(升序或降序)
+            for (int j=0, k = ascending ? 0 : aux.length-1; k>=0 && k<= aux.length-1; k=k+adder) {
+                int[] v = aux[k];
+                for(int sv : v) {
+                    a[j++]=sv;
+                }
+            }
+        }
+    }
+
+    /**
+     * 在数组中追加新元素
+     * @param arr 原数组
+     * @param v 追加值
+     * @return 新数组
+     */
+    private static int[] arrAppend(int[] arr, int v) {
+        arr = Arrays.copyOf(arr, arr.length+1);
+        arr[arr.length-1]=v;
+        return arr;
+    }
+
+    /**
+     * 获取数组中的最大元素值
+     * @param a
+     * @return
+     */
+    public static int getMaxValue(int[] a) {
+        int tmp = a[0];
+        for (int v : a) {
+            if(v > tmp) {
+                tmp = v;
+            }
+        }
+        return tmp;
+    }
+
+    /**
+     * 获取输入数字的位数(例子:137是3位数)
+     * @param num
+     * @return
+     */
+    public static int getDigitLen(int num) {
+        // 如果输入0则是1位数
+        if(num == 0) {
+            return 1;
+        }
+        // 每除一次10就多一位
+        int len = 0;
+        for(int tmp = num; tmp !=0; tmp /= 10) {
+            len++;
+        }
+        return len;
+    }
+
+}

+ 77 - 0
src/test/java/com/cyl/algorithrms/search/BinaryTest.java

@@ -0,0 +1,77 @@
+package com.cyl.algorithrms.search;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class BinaryTest {
+
+    private Comparable[] ascArr;
+    private Comparable[] descArr;
+
+    @Before
+    public void prepare() {
+        ascArr = new Comparable[]{0,1,2,3,4,5,6,7,8,9,10,15,20,31};
+        descArr = new Comparable[]{31,20,15,10,9,8,7,6,5,4,3,2,1,0};
+    }
+
+    @Test
+    public void testSearchInAscLeft() {
+        int ret = Binary.search(ascArr, 2);
+        Assert.assertEquals(String.format("找到的下标:%s", ret), 2, ret);
+    }
+
+    @Test
+    public void testSearchInAscRight() {
+        int ret = Binary.search(ascArr, 20);
+        Assert.assertEquals(String.format("找到的下标:%s", ret), 12, ret);
+    }
+
+    @Test
+    public void testSearchInDescLeft() {
+        int ret = Binary.search(descArr, 10);
+        Assert.assertEquals(String.format("找到的下标:%s", ret), 3, ret);
+    }
+
+    @Test
+    public void testSearchInDescRight() {
+        int ret = Binary.search(descArr, 1);
+        Assert.assertEquals(String.format("找到的下标:%s", ret), 12, ret);
+    }
+
+    @Test
+    public void testSearchInAscStartLocation() {
+        int ret = Binary.search(ascArr, 0);
+        Assert.assertEquals(String.format("找到的下标:%s", ret), 0, ret);
+    }
+
+    @Test
+    public void testSearchInAscEndLocation() {
+        int ret = Binary.search(ascArr, 31);
+        Assert.assertEquals(String.format("找到的下标:%s", ret), 13, ret);
+    }
+
+    @Test
+    public void testSearchInDescStartLocation() {
+        int ret = Binary.search(descArr, 31);
+        Assert.assertEquals(String.format("找到的下标:%s", ret), 0, ret);
+    }
+
+    @Test
+    public void testSearchInDescEndLocation() {
+        int ret = Binary.search(descArr, 0);
+        Assert.assertEquals(String.format("找到的下标:%s", ret), 13, ret);
+    }
+
+    @Test
+    public void testSearchNotInAsc() {
+        int ret = Binary.search(ascArr, 100);
+        Assert.assertEquals(String.format("找到的下标:%s", ret), -1, ret);
+    }
+
+    @Test
+    public void testSearchNotInDesc() {
+        int ret = Binary.search(descArr, 100);
+        Assert.assertEquals(String.format("找到的下标:%s", ret), -1, ret);
+    }
+}

+ 77 - 0
src/test/java/com/cyl/algorithrms/search/InsertionTest.java

@@ -0,0 +1,77 @@
+package com.cyl.algorithrms.search;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class InsertionTest {
+
+    private Comparable[] ascArr;
+    private Comparable[] descArr;
+
+    @Before
+    public void prepare() {
+        ascArr = new Comparable[]{0,1,2,3,4,5,6,7,8,9,10,15,20,31};
+        descArr = new Comparable[]{31,20,15,10,9,8,7,6,5,4,3,2,1,0};
+    }
+
+    @Test
+    public void testSearchInAscLeft() {
+        int ret = Insertion.search(ascArr, 2);
+        Assert.assertEquals(String.format("找到的下标:%s", ret), 2, ret);
+    }
+
+    @Test
+    public void testSearchInAscRight() {
+        int ret = Insertion.search(ascArr, 20);
+        Assert.assertEquals(String.format("找到的下标:%s", ret), 12, ret);
+    }
+
+    @Test
+    public void testSearchInDescLeft() {
+        int ret = Insertion.search(descArr, 10);
+        Assert.assertEquals(String.format("找到的下标:%s", ret), 3, ret);
+    }
+
+    @Test
+    public void testSearchInDescRight() {
+        int ret = Insertion.search(descArr, 1);
+        Assert.assertEquals(String.format("找到的下标:%s", ret), 12, ret);
+    }
+
+    @Test
+    public void testSearchInAscStartLocation() {
+        int ret = Insertion.search(ascArr, 0);
+        Assert.assertEquals(String.format("找到的下标:%s", ret), 0, ret);
+    }
+
+    @Test
+    public void testSearchInAscEndLocation() {
+        int ret = Insertion.search(ascArr, 31);
+        Assert.assertEquals(String.format("找到的下标:%s", ret), 13, ret);
+    }
+
+    @Test
+    public void testSearchInDescStartLocation() {
+        int ret = Insertion.search(descArr, 31);
+        Assert.assertEquals(String.format("找到的下标:%s", ret), 0, ret);
+    }
+
+    @Test
+    public void testSearchInDescEndLocation() {
+        int ret = Insertion.search(descArr, 0);
+        Assert.assertEquals(String.format("找到的下标:%s", ret), 13, ret);
+    }
+
+    @Test
+    public void testSearchNotInAsc() {
+        int ret = Insertion.search(ascArr, 100);
+        Assert.assertEquals(String.format("找到的下标:%s", ret), -1, ret);
+    }
+
+    @Test
+    public void testSearchNotInDesc() {
+        int ret = Binary.search(descArr, 100);
+        Assert.assertEquals(String.format("找到的下标:%s", ret), -1, ret);
+    }
+}

+ 29 - 0
src/test/java/com/cyl/algorithrms/search/SequenceTest.java

@@ -0,0 +1,29 @@
+package com.cyl.algorithrms.search;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class SequenceTest {
+
+    private Comparable[] origArr;
+
+    @Before
+    public void prepare() {
+        origArr = new Comparable[]{5,3,8,9,1,2,4,0,7,8,6,5};
+    }
+
+    @Test
+    public void testSearchSuccess() {
+        int idx = Sequence.search(origArr, 5);
+        Assert.assertEquals(String.format("查找到的元素:%s,下标:%s", origArr[idx], idx), 0, idx);
+    }
+
+    @Test
+    public void testSearchError() {
+        int idx = Sequence.search(origArr, 20);
+        Assert.assertEquals(String.format("返回值:%s", idx), -1, idx);
+    }
+}

+ 33 - 0
src/test/java/com/cyl/algorithrms/sort/BucketTest.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 BucketTest {
+
+    private Comparable[] origArr = null;
+
+    @Before
+    public void prepare() {
+        origArr = new Comparable[]{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() {
+        Comparable[] ascArr = new Comparable[]{0,0,1,2,2,2,3,3,4,4,4,5,5,6,7,7,8,8,8,9,10,20};
+        Bucket.sort(origArr, true);
+        assertArrayEquals(String.format("测试升序,排序结果:%s", Arrays.toString(origArr)), ascArr, origArr);
+    }
+
+    @Test
+    public void testSortDesc() {
+        Comparable[] descArr = new Comparable[]{20,10,9,8,8,8,7,7,6,5,5,4,4,4,3,3,2,2,2,1,0,0};
+        Bucket.sort(origArr, false);
+        assertArrayEquals(String.format("测试降序,排序结果:%s", Arrays.toString(origArr)), descArr, origArr);
+    }
+
+}

+ 42 - 0
src/test/java/com/cyl/algorithrms/sort/RadixTest.java

@@ -0,0 +1,42 @@
+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 RadixTest {
+
+    private int[] origArr = null;
+
+    @Before
+    public void prepare() {
+        origArr = new int[]{321,1,10,127,743,60,577};
+    }
+
+    @Test
+    public void testSortAsc() {
+        int[] ascArr = new int[]{1,10,60,127,321,577,743};
+        Radix.sort(origArr, true);
+        assertArrayEquals(String.format("测试升序,排序结果:%s", Arrays.toString(origArr)), ascArr, origArr);
+    }
+
+    @Test
+    public void testSortDesc() {
+        int[] ascArr = new int[]{743,577,321,127,60,10,1};
+        Radix.sort(origArr, false);
+        assertArrayEquals(String.format("测试升序,排序结果:%s", Arrays.toString(origArr)), ascArr, origArr);
+    }
+
+    @Test
+    public void getNumLen() {
+        int a = Radix.getDigitLen(9);
+        Assert.assertEquals(String.format("位数长度:%s", a), 1, a);
+
+        int b = Radix.getDigitLen(19);
+        Assert.assertEquals(String.format("位数长度:%s", b), 2, b);
+    }
+}