C++で最大値・最小値を交互に並べるソートを実装する方法
問題の概要
整数のリスト nums が与えられたとき、以下のルールに従ってリストを並べ替えることを考えます。
- 1番目の要素は最大値
- 2番目の要素は最小値
- 3番目の要素は2番目に大きい値
- 4番目の要素は2番目に小さい値
以降も同様に、大きい値と小さい値を交互に配置していきます。
たとえば、入力が [6, 3, 10, 4] の場合、出力は [10, 3, 6, 4] となります。
解法のアプローチ
この問題は、配列をあらかじめ昇順にソートしておき、両端から交互に要素を取り出すことで解決できます。具体的な手順は以下の通りです。
- 結果を格納するための配列
retを定義します。 - 配列
numsを昇順にソートします。 jを「numsのサイズ − 1」で初期化します(末尾=最大値側)。iを0で初期化します(先頭=最小値側)。i <= jの間、以下を繰り返します。retの末尾にnums[j](大きい側)を追加し、jを1減らします。- その後も
i <= jが成り立つ場合は、retの末尾にnums[i](小さい側)を追加し、iを1増やします。
- 最後に
retを返します。
この方法では、ソート済みの配列の後ろから最大値を、前から最小値を交互に拾っていくため、要素数が奇数の場合でも中央の要素が正しく一度だけ処理されます。
C++による実装例
#include <bits/stdc++.h>
using namespace std;
void print_vector(vector<auto> v){
cout << "[";
for(int i = 0; i<v.size(); i++){
cout << v[i] << ", ";
}
cout << "]"<<endl;
}
class Solution {
public:
vector<int> solve(vector<int> & nums) {
vector<int> ret;
sort(nums.begin(), nums.end());
int j = nums.size() - 1;
int i = 0;
while (i <= j) {
ret.push_back(nums[j]);
j--;
if (i <= j) {
ret.push_back(nums[i]);
i++;
}
}
return ret;
}
};
main() {
Solution ob;
vector<int> v = {6,3,10,4};
print_vector(ob.solve(v));
}入力
{6,3,10,4}出力
10, 3, 6, 4
計算量について
このアルゴリズムの時間計算量は、ソート処理が支配的となるため O(n log n) です。ソート後の交互配置は配列を一度走査するだけなので O(n) で済みます。また、結果用の配列 ret が必要なため、空間計算量は O(n) となります。元の配列を破壊してもよい場合は、インプレースで両端の要素を入れ替えながら処理することで、追加メモリを削減することも可能です。
-
C++で解く「ジャンプゲームV」:メモ化再帰による最大訪問インデックス数の求め方
問題の概要整数型の配列 arr と整数 d が与えられます。1ステップごとに、インデックス i から次の場所へジャンプできます。右方向: i + x(ただし i + x < n、かつ x は 1 以上 d 以下)左方向: i - x(ただし i - x >= 0、かつ x は 1 以上 d 以下)ここで n は配列のサイズです。さらに重要な制約として、インデックス i から j へジャンプできるのは、arr[i] > arr[j] であり、かつ i と j の間にあるすべてのインデックス k に対して arr[i] > arr[k] を満たす場合のみです。つまり、より低
-
C++による3方向マージソートの実装と解説
マージソートは、配列を再帰的に2つの部分に分割し、それぞれをソートしてからマージ(併合)するアルゴリズムです。このバリエーションの一つに「3方向マージソート(3-way Merge Sort)」があり、配列を2つではなく3つの部分に分割して処理を行います。 基本概念 通常のマージソートでは、配列を半分のサイズの部分配列に再帰的に分解します。一方、3方向マージソートでは、配列を3分の1のサイズの部分配列に分解していきます。分割数が増えることで再帰の深さが浅くなる(底が3の対数になる)という特徴があります。 実行例 入力: 46, -1, -44, 79, 31, -41, 11, 20, 7