답변:
면접이든 실제 작업이든, 우선 순위는 자신에게 맞는 효과적인 솔루션 이어야 합니다 . 그것은 일반적으로 당신은 당신이 간단 생각할 수있는 최초의 솔루션을 제공하고 쉽게에 대한한다는 뜻 은 설명합니다.
저에게는 숫자를 정렬하고 간격을 스캔 하는 것을 의미 합니다. 그러나 저는 비즈니스 시스템과 웹 앱에서 일합니다. 나는 비트와 함께 바이올린을 피우지 않으며 팀이 싫어요!
저수준의 금속에 가까운 직업에 대해 인터뷰하는 경우, "정렬"은 아마도 빈 응시로 만날 것입니다. 그들은 당신이 비트 등에 대해 편안하게 생각하기를 원합니다. 첫 번째 대답은 "아, 나는 비트 맵을 사용할 것입니다." (또는 비트 배열 또는 비트 세트)
그리고 어느 쪽이든-틀린 해결책을 제시 하더라도, 면접관 (또는 상사!)이 압박을 가하면 관리자의 특정 관심 영역에 초점을 맞추고 개선 또는 대안을 제안 할 수 있습니다.
O(n*log(n))
있습니다. (또는 정수 버킷 정렬의 경우 O (n)!)BitSet
/ BitMap
/ BitArray
)BitArray
플래그에 "찾을 수 있습니다." 그리고를 검색하십시오 0
.BitArray
/BitSet
(찾기 위해0
'들). 그렇습니다O(n)
.또는 무엇이든.
실제로 우려 사항을 해결하십시오 . 필요한 경우 순진한 솔루션을 사용하여 먼저 문제를 해결하십시오. 아직 존재하지 않는 문제를 해결하기 위해 모든 사람의 시간을 낭비하지 마십시오.
파일이기 때문에 여러 번 통과 할 수 있다고 가정합니다. 먼저 256 카운터의 배열을 만들고 파일을 반복하고 각 숫자마다 숫자의 첫 번째 바이트로 인덱싱 된 카운터를 증가시킵니다. 완료되면 대부분의 카운터는 2 ^ 24에 있어야하지만 1 ~ 4 개의 카운터는 더 낮은 값을 가져야합니다. 이러한 각 색인은 누락 된 숫자 중 하나의 첫 번째 바이트를 나타냅니다 (4보다 작은 경우 여러 누락 된 숫자가 동일한 첫 번째 바이트를 공유하기 때문입니다).
이러한 각 인덱스에 대해 256 카운터의 다른 배열을 만들고 파일을 다시 통과시킵니다. 이 시간은, 첫번째 바이트는 이전의 값 중 하나 인 경우에 기초하여 그것의 배열에서 카운터를 증가 제 바이트를 . 완료되면 2 ^ 16보다 낮은 카운터를 다시 찾아보고 누락 된 숫자의 두 번째 바이트를 각각 첫 번째 바이트와 일치시킵니다.
세 번째 바이트 (각 패스에 최대 4 개의 다른 바이트가 올 수 있지만 각 패스에 최대 4 개의 배열이 필요함)와 네 번째 바이트에 대해 다시 수행하면 누락 된 숫자가 모두 발견됩니다.
시간 복잡성- O(n * log n)
공간 복잡성- 일정 !
실제로, 나는 n=2^32
매개 변수 라고 생각 했지만 누락 된 숫자의 수 k=4
는 매개 변수입니다. k<<n
이것이 공간 복잡성을 의미 한다고 가정하면O(k)
.
재미를 위해 (그리고 현재 Rust를 배우려고 노력하고 있기 때문에) Rust에서 구현했습니다 : https://gist.github.com/idanarye/90a925ebb2ea57de18f03f570f70ea1f . 나는 ~ 2 ^ 32 숫자로 그것을 실행할 것이기 때문에 텍스트 표현을하기로 선택했습니다 ...
이것이 Java 인 경우 BitSet을 사용할 수 있습니다. 글쎄, 그중 두 개는 32 비트 숫자를 모두 보유 할 수 없기 때문에. 골격 코드, 아마도 버그 :
BitSet bitsetForPositives = new Bitset(2^31); // obviously not 2^31 but you get the idea
BitSet bitsetForNegatives = new Bitset(2^31);
for (int value: valuesTheyPassInSomehow) {
if ((value & 0x80000000) == 0)
bitsetForPositives.set(value );
else
bitsetForNegatives.set(value & ~0x80000000);
}
그런 다음 BitSet.nextClearBit()
누가 빠진 사람을 찾는 데 사용하십시오 .
훨씬 나중에 추가 된 참고 사항 :
이 알고리즘을 사용하면 시간이 많이 걸리는 부분 을 병렬 로 실행하는 것이 매우 쉽습니다 . 원본 파일이 대략 네 부분으로 나뉘어져 있다고 가정 해 봅시다. 4 쌍의 BitSet을 할당합니다 (2GB, 여전히 관리 가능).
I / O가 여전히 속도 제한 단계가 될 것으로 기대하지만 마술처럼 모든 숫자가 메모리에 있으면 실제로 속도를 높일 수 있습니다.
Integer.MIN_VALUE
올바르게 처리되지 않습니다 . 부호 비트를 수정하지 않고 부호 비트를 마스킹 할 수 있습니다.
bool GetBit(byte[] byteArray, uint index) { var byteIndex = index >> 3; var bitInByte = index & 7; return (byteArray[byteIndex] >> bitInByte) & 1 != 0; }
이 질문은 비트 배열 (true / false)을 사용하여 해결할 수 있습니다. 이것은 특정 숫자의 발견 여부를 보유하기 위해 배열의 색인을 사용하여 모든 숫자에 대한 답을 보유하는 가장 효율적인 구조 여야합니다.
기음#
var bArray = new BitArray(Int32.MaxValue);
//Assume the file has 1 number per line
using (StreamReader sr = File.OpenText(fileName))
{
string s = String.Empty;
while ((s = sr.ReadLine()) != null)
{
var n = int32.Parse(s);
bArray[n] = true;
}
}
그런 다음 배열을 반복하고 여전히 거짓 인 값의 경우 파일에 없습니다.
파일을 작은 덩어리로 나눌 수는 있지만 Windows 7 (64 비트)을 실행하는 16.0GB 노트북에 전체 int32 최대 크기 배열 (2147483647)을 할당 할 수있었습니다.
64 비트를 실행하지 않더라도 더 작은 비트 배열을 할당 할 수 있습니다. 사용 가능한 환경 자원에 적합한 범위가 [0-64000] [64001-128000] 등인 작은 파일 세트를 작성하여 파일을 사전 처리합니다. 큰 파일을 살펴보고 각 숫자를 해당 세트 파일에 씁니다. 그런 다음 각 작은 파일을 처리하십시오. 사전 처리 단계로 인해 시간이 조금 더 걸리지 만 리소스가 제한되어 있으면 리소스 제한이 해결됩니다.
이것은 면접 질문이므로 면접관에게 제약 조건에 대한 이해를 보여줄 것입니다. 그렇다면 "가능한 모든 숫자"는 무엇을 의미합니까? 모두가 짐작하는 것처럼 실제로 0 ... 2 <(32-1)입니까? 일반적인 32 비트 아키텍처는 32 비트 이상의 숫자로 작동 할 수 있습니다. 분명히 표현의 문제 일뿐입니다.
32 비트 시스템에서 해결해야합니까, 아니면 숫자 제한의 일부입니까? 예를 들어 일반적인 32 비트 시스템에서는 파일을 RAM에 한 번에로드 할 수 없습니다. 또한 32 비트 시스템은 파일 크기 제한으로 인해 모든 숫자가 포함 된 파일을 가질 수없는 경우가 종종 있습니다. "4를 제외한 모든 숫자"와 같이 영리한 인코딩이 없다면 문제는 사소하게 해결됩니다.
그러나 실제로 질문을 "몇 개를 제외한 모든 숫자가 0 ... 2 ^ (32-1) 인 파일을 제공하면 누락 된 숫자를 줘"라고 이해하려면 (그리고 이것이 큰 경우 !) 여러 가지 방법으로 해결할 수 있습니다.
사소하지만 실현 불가능 : 가능한 각 숫자에 대해 파일을 스캔하여 파일이 있는지 확인하십시오.
512MB의 RAM과 파일을 통한 단일 패스 : 파일에서 읽은 모든 번호 (= 해당 인덱스에서 비트 설정)를 표시 한 다음 RAM을 한 번 전달하고 누락 된 것을 확인합니다.
인터뷰에서 기억하기 쉽고 표현하기 쉬운 한 가지 접근법은 N 비트의 모든 숫자를 보면 각 비트가 해당 값의 정확히 절반에 설정되고 다른 절반에 설정되지 않는다는 사실을 사용하는 것입니다. .
파일의 모든 값을 반복하고 끝에 32 개의 값을 유지하면 해당 값보다 정확히 (2 ^ 32 / 2) 약간 작은 32 개의 값으로 끝납니다. 최대 값 (2 ^ 32 / 2)과 총계의 차이는 결 측값의 각 위치에 설정된 총 비트 수를 제공합니다.
일단 가지고 나면, 그 총계를 줄 수있는 4 가지 값의 모든 가능한 세트를 결정할 수 있습니다. 그런 다음 파일의 값을 다시 검토하여 해당 조합의 일부인 값을 확인할 수 있습니다. 하나를 찾으면 해당 값을 포함하는 조합이 가능성으로 제거됩니다. 가능한 조합이 하나만 남아 있으면 대답합니다.
예를 들어 니블을 사용하면 다음 값이 있습니다.
1010
0110
1111
0111
1101
1001
0100
0101
0001
1011
1100
1110
각 위치에 설정된 총 비트 수는 다음과 같습니다.
7867
8 (4 ^ 2 / 2)에서 그것들을 빼면 다음과 같습니다.
1021
이는 다음 가능한 4 가지 값 세트가 있음을 의미합니다.
1000
0000
0011
0010
1010
0001
0010
0000
(내가 놓친 경우 용서하십시오, 나는 단지 이것을 시각으로하고 있습니다)
그리고 원래 숫자를 다시 살펴보면 1010이 바로 첫 번째 세트가 답이라는 것을 의미합니다.
determine all the possible sets of 4 values that could give those totals
. 나는 이것이 이것이 당신의 대답에서 누락 된 솔루션의 중요한 부분이라고 생각합니다. 또한 시간과 공간의 복잡성에 영향을 줄 수 있습니다.
파일을 숫자를 늘려 정렬한다고 가정합니다.
실제로 (2³²-4) 숫자가 포함되어 있는지 확인하십시오.
이제 파일이 완성 된 경우 (또는 4 개의 누락 된 숫자가 마지막 4 개의 숫자 인 경우) 파일의 위치 N에있는 단어를 읽으면 일치하는 값 N이 반환됩니다.
예상치 못한 첫 번째 숫자 X1을 찾으려면 위치 [0..2³²-4-1)에서 이분법 검색을 사용하십시오.
첫 번째 누락 된 숫자를 찾으면 [X1 .. (2³²-4-1)] 위치에서 이분법 검색을 다시 수행하여 두 번째 누락 된 X2를 찾으십시오. 이번에는 N 위치에서 단어를 읽으면 일치하는 값 N-1이 반환됩니다. 누락 된 숫자가 더 이상없는 경우 (하나의 누락 된 숫자를 전달한 이후)
나머지 두 숫자에 대해서도 마찬가지로 반복하십시오. 세 번째 반복에서 N 위치의 단어를 읽으면 N-2가 반환되고 네 번째 반복에서는 N-3이 반환됩니다.
경고 : 나는 이것을 테스트하지 않았습니다. 그러나 나는 그것이 작동해야한다고 생각합니다. :)
현실에서 나는 다른 답변에 동의합니다. 첫 번째 질문은 환경에 관한 것입니다. 우리는 RAM을 사용할 수 있습니까 (얼마나 많은가) 직접 액세스 저장 장치에있는 파일입니까, 이것은 원샷 작업 (최적화 필요 없음) 또는 중요한 작업 (각 사이클 수)입니까? 외부 정렬 유틸리티를 사용할 수 있습니까? 등
의 컨텍스트에 대한 허용 타협을 찾을 수 있습니다. 이것은 적어도 알고리즘을 찾기 전에 문제 분석을 시작한다는 것을 보여줍니다.
모든 표준 질문과 마찬가지로 해결책은 인터뷰 전에 Google에 질문하는 것입니다.
이 질문과 변형에는 모든 숫자의 XORing과 관련된 매우 정확한 '정답'이 있습니다. 데이터베이스 또는 인덱스의 인덱스를 이해하고 있음을 보여줍니다. 따라서 '어쩌면 효과가 있지만 논문에 나와있는 내용은 없습니다'에 대한 영점은 imfriad입니다.
장점에는 몇 시간 동안 수정하면 천재처럼 보일 수있는 유한 한 질문이 있습니다. 당신이 당신의 머리에서 그것을 해결하는 척하는 것을 잊지 마십시오.
편집하다. Ahh 4에 대해서는 XOR과 다른 접근법이있는 것 같습니다.
편집하다. Downvoters : 이것은 OP에 명시된 정확한 문제에 대한 출판 된 교재 O (n) 솔루션입니다.