답변:
이것은 추가 부호 비트가 필요하지 않은 내가 생각해 낸 것입니다.
for i := 0 to n - 1
while A[A[i]] != A[i]
swap(A[i], A[A[i]])
end while
end for
for i := 0 to n - 1
if A[i] != i then
print A[i]
end if
end for
첫 번째 루프 x
는 요소 가 한 번 이상 존재하면 해당 항목 중 하나가 위치에 있도록 배열을 순열합니다 A[x]
.
처음에는 O (n)처럼 보이지 않을 수도 있지만, 중첩 된 루프가 있어도 여전히 제 O(N)
시간에 실행됩니다 . 스왑은 i
that 이있는 경우에만 발생 A[i] != i
하며 각 스왑은 A[i] == i
이전에 true가 아니었던 that 과 같은 요소를 하나 이상 설정합니다 . 즉, 총 스왑 수 (따라서 while
루프 본문 의 총 실행 수 )가 최대 N-1
.
두 번째 루프는 x
for가 A[x]
같지 않은 값을 인쇄합니다 x
. 첫 번째 루프 x
는 배열에 적어도 한 번 존재 하는 경우 해당 인스턴스 중 하나가에 있음을 보장하기 때문에 해당 인스턴스에 존재하지 않는 A[x]
값을 인쇄합니다 x
. 배열.
5
범위 0..N-1
( N
이 경우 5
) 가 아니기 때문에 유효한 입력 이 아닙니다 .
print
설명을 변경하여 print i
이를 stackoverflow.com/questions/5249985/… 에 대한 솔루션으로 바꾸고 ( "bag"이 수정 가능한 배열이라고 가정) Qk of stackoverflow.com/questions/3492302/… 를 언급하겠습니다 .
caf의 훌륭한 대답 은 배열에 k 번 나타나는 각 숫자를 k-1 번 인쇄합니다. 이것은 유용한 동작이지만 질문은 각 복제물이 한 번만 인쇄되도록 요구하며 선형 시간 / 상수 공간 경계를 날리지 않고이를 수행 할 수있는 가능성을 암시합니다. 이것은 그의 두 번째 루프를 다음 의사 코드로 대체하여 수행 할 수 있습니다.
for (i = 0; i < N; ++i) {
if (A[i] != i && A[A[i]] == A[i]) {
print A[i];
A[A[i]] = i;
}
}
이것은 첫 번째 루프가 실행 된 후 값 m
이 두 번 이상 나타나는 경우 해당 모양 중 하나가 올바른 위치에 있음을 보장 하는 속성을 이용합니다 A[m]
. 주의를 기울이면 해당 "홈"위치를 사용하여 사본이 아직 인쇄되었는지 여부에 대한 정보를 저장할 수 있습니다.
caf의 버전에서 배열을 살펴 A[i] != i
보았을 때 그것이 A[i]
중복 임을 암시했습니다 . 내 버전에서는 약간 다른 불변에 의존합니다. A[i] != i && A[A[i]] == A[i]
이는 이전에 본 적이없는A[i]
중복 임을 의미합니다 . ( "우리가 이전에 본 적이없는"부분을 삭제하면 나머지는 caf의 불변의 진실과 모든 복제본이 홈 위치에 일부 사본이 있다는 보장에 의해 암시되는 것으로 볼 수 있습니다.)이 속성은 다음과 같습니다. 처음 (카페의 첫 번째 루프가 끝난 후)와 각 단계 후에 유지된다는 것을 아래에 보여줍니다.
배열을 살펴보면 A[i] != i
테스트 부분의 성공은 이전에 본 적이없는 중복 일 A[i]
수 있음을 의미합니다 . 이전에 본 적이 없다면 A[i]
의 집 위치가 자신을 가리킬 것으로 예상 if
합니다. 이것이 조건의 후반부에서 테스트 한 것입니다. 이 경우 인쇄하고 홈 위치를 변경하여 처음 발견 된이 복제본을 다시 가리 키도록하여 2 단계 "주기"를 만듭니다.
이 작업은 우리의 불변을 변경할 생각하지 않는 것을 확인하려면 m = A[i]
특정 위치에 대한 i
만족 A[i] != i && A[A[i]] == A[i]
. 우리가 만든 변경 사항 ( A[A[i]] = i
)은 조건의 m
후반부 if
를 실패하게하여 다른 집이 아닌 항목이 중복으로 출력되는 것을 방지하기 위해 작동 하지만 i
, 집 위치에 도착하면 작동 m
할까요? 예, 이제이 새로운 조건 i
에서 if
조건의 전반부 A[i] != i
가 참인 것을 발견 하더라도 후반부는 가리키는 위치가 집 위치인지 아닌지 여부를 테스트하기 때문입니다. 이 상황에서 우리는 더 이상 여부를 알 수 없습니다 m
또는 A[m]
중복 값이었다, 그러나 우리는 어느 쪽이든 것을 알고,이 2주기가 caf의 첫 번째 루프의 결과에 나타나지 않도록 보장되기 때문에 이미보고되었습니다 . ( m != A[m]
그런 다음 정확히 하나의 m
and A[m]
가 두 번 이상 발생하고 다른 하나는 전혀 발생하지 않습니다.)
다음은 의사 코드입니다.
for i <- 0 to n-1:
if (A[abs(A[i])]) >= 0 :
(A[abs(A[i])]) = -(A[abs(A[i])])
else
print i
end for
-
와 ~
제로 문제에 대한.
O(n)
숨겨진 공간 ( n
부호 비트)을 사용합니다. 각 요소가 0
및 사이의 값만 보유 할 수 있도록 배열이 정의되면 n-1
분명히 작동하지 않습니다.
상대적으로 작은 N의 경우 div / mod 작업을 사용할 수 있습니다.
n.times do |i|
e = a[i]%n
a[e] += n
end
n.times do |i|
count = a[i]/n
puts i if count > 1
end
C / C ++는 아니지만 어쨌든
정말 예쁘지는 않지만 적어도 O (N) 및 O (1) 속성을 쉽게 볼 수 있습니다. 기본적으로 배열을 스캔하고 각 숫자에 대해 해당 위치가 이미 본 한 번 (N) 또는 이미 본 여러 번 (N + 1) 플래그가 지정되었는지 확인합니다. 이미 본 한 번 플래그가 지정된 경우이를 인쇄하고 이미 본 여러 번 플래그를 지정합니다. 플래그가 지정되지 않은 경우 이미 본 한 번 플래그를 지정하고 해당 인덱스의 원래 값을 현재 위치로 이동합니다 (플래 깅은 파괴적인 작업입니다).
for (i=0; i<a.length; i++) {
value = a[i];
if (value >= N)
continue;
if (a[value] == N) {
a[value] = N+1;
print value;
} else if (a[value] < N) {
if (value > i)
a[i--] = a[value];
a[value] = N;
}
}
또는 더 나은 방법 (이중 루프에도 불구하고 더 빠름) :
for (i=0; i<a.length; i++) {
value = a[i];
while (value < N) {
if (a[value] == N) {
a[value] = N+1;
print value;
value = N;
} else if (a[value] < N) {
newvalue = value > i ? a[value] : N;
a[value] = N;
value = newvalue;
}
}
}
if (value > i) a[i--] = a[value];
작동 하는지 알아 내기 위해 약간의 생각이 필요했습니다 . 만약 value <= i
그렇다면 우리가 이미 값을 처리하고 a[value]
안전하게 덮어 쓸 수 있다면. 또한 나는 O (N) 본성이 명백하다고 말하지 않을 것입니다! 철자법 : 메인 루프는 N
여러 번 a[i--] = a[value];
실행 되지만 행이 여러 번 실행됩니다. 해당 라인은 실행될 a[value] < N
때마다, 아직 N
설정 되지 않은 배열 값이로 설정된 직후 에만 실행될 수 N
있으므로 N
최대 2N
루프 반복 횟수 까지 최대 횟수까지 실행할 수 있습니다 .
C의 한 가지 해결책은 다음과 같습니다.
#include <stdio.h>
int finddup(int *arr,int len)
{
int i;
printf("Duplicate Elements ::");
for(i = 0; i < len; i++)
{
if(arr[abs(arr[i])] > 0)
arr[abs(arr[i])] = -arr[abs(arr[i])];
else if(arr[abs(arr[i])] == 0)
{
arr[abs(arr[i])] = - len ;
}
else
printf("%d ", abs(arr[i]));
}
}
int main()
{
int arr1[]={0,1,1,2,2,0,2,0,0,5};
finddup(arr1,sizeof(arr1)/sizeof(arr1[0]));
return 0;
}
O (n) 시간과 O (1) 공간 복잡성입니다.
이 배열을 단방향 그래프 데이터 구조로 제시한다고 가정 해 보겠습니다. 각 숫자는 꼭지점이고 배열의 인덱스는 그래프의 가장자리를 형성하는 다른 꼭지점을 가리 킵니다.
더 간단하게하기 위해 0에서 n-1까지의 인덱스와 0..n-1의 숫자 범위가 있습니다. 예 :
0 1 2 3 4
a[3, 2, 4, 3, 1]
0 (3)-> 3 (3)은주기입니다.
답변 : 인덱스를 사용하여 배열을 순회하십시오. a [x] = a [y]이면 순환이므로 중복됩니다. 다음 인덱스로 건너 뛰고 배열이 끝날 때까지 계속합니다. 복잡성 : O (n) 시간 및 O (1) 공간.
알고리즘은 다음 C 함수에서 쉽게 볼 수 있습니다. 필요하지는 않지만 원래 배열을 검색하는 것은 모듈로 n의 각 항목을 사용하여 가능합니다 .
void print_repeats(unsigned a[], unsigned n)
{
unsigned i, _2n = 2*n;
for(i = 0; i < n; ++i) if(a[a[i] % n] < _2n) a[a[i] % n] += n;
for(i = 0; i < n; ++i) if(a[i] >= _2n) printf("%u ", i);
putchar('\n');
}
static void findrepeat()
{
int[] arr = new int[7] {0,2,1,0,0,4,4};
for (int i = 0; i < arr.Length; i++)
{
if (i != arr[i])
{
if (arr[i] == arr[arr[i]])
{
Console.WriteLine(arr[i] + "!!!");
}
int t = arr[i];
arr[i] = arr[arr[i]];
arr[t] = t;
}
}
for (int j = 0; j < arr.Length; j++)
{
Console.Write(arr[j] + " ");
}
Console.WriteLine();
for (int j = 0; j < arr.Length; j++)
{
if (j == arr[j])
{
arr[j] = 1;
}
else
{
arr[arr[j]]++;
arr[j] = 0;
}
}
for (int j = 0; j < arr.Length; j++)
{
Console.Write(arr[j] + " ");
}
Console.WriteLine();
}
0 (n) 시간 복잡성과 일정한 추가 공간에서 중복을 찾기 위해 신속하게 하나의 샘플 놀이터 앱을 만들었습니다. 중복을 찾는 URL을 확인하십시오
IMP 위의 솔루션은 배열이 0에서 n-1까지의 요소를 포함 할 때 작동하며 이러한 숫자 중 하나가 횟수에 관계없이 나타납니다.
private static void printRepeating(int arr[], int size) {
int i = 0;
int j = 1;
while (i < (size - 1)) {
if (arr[i] == arr[j]) {
System.out.println(arr[i] + " repeated at index " + j);
j = size;
}
j++;
if (j >= (size - 1)) {
i++;
j = i + 1;
}
}
}
배열이 너무 크지 않은 경우이 솔루션은 더 간단합니다. 동일한 크기의 다른 배열을 생성합니다.
1 입력 배열과 동일한 크기의 비트 맵 / 배열을 만듭니다.
int check_list[SIZE_OF_INPUT];
for(n elements in checklist)
check_list[i]=0; //initialize to zero
2 입력 배열을 스캔하고 위 배열에서 개수를 늘립니다.
for(i=0;i<n;i++) // every element in input array
{
check_list[a[i]]++; //increment its count
}
3 이제 check_list 배열을 스캔하고 복제 된 항목을 한 번 또는 여러 번 인쇄합니다.
for(i=0;i<n;i++)
{
if(check_list[i]>1) // appeared as duplicate
{
printf(" ",i);
}
}
물론 위의 솔루션에 의해 소비되는 공간의 두 배를 차지하지만 시간 효율성은 기본적으로 O (n) 인 O (2n)입니다.
O(1)
공간 이 아닙니다 .
a[a[i]]
제약 은 솔루션에 대한 약간의 단서를 제공합니다. 모든 유효한 배열 값은에서 유효한 배열 인덱스 힌트이기도 하고 O (1) 공간 제약 조건은swap()
작업에서 핵심 적인 힌트 라는 사실입니다.