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

C++で連結リストの交互ノードの合計を求める方法(反復法・再帰法)

問題概要

この記事では、連結リスト(リンクリスト)が与えられたときに、その交互ノード(0、2、4…番目のノード)の値の合計を求める方法を解説します。

連結リストとは、リンク(ポインタ)によって順次接続されたデータ構造の列です。各ノードはデータ本体と、次のノードを指す参照を持っています。

今回の課題は、連結リストのうち位置 0、2、4、6 … にあるノード、つまり先頭から1つおきのノードの値をすべて加算することです。

入出力例

入力:

4 → 12 → 10 → 76 → 9 → 26 → 1

出力:

24

説明:

交互ノードを取り出すと −
4 + 10 + 9 + 1 = 24

解決の考え方

この問題は、リストを先頭から順に走査しながら、加算対象のノードに出会ったらその値を合計に足していくことで解けます。「現在のノードが加算対象かどうか」を判定するために、bool型のフラグ変数を使用します。

実装方法には反復(ループ)による方法と再帰による方法の2通りがあります。以下、それぞれのコード例を見ていきましょう。

方法1:反復法(イテレーション)

whileループでリストをたどり、フラグがtrueのときだけノードの値を加算します。ノードを1つ進めるごとにフラグを反転させれば、自然に1つおきのノードだけが合計されます。

#include <iostream>
using namespace std;
struct Node {
    int data;
    struct Node* next;
};
void pushNode(struct Node** head_ref, int newData) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->data = newData;
    newNode->next = (*head_ref);
    (*head_ref) = newNode;
}
int sumAlternateNodeIt(struct Node* head) {
    bool flag = true;
    int sum = 0;
    while (head != NULL){
        if (flag)
            sum += head->data;
        flag = !flag;
        head = head->next;
    }
    return sum;
}
int main(){
    struct Node* head = NULL;
    pushNode(&head, 54);
    pushNode(&head, 12);
    pushNode(&head, 87);
    pushNode(&head, 1);
    pushNode(&head, 99);
    pushNode(&head, 11);
    cout<<"交互ノードの合計は "<<sumAlternateNodeIt(head);
    return 0;
}

出力:

交互ノードの合計は 24

なお、pushNode関数は先頭への挿入を行うため、上記のコードではリストは 11 → 99 → 1 → 87 → 12 → 54 の順に構成されます。交互ノードは 11 + 1 + 12 = 24 となり、出力結果と一致します。

方法2:再帰法(リカージョン)

再帰版では、関数の引数に「合計値への参照」と「フラグ」を渡します。ノードがNULL(リストの終端)に達した時点で再帰を終了し、フラグがtrueのノードの値だけを加算していきます。

#include <iostream>
using namespace std;
struct Node {
    int data;
    struct Node* next;
};
void pushNode(struct Node** head_ref, int new_data){
    struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
    new_node->data = new_data;
    new_node->next = (*head_ref);
    (*head_ref) = new_node;
}
void sumAlternateNodeRec(struct Node* node, int& sum, bool flag = true){
    if (node == NULL)
        return;
    if (flag == true)
    sum += (node->data);
    sumAlternateNodeRec(node->next, sum, !flag);
}
int main(){
    struct Node* head = NULL;
    pushNode(&head, 54);
    pushNode(&head, 12);
    pushNode(&head, 87);
    pushNode(&head, 1);
    pushNode(&head, 99);
    pushNode(&head, 11);
    int sum = 0;
    sumAlternateNodeRec(head, sum, true);
    cout<<"交互ノードの合計は "<<sum;
    return 0;
}

出力:

交互ノードの合計は 24

計算量と使い分けのポイント

どちらの方法でも、リスト内の全ノードを一度ずつ訪問するため、時間計算量は O(n) です。一方、空間計算量は反復法では追加メモリが不要なため O(1)、再帰法では呼び出しスタックがリストの長さ分必要になるため O(n) となります。

非常に長い連結リストを扱う場合、再帰法ではスタックオーバーフローのリスクがあるため、実務では反復法を選ぶのが安全です。コードの簡潔さを重視する場面や、学習目的で再帰の動きを理解したい場合には、再帰法も有効な選択肢となります。

  1. C++で循環リンクリストのノード数をカウントする方法

    ノードから構成される循環リンクリスト(Circular Linked List)が与えられ、そのリスト内に存在するノードの総数を求めるのが課題です。 循環リンクリストとは、連結リストの一種であり、最初の要素が最後の要素を指し、最後の要素が最初の要素を指すという特徴を持つデータ構造です。片方向リンクリスト(Singly Linked List)でも双方向リンクリスト(Doubly Linked List)でも、この循環リンクリストとして実装することが可能です。 以下のプログラムでは、片方向リンクリストを循環リンクリストとして実装し、その中に含まれるノード数をカウントする方法を紹介します。 具体

  2. 【C++】再帰を使ってリンクリストの交互ノードを出力する方法

    リンクリスト(連結リスト)とはリンクリストは、各要素(ノード)をメモリ上の連続しない領域に格納できる線形データ構造です。各ノードにはデータ本体と、次のノードを指すポインタが含まれており、ポインタをつなぐことで一連のリストとして扱うことができます。問題の概要今回は、与えられたリンクリストを走査し、交互(ひとつおき)のノードだけを出力するプログラムを作成します。具体的には、1番目・3番目・5番目…というように、奇数番目の要素のみを順に出力していきます。入出力例入力 : 2 -> 4 -> 1 -> 67 -> 48 -> 90 出力 : 2 -> 1 ->