質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

0回答

458閲覧

マージソートのプログラムでわからないところ

jaogjig

総合スコア21

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2023/01/09 16:37

編集2023/01/09 17:14

前提

今「問題解決力を鍛える!アルゴリズムとデータ構造」著大槻兼資の本を読んでいます。p200のマージソートのフローチャートを書いてプログラムを理解しようとしてるところわからないところが出てきました。
その部分が下の部分です。

// 併合する int index_left = 0; // 左側の添字 int index_right = (int)buf.size() - 1; // 右側の添字 for (int i = left; i < right; ++i) { // 左側採用 if (buf[index_left] <= buf[index_right]) { a[i] = buf[index_left++]; } // 右側採用 else { a[i] = buf[index_right--]; } }

ここの部分の具体的にどこがわからないかというと
マージソート関数の再帰的に分割したまではいいのですが
マージ(統合)する時に、本書では一つ一つの要素を2、4、8とマージしています。
しかし下のコードを見ると

int index_left = 0; // 左側の添字 int index_right = (int)buf.size() - 1; // 右側の添字

一時的な配列を作ってそれぞれ変数を入れて比較するならわかりますが
やった部分mergesort(a,0,8),(4,8)(4,6)(6,8)(6,7)(7,8)...以下略
を活用しないで添字0からsize-1(7)といきなり8つの要素を合わせてソートしようとしているように見えます。
また、なぜ分割したのかがわからなくなってしまいました。
どのような手順でプログラムが動くかご教授お願いします。

要素数はN=8
配列はa[12,9,15,3,8,17,6,1]

// 併合する int index_left = 0; // 左側の添字 int index_right = (int)buf.size() - 1; // 右側の添字 for (int i = left; i < right; ++i) { // 左側採用 if (buf[index_left] <= buf[index_right]) { a[i] = buf[index_left++]; } // 右側採用 else { a[i] = buf[index_right--]; } }

該当のソースコード

#include <iostream> #include <vector> using namespace std; // 配列 a の区間 [left, right) をソートする // [left, right) は,left, left+1, ..., right-1 番目を表す void MergeSort(vector<int> &a, int left, int right) { if (right - left == 1) return; int mid = left + (right - left) / 2; // 左半分 [left, mid) をソート MergeSort(a, left, mid); // 右半分 [mid, right) をソート MergeSort(a, mid, right); // いったん「左」と「右」のソート結果をコピーしておく (右側は左右反転) vector<int> buf; for (int i = left; i < mid; ++i) buf.push_back(a[i]); for (int i = right - 1; i >= mid; --i) buf.push_back(a[i]); // 併合する int index_left = 0; // 左側の添字 int index_right = (int)buf.size() - 1; // 右側の添字 for (int i = left; i < right; ++i) { // 左側採用 if (buf[index_left] <= buf[index_right]) { a[i] = buf[index_left++]; } // 右側採用 else { a[i] = buf[index_right--]; } } } int main() { // 入力 int N; // 要素数 cin >> N; vector<int> a(N); for (int i = 0; i < N; ++i) cin >> a[i]; // マージソート MergeSort(a, 0, N); }

補足情報(FW/ツールのバージョンなど)

C++

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dameo

2023/01/09 18:47

多分再帰呼び出し的な部分が難しいのかと思います。 分割を行った後、統合を行っているのですが、分割の際に再帰呼び出しが使用されています。 再帰呼び出しから戻ってきたときには指定した範囲がソートされているので、左半分も右半分もどちらもソートされている状態で統合を行います。 参考までにコメント空行を削除して、固定データをソートするように変更した後、ちょっと出力を加えたコードを載せておきます。範囲を出力するタイミングと関数の最後で結果を出力するタイミングをよく見てください。 #include <iostream> #include <vector> using namespace std; void MergeSort(vector<int> &a, int left, int right) { if (right - left == 1) return; cout << "[" << left << "," << right << ")" << endl; int mid = left + (right - left) / 2; MergeSort(a, left, mid); MergeSort(a, mid, right); vector<int> buf; for (int i = left; i < mid; ++i) buf.push_back(a[i]); for (int i = right - 1; i >= mid; --i) buf.push_back(a[i]); int index_left = 0; int index_right = (int)buf.size() - 1; for (int i = left; i < right; ++i) { if (buf[index_left] <= buf[index_right]) { a[i] = buf[index_left++]; } else { a[i] = buf[index_right--]; } } cout << "["; bool first = true; for (auto v: a) { if (first) first = false; else cout << ","; cout << v; } cout << "]" << endl; } int main() { int N = 8; vector<int> a(N); for (int i = 0; i < N; ++i) a[i] = N - 1 - i; MergeSort(a, 0, N); return 0; } 結果 [0,8) [0,4) [0,2) [6,7,5,4,3,2,1,0] [2,4) [6,7,4,5,3,2,1,0] [4,5,6,7,3,2,1,0] [4,8) [4,6) [4,5,6,7,2,3,1,0] [6,8) [4,5,6,7,2,3,0,1] [4,5,6,7,0,1,2,3] [0,1,2,3,4,5,6,7]
jaogjig

2023/01/10 12:56

配列の最初の並びはどうなっているのでしょうか
dameo

2023/01/10 13:04

main関数内で for (int i = 0; i < N; ++i) a[i] = N - 1 - i; としているので、 [7,6,5,4,3,2,1,0] と逆順に並べています。 前のコードを変えて出力するとこんな感じです。 #include <iostream> #include <vector> using namespace std; void dumpVector(vector<int>&a) { cout << "["; bool first = true; for (auto v: a) { if (first) first = false; else cout << ","; cout << v; } cout << "]" << endl; } void MergeSort(vector<int> &a, int left, int right) { if (right - left == 1) return; cout << "[" << left << "," << right << ")" << endl; int mid = left + (right - left) / 2; MergeSort(a, left, mid); MergeSort(a, mid, right); vector<int> buf; for (int i = left; i < mid; ++i) buf.push_back(a[i]); for (int i = right - 1; i >= mid; --i) buf.push_back(a[i]); int index_left = 0; int index_right = (int)buf.size() - 1; for (int i = left; i < right; ++i) { if (buf[index_left] <= buf[index_right]) { a[i] = buf[index_left++]; } else { a[i] = buf[index_right--]; } } dumpVector(a); } int main() { int N = 8; vector<int> a(N); for (int i = 0; i < N; ++i) a[i] = N - 1 - i; dumpVector(a); MergeSort(a, 0, N); return 0; } 結果 [7,6,5,4,3,2,1,0] [0,8) [0,4) [0,2) [6,7,5,4,3,2,1,0] [2,4) [6,7,4,5,3,2,1,0] [4,5,6,7,3,2,1,0] [4,8) [4,6) [4,5,6,7,2,3,1,0] [6,8) [4,5,6,7,2,3,0,1] [4,5,6,7,0,1,2,3] [0,1,2,3,4,5,6,7]
jaogjig

2023/01/10 16:03

分割統治法自体がわからないと思ったので少し話が逸れる可能性がありますが質問させてください 以下のコードがある場合 なぜsolve(1,2)とsolve(2,3)は3+1で足すのでしょうか S1+S2が使われたからでしょうか しかし、コードを見る限り一番最後に合計を足すだけにreturn s1 + s2;が使われているようにしか見えません。どういう理屈でそうなっているかわかりますか 分割統治法による区間の合計値の計算をしているコード #include <iostream> using namespace std; int N, A[109]; int solve(int l, int r) { if (r - l == 1) return A[l]; int m = (l + r) / 2; // 区間 [l, r) の中央で分割する int s1 = solve(l, m); // s1 は A[l]+...+A[m-1] の合計値となる int s2 = solve(m, r); // s2 は A[m]+...+A[r-1] の合計値となる return s1 + s2; } int main() { // 入力 cin >> N; for (int i = 1; i <= N; i++) cin >> A[i]; // 再帰呼び出し → 答えの出力 int Answer = solve(1, N + 1); cout << Answer << endl; return 0; }
dameo

2023/01/10 16:11

すみませんが、流石にこれ以上どう説明したらいいのか分かりません。 初めて再帰を見ると戸惑うのは分からなくもないのですが、ロジックを順に追うだけで簡単に理解できる話かとおもいます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問