【问题标题】:Using Counting Sort with Negative values? (Descending Order)对负值使用计数排序? (降序)
【发布时间】:2017-03-21 11:11:05
【问题描述】:

对于x > 0,我有一个计数排序,它按降序对我的数组进行排序。然而,当考虑负数时,我的实现逻辑崩溃了,因为我在辅助数组values 中使用负索引。我正在考虑以某种方式使用 uint 但我不是很熟悉。

我怎样才能克服这个使用计数排序

static void countingSort(int[] arr)    
{
    int i, j, max = -1; // I think it falls apart about here 
    int[] values;

    for (i = 0; i < arr.Length; i++)
        if (arr[i] > max) max = arr[i];

    values = new int[max + 1];

    //Here it reaches for a negative index when i = 2,looking for -6.            
    for (i = 0; i < arr.Max(); i++)
        values[arr[i]]++; 

    i = 0; j = values.Length - 1;
    while (i < arr.Length)
    {
        if (values[j] > 0)
        {
            values[j]--;
            arr[i] = j;
            i++;
        }
        else j--;
    }
}

我知道我的问题在于帮助数组的索引。而且由于我不想继续制作带有负索引的数组,所以我有点难过。

如果不将整个类实现为索引器,您甚至可以在 c# 中做到这一点吗? 我知道你可以在 C 中做到这一点,它的定义很明确:

来自 C99 §6.5.2.1/2: 下标运算符[]的定义是E1[E2]等同于(*((E1)+(E2)))。

我的测试数组是{ 8, 5, -6, 7, 1, 4 }

我的预期输出是{ 8, 7, 5, 4, 1, -6 }

【问题讨论】:

  • 好吧,C# 不像 C 那样在其索引器中执行“指针数学”,所以不,它们不等价。

标签: c# sorting counting-sort


【解决方案1】:

在您的示例中,您已经在扫描输入数组以查找最大值。缺少的是您没有同时扫描输入数组以找到最小值。如果添加它,然后知道最小值,您可以偏移数组索引的范围以允许负数(如果只处理正数,甚至可能减小数组的大小)。

看起来像这样:

static void Sort(int[] array)
{
    int min = int.MaxValue, max = int.MinValue;

    for (int i = 0; i < array.Length; i++)
    {
        if (array[i] < min)
        {
            min = array[i];
        }

        if (array[i] > max)
        {
            max = array[i];
        }
    }

    int[] counts = new int[max - min + 1];

    for (int i = 0; i < array.Length; i++)
    {
        counts[array[i] - min]++;
    }

    int k = 0;

    for (int j = max; j >= min; j--)
    {
        for (int i = 0; i < counts[j - min]; i++)
        {
            array[k++] = j;
        }
    }
}

请注意,这种排序的一个显着缺点是需要维护一个连续的数组来保存输入中的所有可能值。即使输入数组中只有两个元素,如果它们的值是 int.MinValueint.MaxValue,您将需要一个 16GB 大的中间数组(暂时忽略整数数学计算时会遇到问题数组的长度仅使用int)。

另一种方法是使用字典来存储计数。这使您可以避免为不存在的输入值分配内存。它也恰好允许您只需要扫描输入一次,而不是两次(但这样做的代价是您正在处理一个数据结构,当您添加新元素时必须重新分配其底层存储,所以算法复杂度并没有真正降低多少)。

看起来像这样:

static void Sort(int[] array)
{
    int min = int.MaxValue, max = int.MinValue;

    Dictionary<int, int> counts = new Dictionary<int, int>();

    for (int i = 0; i < array.Length; i++)
    {
        if (array[i] < min)
        {
            min = array[i];
        }

        if (array[i] > max)
        {
            max = array[i];
        }

        int count;

        // If the key is not present, count will get the default value for int, i.e. 0
        counts.TryGetValue(array[i], out count);
        counts[array[i]] = count + 1;
    }

    int k = 0;

    for (int j = max; j >= min; j--)
    {
        int count;

        if (counts.TryGetValue(j, out count))
        {
            for (int i = 0; i < count; i++)
            {
                array[k++] = j;
            }
        }
    }
}

【讨论】:

    【解决方案2】:

    它实际上可以在一个非常简单的转变中完成,如果你排序更新:

        static void countingSort(int[] arr)
        {
            int i, j, max = -1; 
            int[] values;
            //get the highest absolute value to determine the size of the array 
            for (i = 0; i < arr.Length; i++)
                if (Math.Abs(arr[i]) > max) max = Math.Abs(arr[i]);
            //create double the max size array
            values = new int[max*2 + 1];
    
            //when reaching a value make a shift of max size            
            for (i = 0; i < arr.Length; i++)
                values[arr[i]+max]++;
    
            i = 0; j = values.Length - 1;
            while (i < arr.Length)
            {
                if (values[j] > 0)
                {
                    values[j]--;
                    //shift back when putting in the sorted array
                    arr[i] = j-max;
                    i++;
                }
                else j--;
            }
    
        }
    

    这样,假设您有一个 {-4,3,2} 数组,您将创建一个大小为 (4*2+1)=9 的数组,因为最高绝对值是 -4 ,移位将为 4,因此 -4 将位于索引 0 中,例如 2 将位于 values 数组的索引 6 中,这样您就可以避免问题并获得您想要的结果。

    【讨论】:

      【解决方案3】:

      看到这个。 https://en.wikipedia.org/wiki/Counting_sort.

      问题在于“值[arr[i]]++”。如果 arr[i] 中的值返回负数,它将不起作用。

      一种可能性是编写一个函数“Hash-key(arr[i])”,它接受数组中的任意数字并将其转换为字典键值。您可以自己编写或使用库。

      如果您使用的是 C#,那么“System.Collections”有许多类来促进字典索引。

      using System;
      using System.Collections;
      public class SamplesArrayList  {
      
         public static void Main()  {
      
         // Creates and initializes a new ArrayList.
         ArrayList myAL = new ArrayList();
         myAL.Add("Hello");
         myAL.Add("World");
      

      myAL.Add("!");

      您的助手将加载源“arr”数组中的数据。 希望这会有所帮助。

      【讨论】:

        【解决方案4】:

        解决包含负数的数组的一种方法是:

        将所有负数分开放在另一个数组中,使它们为正(简单地否定符号),对它们使用计数排序,然后将它们反转并放回负号。除此之外,分别对正数进行计数排序,并将负数与正数合并。你得到了答案!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-10-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-03-07
          • 1970-01-01
          • 2011-11-05
          相关资源
          最近更新 更多