我有一个计数排序正常工作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 }
答案 0 :(得分:3)
实际上,这可以通过一个非常简单的转换完成,你可以更新排序:
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中,这样就可以避免问题并获得所需的结果。
答案 1 :(得分: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.MinValue
和int.MaxValue
,你需要一个16GB大的中间数组(暂时忽略你&#39} ; ll使用整数数学运算来解决问题,仅使用int
计算数组的长度。
另一种方法是使用字典来存储计数。这样可以避免为不存在的输入值分配内存。它也恰好允许你只需要扫描输入一次,而不是两次(但是这样做的代价是你正在处理一个数据结构,当你添加新元素时必须重新分配它的底层存储,所以算法的复杂性并没有真正减少很多。)
看起来像这样:
static void Sort(int[] array)
{
int min = int.MaxValue, max = int.MinValue;
for (int i = 0; i < array.Length; i++)
{
}
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 :(得分:0)
看到这个。 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");
您的助手将加载源“arr”数组中的数据。 希望这会有所帮助。