O (n)에서 길이 n의 정렬되지 않은 배열에서 k 번째로 큰 요소를 찾는 방법이 있다고 생각합니다. 또는 아마도 “예상 된”O (n) 또는 무언가 일 것입니다. 우리는 어떻게 이것을 할 수 있습니까?
답변
이것을 k 차 통계량 찾기라고 합니다 . 평균 시간, 최악의 경우 시간을 취하는 매우 간단한 무작위 알고리즘 ( quickselect ) O(n)
과 O(n^2)
최악의 경우를 취하는 매우 복잡한 비 랜덤 화 알고리즘 ( introselect )이 O(n)
있습니다. Wikipedia에 대한 정보가 있지만 그다지 좋지 않습니다.
필요한 것은 이 파워 포인트 슬라이드에 있습니다 . O(n)
최악의 알고리즘 (introselect) 의 기본 알고리즘을 추출하기 만하면됩니다 .
Select(A,n,i):
Divide input into ⌈n/5⌉ groups of size 5.
/* Partition on median-of-medians */
medians = array of each group’s median.
pivot = Select(medians, ⌈n/5⌉, ⌈n/10⌉)
Left Array L and Right Array G = partition(A, pivot)
/* Find ith element in L, pivot, or G */
k = |L| + 1
If i = k, return pivot
If i < k, return Select(L, k-1, i)
If i > k, return Select(G, n-k, i-k)
Cormen et al.의 알고리즘 소개 책에도 매우 자세하게 설명되어 있습니다.
답변
당신이 진실을 원한다면 O(n)
알고리즘O(kn)
이와는 반대로 quickselect를 사용해야합니다 (기본적으로 관심없는 파티션을 버리는 곳은 quicksort입니다). 내 교수님은 런타임 분석을 통해 훌륭한 글을 작성했습니다 : ( reference )
QuickSelect 알고리즘은 정렬되지 않은 n
요소 배열에서 k 번째로 작은 요소를 빠르게 찾습니다 . 이것은 RandomizedAlgorithm 그래서 우리는 최악의 경우는 계산, 예상 시간을 실행할 수 있습니다.
알고리즘은 다음과 같습니다.
QuickSelect(A, k)
let r be chosen uniformly at random in the range 1 to length(A)
let pivot = A[r]
let A1, A2 be new arrays
# split into a pile A1 of small elements and A2 of big elements
for i = 1 to n
if A[i] < pivot then
append A[i] to A1
else if A[i] > pivot then
append A[i] to A2
else
# do nothing
end for
if k <= length(A1):
# it's in the pile of small elements
return QuickSelect(A1, k)
else if k > length(A) - length(A2)
# it's in the pile of big elements
return QuickSelect(A2, k - (length(A) - length(A2))
else
# it's equal to the pivot
return pivot
이 알고리즘의 실행 시간은 무엇입니까? 적이 우리를 위해 동전을 뒤집 으면 피벗이 항상 가장 큰 요소이며k
항상 1이며
T(n) = Theta(n) + T(n-1) = Theta(n2)
그러나 선택 사항이 실제로 임의 인 경우 예상 실행 시간은 다음과 같습니다.
T(n) <= Theta(n) + (1/n) ∑i=1 to nT(max(i, n-i-1))
우리는 재귀가 항상 더 큰 영역에 도달한다고 전적으로 합리적이지 않다고 가정하고 있습니다. A1
또는A2
.
T(n) <= an
일부를 추측 해 봅시다a
. 그럼 우리는 얻는다
T(n)
<= cn + (1/n) ∑i=1 to nT(max(i-1, n-i))
= cn + (1/n) ∑i=1 to floor(n/2) T(n-i) + (1/n) ∑i=floor(n/2)+1 to n T(i)
<= cn + 2 (1/n) ∑i=floor(n/2) to n T(i)
<= cn + 2 (1/n) ∑i=floor(n/2) to n ai
이제 어떻게 든 우리는 왼쪽에있는 +를 흡수하기 위해 더하기 부호의 오른쪽에 끔찍한 합계를 가져와야합니다 cn
. 우리가 방금 묶으면 대략적으로 얻을 수 있습니다 . 그러나 이것은 너무 큽니다-여분으로 짜낼 공간이 없습니다.2(1/n) ∑i=n/2 to n an
2(1/n)(n/2)an = an
cn
. 산술 시리즈 공식을 사용하여 합계를 확장 해 봅시다.
∑i=floor(n/2) to n i
= ∑i=1 to n i - ∑i=1 to floor(n/2) i
= n(n+1)/2 - floor(n/2)(floor(n/2)+1)/2
<= n2/2 - (n/4)2/2
= (15/32)n2
여기서 우리는 n이 “충분히 크다”는 것을 이용하여 못생긴 floor(n/2)
요소를 훨씬 더 깨끗하고 작은 것으로 대체합니다 n/4
. 이제 우리는 계속할 수 있습니다
cn + 2 (1/n) ∑i=floor(n/2) to n ai,
<= cn + (2a/n) (15/32) n2
= n (c + (15/16)a)
<= an
제공 a > 16c
.
이 제공합니다 T(n) = O(n)
. 분명히 Omega(n)
그렇습니다 T(n) = Theta(n)
.
답변
그 ( ‘kth 가장 큰 요소 배열’)에 대한 빠른 Google은 이것을 반환했습니다 : http://discuss.joelonsoftware.com/default.asp?interview.11.509587.17
"Make one pass through tracking the three largest values so far."
(특히 3d 최대)
그리고이 답변 :
Build a heap/priority queue. O(n)
Pop top element. O(log n)
Pop top element. O(log n)
Pop top element. O(log n)
Total = O(n) + 3 O(log n) = O(n)
답변
당신은 퀵 정렬을 좋아합니다. 무작위로 원소를 고르고 모든 것을 높이거나 낮추십시오. 이 시점에서 실제로 선택한 요소를 알 수 있으며 수행 한 k 번째 요소 인 경우 bin (높거나 낮은)으로 반복하여 k 번째 요소가 빠지게됩니다. 통계적으로 말하면, 시간 k 번째 원소가 n, O (n)으로 커지는 것을 찾는 데 필요합니다.
답변
프로그래머의 알고리즘 분석 도우미 (Companion to Algorithm Analysis ) 는 O (n) 버전을 제공 하지만 저자는 상수 인자가 너무 높지만 순진한 정렬 목록과 선택 방법을 선호 할 것입니다.
나는 당신의 질문의 편지에 대답했습니다 🙂
답변
C ++ 표준 라이브러리는 데이터를 수정하더라도 거의 정확하게 함수 호출을 갖 nth_element
습니다. 선형 런타임 O (N)을 예상했으며 부분 정렬도 수행합니다.
const int N = ...;
double a[N];
// ...
const int m = ...; // m < N
nth_element (a, a + m, a + N);
// a[m] contains the mth element in a
답변
O (n) 복잡성에 대해서는 잘 모르지만 O (n)과 nLog (n) 사이에 있어야합니다. 또한 nLog (n)보다 O (n)에 더 가깝습니다. 함수는 자바로 작성
public int quickSelect(ArrayList<Integer>list, int nthSmallest){
//Choose random number in range of 0 to array length
Random random = new Random();
//This will give random number which is not greater than length - 1
int pivotIndex = random.nextInt(list.size() - 1);
int pivot = list.get(pivotIndex);
ArrayList<Integer> smallerNumberList = new ArrayList<Integer>();
ArrayList<Integer> greaterNumberList = new ArrayList<Integer>();
//Split list into two.
//Value smaller than pivot should go to smallerNumberList
//Value greater than pivot should go to greaterNumberList
//Do nothing for value which is equal to pivot
for(int i=0; i<list.size(); i++){
if(list.get(i)<pivot){
smallerNumberList.add(list.get(i));
}
else if(list.get(i)>pivot){
greaterNumberList.add(list.get(i));
}
else{
//Do nothing
}
}
//If smallerNumberList size is greater than nthSmallest value, nthSmallest number must be in this list
if(nthSmallest < smallerNumberList.size()){
return quickSelect(smallerNumberList, nthSmallest);
}
//If nthSmallest is greater than [ list.size() - greaterNumberList.size() ], nthSmallest number must be in this list
//The step is bit tricky. If confusing, please see the above loop once again for clarification.
else if(nthSmallest > (list.size() - greaterNumberList.size())){
//nthSmallest will have to be changed here. [ list.size() - greaterNumberList.size() ] elements are already in
//smallerNumberList
nthSmallest = nthSmallest - (list.size() - greaterNumberList.size());
return quickSelect(greaterNumberList,nthSmallest);
}
else{
return pivot;
}
}