我有一段代码可以找出大小为n
的数组中的重复元素,其中元素满足1 <= arr[i] <= n
,代码如下:
#include<stdio.h>
#include<stdlib.h>
void printTwoElements(int arr[], int size)
{
int i;
printf("\n The repeating element is");
for(i = 0; i < size; i++)
{
if(arr[abs(arr[i])-1] > 0)
{
arr[abs(arr[i])-1] = -arr[abs(arr[i])-1];
}
else
{
printf(" %d ", abs(arr[i]));
}
}
}
/* Driver program to test above function */
int main()
{
int arr[] = {7, 3, 4, 5, 5, 6, 2};
int n = sizeof(arr)/sizeof(arr[0]);
printTwoElements(arr, n);
return 0;
}
我想知道在这个给定的代码中使用abs()
吗?
答案 0 :(得分:4)
在算法过程中,一些数组条目被设置为负值作为标记。因此,当条目用作数组的索引时,必须采用条目的绝对值。
希望不破坏任何东西:
该算法要求n
- 元素数组的数组条目都在1和n
之间。
如果任何条目大于n
或小于-n
或0,则它将访问无效地址,如果任何元素为负,则标记逻辑将失败。
算法的逻辑是:
for each array element e:
if the value at (e-1) is positive, e has not yet been seen,
negate the value at (e-1) to mark e as seen
otherwise, e has already been seen, so print it
因此,由于在运行算法的过程中数组条目变为负数,因此必须采用绝对值来获得有效的索引。
让我们按照修改示例的算法来看看它是如何工作的:
before: arr = { 7, 3, 4, 5, 5, 3, 2}
i == 0: arr[0] = 7
arr[7-1] is 2 > 0 ~> negate
arr = { 7, 3, 4, 5, 5, 3, -2}
i == 1: arr[1] = 3
arr[3-1] is 4 > 0 ~> negate
arr = { 7, 3, -4, 5, 5, 3, -2}
i == 2: arr[2] is -4 ~> abs for indexing
arr[4-1] is 5 > 0 ~> negate
arr = { 7, 3, -4,-5, 5, 3, -2}
i == 3: arr[3] is -5 ~> abs for indexing
arr[5-1] is 5 > 0 ~> negate
arr = { 7, 3, -4, -5, -5, 3, -2}
i == 4: arr[4] is -5 ~> abs for indexing
arr[5-1] is -5 < 0 ~> print abs(-5) as duplicate
i == 5: arr[5] is 3
arr[3-1] is -4 < 0 ~> print abs(3) as duplicate
i == 6: arr[6] is -2 ~> abs for indexing
arr[2-1] is 3 > 0 ~> negate
arr = { 7, -3, -4, -5, -5, 3, -2}
indices of positive entries: 0, 5 ~> 1 and 6 not in original array
indices of negative entries: 1, 2, 3, 4, 6 ~> 2, 3, 4, 5, 7 in original array
答案 1 :(得分:1)
如果你已经开始用最直接的方法来解决这个问题,给定额外的O(n)空间,你可能会想到将“已经遇到”的标志存储在一个单独的(辅助)数组中以便以后查找它们: / p>
// pseudocode (zero based indexing ignored for now)
for each value in array
if (already_encountered[value] == true)
print "found a duplicate of " + value
else
already_encountered[value] = true
你的算法更进一步。鉴于整数(可能)是32位,并且您只需要存储有限(小)范围的值,每个数组成员实际上都有足够的空闲位来存储“已经遇到的” “旗帜。
这意味着您可以删除上面给出的辅助already_encountered
数组,并使用此额外空间来存储无额外内存分配的标记。
使用一些比特,你可以选择将这个值存储在最高位(留下31位来存储你的数字)。每当你想检查标志是否设置时,你需要提取这个最高位并检查它,然后在打印出值之前最后清除该位:
// pseudocode (zero based indexing ignored)
for each value in array
{
// this value might already have a flag previously encoded
// for some other element, so remove it before continuing
plain_value = remove_flag(value)
// check if the flag is set for the actual value,
// if not, set it now
if (is_flag_set(array[plain_value]) == true)
print "found a duplicate of " + plain_value
else
array[plain_value] = set_flag(array[plain_value])
}
唯一要做的就是定义set_flag
,is_flag_set
和remove_flag
函数。
在你的情况下,算法通过否定值来“设置标志”,通过检查值是否为负来“测试标志”,并使用绝对值“删除标志” (因此abs
函数)。
这可以安全地实现,因为有符号整数使用单个位来存储它们的符号信息,允许转换保持原始值不变(只要它足够小)。
这将为您留下最终的C代码:
void printTwoElements(int arr[], int size)
{
int i;
printf("\n The repeating element is");
for(i = 0; i < size; i++)
{
// remove the flag
int plain_value = abs(arr[i]);
// is flag set?
if(arr[plain_value-1] < 0)
{
printf(" %d ", plain_value);
}
else
{
// set the flag by negating
arr[plain_value-1] = -arr[plain_value-1];
}
}
}
答案 2 :(得分:0)
它试图确保程序在遍历元素时从不尝试负数组索引。
答案 3 :(得分:0)
abs()是C中的绝对值函数。它确保您的数组索引不是负数。