C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++で配列をM回連結したときのK番目に小さい要素を求める方法

問題の概要

配列Aと、2つの整数K・Mが与えられたとします。このとき、配列Aを自分自身にM回連結した後の配列から、K番目に小さい要素を求める必要があります。

例として、配列が A = [3, 1, 2]、K = 4、M = 3 の場合を考えてみましょう。配列Aを3回連結すると [3, 1, 2, 3, 1, 2, 3, 1, 2] となり、この中で4番目に小さい要素は「2」です。

解法のアプローチ

一見すると、実際に配列をM回連結して巨大な配列を作り、そこからK番目に小さい要素を探す必要があるように思えます。しかし、それではメモリ使用量や計算時間が無駄にかかってしまいます。

ここで重要なポイントは、同じ配列を繰り返し連結しても、各要素の出現頻度は均等にM倍になるという点です。したがって、元の配列Aをソートしておけば、連結後の配列におけるK番目に小さい要素は、ソート済み配列のインデックス ((K - 1) / M) の位置にある値と一致します。

つまり、解法の手順は以下の通りです。

  1. 配列Aをソートする
  2. インデックス (K - 1) / M の位置にある要素を返す

これにより、実際に連結を行うことなく、O(N log N) の計算量で効率的に答えを求められます。

実装例(C++)

#include<iostream>
#include<algorithm>
using namespace std;

int findKSmallestNumber(int A[], int N, int M, int K) {
    sort(A, A + N);
    return (A[((K - 1) / M)]);
}

int main() {
    int A[] = { 3, 1, 2 };
    int M = 3, K = 4;
    int N = sizeof(A) / sizeof(A[0]);
    cout << K << "番目に小さい数(連結" << M << "回): "
         << findKSmallestNumber(A, N, M, K);
}

出力結果

4番目に小さい数(連結3回): 2

処理の流れの解説

上記のコードでは、まず sort() 関数を使って配列Aを昇順に並べ替えています。ソート後の配列は [1, 2, 3] となります。

K = 4、M = 3 の場合、(K - 1) / M = 3 / 3 = 1 となるため、インデックス1の位置にある要素、すなわち「2」が返されます。これは、連結後の配列 [3, 1, 2, 3, 1, 2, 3, 1, 2] をソートした [1, 1, 1, 2, 2, 2, 3, 3, 3] の4番目の要素と一致しています。

まとめ

配列をM回連結した場合のK番目に小さい要素を求める問題は、実際に連結を行わずとも、元の配列をソートしてインデックス (K - 1) / M の要素を参照するだけで解決できます。このテクニックを使えば、大きなメモリを消費することなく、シンプルかつ高速に目的の値を取得できます。

  1. C++で配列内の各要素のサーパッサー(Surpasser)の数を求めるアルゴリズム

    ある配列Aが与えられたとき、各要素の「サーパッサー(surpasser)」の数を求める問題を考えてみましょう。サーパッサーとは、現在注目している要素よりも右側に存在する、その要素より大きい値のことです。 例えば、A = {2, 7, 5, 3, 0, 8, 1} という配列の場合、サーパッサーの数は {4, 1, 1, 1, 2, 0, 0} となります。これは、先頭の「2」の右側には「7・5・3・8」という4つの大きな値が存在するためです。その他の要素についても同じルールで数えていきます。 アルゴリズムの考え方 解法は非常にシンプルです。2重のループを使用し、外側のループで各要素を順に取り上

  2. 配列の分割(パーティション)手法でk番目に小さい要素を見つけるC++プログラム

    本記事では、配列を分割(パーティション)する手法を用いて、配列内のk番目に小さい要素を求めるC++プログラムを解説します。この手法はクイックソートの考え方を応用したもので、配列全体をソートすることなく、目的の要素だけを効率的に特定できる点が特徴です。 アルゴリズム まず、ピボットを基準に配列を分割する CreatePartition() 関数と、その結果をもとにk番目に小さい要素が存在する範囲を再帰的に絞り込む Partition() 関数を使用します。 Begin 関数 CreatePartition() は 配列 a、下限 l、上限 h を引数にとる in := l、pi