【问题标题】:Radix sort implementation基数排序实现
【发布时间】:2016-01-24 10:33:08
【问题描述】:

我正在尝试制作基数排序函数,该函数使用基数排序作为基数来计算整数的位数,然后将数字从最低有效位排序到最高有效位。

我正在使用一个包含随机整数的数组。

我怎样才能使这种方法运作良好?

我正在使用此代码:

public static void sort( int[] a, int radix)
    {
        int i, m = a[0], exp = 1, n = a.length;
        int[] b = new int[10];
        for (i = 1; i < n; i++)
            if (a[i] > m)
                m = a[i];
        while (m / exp > 0)
        {
            int[] bucket = new int[10];

            for (i = 0; i < n; i++)
                bucket[(a[i] / exp) % 10]++;
            for (i = 1; i < 10; i++)
                bucket[i] += bucket[i - 1];
            for (i = n - 1; i >= 0; i--)
                b[--bucket[(a[i] / exp) % 10]] = a[i];
            for (i = 0; i < n; i++)
                a[i] = b[i];
            exp *= 10;        
        }
    }    

【问题讨论】:

    标签: java sorting computer-science radix-sort


    【解决方案1】:

    对于非负整数,使用binary 而不是decimal,对机器来说可能更高效、更直观。

    这是我写的一个实现,带有测试用例:

    RadixSort.java

    import java.util.ArrayList;
    import java.util.LinkedList;
    import java.util.List;
    
    /**
     * Radix sort.
     *
     * @author eric
     * @date 3/12/20 12:05 PM
     */
    public class RadixSort {
        /**
         * Sort given array, the array will be modified.
         *
         * @param data array of integer of non-negative integers,
         * @throws IllegalArgumentException if input contain negative integer,
         */
        public static void sort(int[] data) {
            int numSys = 2;
            int bits = validAndfindHighestBit(data); // find highest bit,
    
            // create queues,
            List<List<Integer>> queues = new ArrayList<>(numSys);
            for (int i = 0; i < numSys; i++) queues.add(new LinkedList<>());
    
            // sort bit by bit, low to high,
            for (int i = 0; i < bits; i++) {
                // re-init queues,
                for (int j = 0; j < numSys; j++) queues.get(j).clear();
    
                // array -> queues,
                for (int x : data) {
                    int bit = (x >> i) & 1; // get the i-th bit,
                    queues.get(bit).add(x);
                }
    
                // queues -> array,
                int t = 0;
                for (List<Integer> queue : queues) {
                    for (int x : queue) data[t++] = x;
                }
            }
        }
    
        /**
         * Valid input number, and find highest bit that has 1.
         *
         * @param data
         * @return
         * @throws IllegalArgumentException if input contain negative integer,
         */
        private static int validAndfindHighestBit(int[] data) {
            // find max number,
            int max = 0;
            for (int x : data) {
                if (x < 0) throw new IllegalArgumentException("negative integers are not supported");
                if (x > max) max = x;
            }
            System.out.printf("max number: %d, ", max);
    
            // find highest bit,
            int highestBit = 0;
            while (max != 0) {
                highestBit++;
                max >>= 1;
            }
            System.out.printf("highest bit: %d\n", highestBit);
    
            return highestBit;
        }
    }
    

    RadixSortTest.java
    (通过TestNG 的测试用例)

    import org.testng.Assert;
    import org.testng.annotations.Test;
    
    import java.util.Arrays;
    import java.util.Random;
    import java.util.concurrent.ThreadLocalRandom;
    
    import static org.testng.Assert.*;
    
    /**
     * RadixSort test.
     */
    public class RadixSortTest {
        @Test
        public void testSort() {
            int[] data;
            // generated un-sorted random array,
            do data = genRandomArr(); while (data.length > 1 && isSorted(data));
            System.out.printf("input arr:\t%s\n", Arrays.toString(data));
            if (data.length > 1) Assert.assertFalse(isSorted(data));
    
            // sort
            RadixSort.sort(data);
            System.out.printf("output arr:\t%s\n", Arrays.toString(data));
            Assert.assertTrue(isSorted(data));
        }
    
        // corner case,
        @Test
        public void testSort_corner() {
            int[] dataEmpty = new int[0]; // empty array,
            RadixSort.sort(dataEmpty);
            Assert.assertTrue(isSorted(dataEmpty));
    
            int[] dataSingle = new int[]{5}; // single element,
            RadixSort.sort(dataSingle);
            Assert.assertTrue(isSorted(dataSingle));
        }
    
        // invalid input,
        @Test(expectedExceptions = IllegalArgumentException.class)
        public void testSort_invalid() {
            int[] dataSingle = new int[]{1, -1}; // negative number,
            RadixSort.sort(dataSingle);
        }
    
        /**
         * generate random array, of size 10, in range [0, 1024),
         *
         * @return
         */
        public static int[] genRandomArr() {
            return genRandomArr(10, 100);
        }
    
        /**
         * generate random array,
         *
         * @param size  array size, default to 10,
         * @param bound upper bound, default to 100,
         * @return
         */
        public static int[] genRandomArr(int size, int bound) {
            if (size <= 0) size = 10;
            if (bound <= 0) bound = 100;
            ThreadLocalRandom rd = ThreadLocalRandom.current();
            int[] arr = new int[size];
            for (int i = 0; i < arr.length; i++) arr[i] = rd.nextInt(bound);
            return arr;
        }
    
        // check whether array is sorted,
        private static boolean isSorted(int[] a) {
            for (int i = 0; i < a.length - 1; i++) {
                if (a[i] > a[i + 1]) return false;
            }
            return true;
        }
    }
    

    【讨论】:

      【解决方案2】:

      最后一个for循环中的语句

      a[i] = b[i];
      

      告诉 b 的大小必须等于或大于 a。因此去@rcgldr 回答。传递给您的函数的基数的值也未使用。您还可以通过交换数组指针而不是复制元素来使您的函数更快一点,即避免最后一个 for 循环。

      int swap[] = a;
      a = b;
      b = swap;
      

      那么最后在所有循环结束后,返回数组a

      return a;
      

      上面是你的升序排序程序。我在程序下面给出以降序排序。唯一需要的更改是开始将频率从基本数组(在本例中为 Z )的末尾添加到索引零。

      public static int[] DSC(int A[], int radix, int base)
      {
          int length = A.length ;
          int B[] = new int[length] ;
          int div = 1 ;
          int swap[] ;
          int i ;
          while(radix > 0)
          {
              int Z[] = new int[base] ;
              i = 0 ;
              while(i < length)
              {
                  Z[( A[i] / div ) % base]++ ;
                  i++ ;
              }
              i = base ;
              while(i > 1)
              {
                  i--;
                  Z[i-1] += Z[i] ;
              }
              i = length ;
              while(i > 0)
              {
                  i-- ;
                  B[--Z[( A[i] / div ) % base]] = A[i];
              }
              swap = A;
              A = B;
              B = swap;
              div *= base;
              radix--;
          }
          return A;
      }
      

      【讨论】:

        【解决方案3】:

        我只是指出最明显的问题,把细节留给你。

        每个存储桶都必须保存一个数字列表,因此仅使用 int 作为存储桶是行不通的。改用这样的东西:

        List<Integer>[] bucket = new List<Integer>[10];
        

        用于以新顺序收集元素的数组需要与原始数组的大小相同。你刚刚做了 10 长。

        【讨论】:

        • 当我这样做时它不起作用: List[] buckets= new List[radix];
        • 你错过了我回答的这一部分:“并且(我)把细节留给你”
        【解决方案4】:

        试试

            int[] b = new int[a.length];
        

        或者因为 n = a.length

            int[] b = new int[n];
        

        【讨论】:

          猜你喜欢
          • 2021-03-10
          • 1970-01-01
          • 1970-01-01
          • 2016-11-17
          • 2010-11-19
          • 2014-03-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多