この解答例はChatGPTによって生成されています,正しいかは自己判断で。 皆の投稿からも解答例が見つかるかもしれませんよ。
BFE.devでのAI解答例 - JavaScript コーディング問題
45. ソートしてない配列にてK番目に大きい要素を見つける
解法1: Quick Select
Quick Selectは、Quick Sortの応用であり、特定の要素をソートする必要がない場合に最適です。これは、一定の時間内にK番目大きい数字を見つけることができることが知られています。
アルゴリズムのアイデアは以下の通りです。
- 配列の中からピボットを選んで、それより大きい数字を右に、小さい数字を左に分割します。
- 分割後、ピボットがソートされた配列の何番目に位置するかを調べます。
- もし、ピボットがK番目の数字に位置しているならそれを返します。
- そうでなければ、右側にあるか左側にあるかを判断して再帰的に行います。
以下が実装例です。
function quickSelect(arr, k) { const pivotIndex = Math.floor(Math.random() * arr.length); const pivot = arr[pivotIndex]; const left = []; const right = []; for (let i = 0; i < arr.length; i++) { if (i !== pivotIndex) { arr[i] < pivot ? left.push(arr[i]) : right.push(arr[i]); } } if (right.length >= k) { return quickSelect(right, k); } else if (right.length === k - 1) { return pivot; } else { return quickSelect(left, k - right.length - 1); }}
このアルゴリズムでは、ピボットをランダムに選ぶことで最悪の場合でもO(n)
の時間でK番目の数字を見つけることができます。
解法2: Heap
Heapを使ってもK番目の数字を見つけることができます。Heapは、最大/最小の要素をO(1)
で見つけることができるという特徴がありますが、他の要素を見つけるのには線形時間がかかるため、この問題には不向きです。
しかし、K番目の数字を見つけるためにはK個の最大/最小の数字に興味がありますので、Kサイズの最小Heap(最大Heap)を使って部分的に配列をソートすることができます。
以下が実装例です。
function findKthLargest(arr, k) { const heap = new MinHeap(); for (let i = 0; i < arr.length; i++) { heap.insert(arr[i]); if (heap.size() > k) { heap.extract(); } } return heap.extract();}class MinHeap { constructor() { this.heap = [null]; } size() { return this.heap.length - 1; } insert(value) { this.heap.push(value); this.bubbleUp(this.size()); } extract() { if (this.size() < 1) { return null; } const value = this.heap[1]; const lastValue = this.heap.pop(); if (this.size() > 0) { this.heap[1] = lastValue; this.bubbleDown(1); } return value; } bubbleUp(index) { const value = this.heap[index]; while (index > 1 && value < this.heap[Math.floor(index / 2)]) { this.heap[index] = this.heap[Math.floor(index / 2)]; index = Math.floor(index / 2); } this.heap[index] = value; } bubbleDown(index) { const value = this.heap[index]; while (2 * index <= this.size()) { let childIndex = 2 * index; if (childIndex + 1 <= this.size() && this.heap[childIndex + 1] < this.heap[childIndex]) { childIndex++; } if (value <= this.heap[childIndex]) { break; } this.heap[index] = this.heap[childIndex]; index = childIndex; } this.heap[index] = value; }}
このアルゴリズムは、配列の長さがNのとき、最初にK個の数字をHeapに挿入するためにO(KlogK)
の時間がかかります。その後、残りの(N - K)
個の数字を一つずつ取り出してHeapに挿入します。Heapの最小値には常にK番目大きい数字が含まれるため、配列からの取り出しは最大でもN - K回行います。O(logK)
時間で最小値を取り出せるため、結局のところこのアルゴリズムの計算量はO(KlogK + (N - K)logK) = O(NlogK)
となります。