整数配列前Kが大きい問題
5046 ワード
このような問題は最高周波K項の基礎問題である.質問の説明は次のとおりです:
このオフライン処理では,Quick Selectアルゴリズムを用いることができる.
Quick Select
Quick Selectアルゴリズムの基本思想:速列の分治思想を利用して、探索対象配列の主元pivotを求め、主元を境に左右2つの区間 に分ける.主元の位置を比較することにより、K番目の大きさの数が主元左区間か主元右区間かを判断する.それとも主元ですか?(境界条件の判断にも注意し、境界にある可能性がある) サブゾーン間再帰呼び出し
オンラインアルゴリズム
データ・ストリームの形式では、最初から遍歴する機会はありません.解決策は山を考えることだ.この問題では、削除操作をサポートする必要はありません.データは増加しても減らないだけです.では、K位にランクインしたデータは、前のk位に入る機会がありません.そのため、これらのデータを保持する意味は大きくありません.削除することができます.これで、最小のスタックを維持する必要があります.整数がTopKの集合に加わるかどうかを決定する必要がある場合、決定的な要因は、現在の整数がTopKの最小数よりも大きいかどうかである.したがって,TopKのk個の整数では,最小のスタックを維持し,最小の整数を推挙してデータストリームから新しく出た整数と比較し,新しく来た数がもっと大きいと,スタック内の最小の整数を蹴り落とし,新しい数を加え,逆に新しい数を捨てるべきである.
高速ソートの実装
集計ソートの実装
, k
オフラインアルゴリズムこのオフライン処理では,Quick Selectアルゴリズムを用いることができる.
O(N)
時間以内に配列のK番目の大きな数を見つけ、配列全体をスキャンすると前のK番目の大きな数が得られる.このアルゴリズムは速排思想に基づいている.Quick Select
Quick Selectアルゴリズムの基本思想:
import java.util.Random;
class Solution {
/*
* @param nums an integer array
* @param k an integer
* @return the top k largest numbers in array
*/
public int[] topk(int[] nums, int k) {
// Write your code here
quickSort(nums, 0, nums.length - 1, k);
int[] topk = new int[k];
for (int i = 0; i < k; ++i)
topk[i] = nums[i];
return topk;
}
private void quickSort(int[] A, int start, int end, int k) {
if (start >= k)
return;
if (start >= end) {
return;
}
int left = start, right = end;
// key point 1: pivot is the value, not the index
//
Random rand =new Random(end - start + 1);
//nextInt(int n) int , [0,n) , 0 n int , 0 n
int index = rand.nextInt(end - start + 1) + start;
int pivot = A[index];
// key point 2: every time you compare left & right, it should be
// left <= right not left < right
while (left <= right) {
// key point 3: A[left] < pivot not A[left] <= pivot
while (left <= right && A[left] > pivot) {
left++;
}
// key point 3: A[right] > pivot not A[right] >= pivot
while (left <= right && A[right] < pivot) {
right--;
}
if (left <= right) {
int temp = A[left];
A[left] = A[right];
A[right] = temp;
left++;
right--;
}
}
quickSort(A, start, right, k);
quickSort(A, left, end, k);
}
}
オンラインアルゴリズム
データ・ストリームの形式では、最初から遍歴する機会はありません.解決策は山を考えることだ.この問題では、削除操作をサポートする必要はありません.データは増加しても減らないだけです.では、K位にランクインしたデータは、前のk位に入る機会がありません.そのため、これらのデータを保持する意味は大きくありません.削除することができます.これで、最小のスタックを維持する必要があります.整数がTopKの集合に加わるかどうかを決定する必要がある場合、決定的な要因は、現在の整数がTopKの最小数よりも大きいかどうかである.したがって,TopKのk個の整数では,最小のスタックを維持し,最小の整数を推挙してデータストリームから新しく出た整数と比較し,新しく来た数がもっと大きいと,スタック内の最小の整数を蹴り落とし,新しい数を加え,逆に新しい数を捨てるべきである.
class Solution {
public int findKthLargest(int[] nums, int k) {
if (k <= 0 || nums.length == 0 || nums == null) {
return 0;
}
PriorityQueue minHeap = new PriorityQueue();
for (int i = 0; i < nums.length; i++) {
minHeap.add(nums[i]);
if (minHeap.size() > k) {
minHeap.poll();
}
}
return minHeap.poll();
}
}
高速ソートの実装
# quicksort
class Solution:
def quicksort(self, A, start, end):
if A is None or len(A) == 0:
return None
if start >= end:
return None
left = start
right = end
# pivot is value not index
pivot = start + (end - start) // 2
# left <= right, stackoverflow
while left <= right:
while left <= right and A[left] < pivot:
left += 1
while left <= right and A[right] > pivot:
right -= 1
if left <= right:
A[left], A[right] = A[right], A[left]
left += 1
right -= 1
self.quicksort(A, start, right)
self.quicksort(A, left, end)
集計ソートの実装
class Solution:
def sortIntegers(self, A):
temp = [0 for _ in range len(A)]
self.merge_sert(0, len(A) - 1, A, temp)
def merge_sort(self, start, end, A, temp):
if start >= end:
return
mid = start + (end - start) // 2
self.merge_sort(start, mid, A, temp)
self.merge_sort(mid+1, end, A, temp)
self.merge(start, mid, end, A, temp)
def merge(self, start, mid, A, end, temp):
left = start
right = mid + 1
index = start
while left <= mid and right <= end:
if A[left] < A[right]:
temp[index] = A[left]
left += 1
else:
temp[index] = A[right]
right += 1
index += 1
while left <= mid:
temp[index] = A[left]
left += 1
while right <= end:
temp[index] = A[right]
right += 1
for index in range(start, end+1):
A[index] = temp[index]