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

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

リンクリスト(連結リスト)とは

リンクリストは、各要素(ノード)をメモリ上の連続しない領域に格納できる線形データ構造です。各ノードにはデータ本体と、次のノードを指すポインタが含まれており、ポインタをつなぐことで一連のリストとして扱うことができます。

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

問題の概要

今回は、与えられたリンクリストを走査し、交互(ひとつおき)のノードだけを出力するプログラムを作成します。具体的には、1番目・3番目・5番目…というように、奇数番目の要素のみを順に出力していきます。

入出力例

入力 : 2 -> 4 -> 1 -> 67 -> 48 -> 90
出力 : 2 -> 1 -> 48

解説:先頭から数えて1番目・3番目・5番目の要素である「2」「1」「48」が出力されています。

フラグ変数を使った基本的なアプローチ

もっともシンプルな方法は、初期値が0のフラグ変数を用意し、ノードをたどりながら値を出力するかどうかを切り替えるやり方です。

  • フラグが0のとき → ノードの値を出力し、フラグを1に変更する
  • フラグが1のとき → 値は出力せず、フラグを0に戻す

この操作をリストの終端まで繰り返すことで、ひとつおきにノードの値が表示されます。

サンプルコード(反復処理版)

#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;
    struct Node* next;
};

void printAlternateNode(struct Node* head){
    int flag = 0;
    while (head != NULL) {
        if (flag == 0){
            printf(" %d ", head->data);
            flag = 1;
        }
        else
            flag = 0;
        head = head->next;
    }
}

void insertNode(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;
}

int main(){
    struct Node* head = NULL;
    insertNode(&head, 23);
    insertNode(&head, 4);
    insertNode(&head, 98);
    insertNode(&head, 5);
    insertNode(&head, 71);
    printAlternateNode(head);
    return 0;
}

出力結果

71  98  23

このプログラムでは、insertNode 関数が新しいノードを常にリストの先頭に挿入するため、挿入時の順序(23 → 4 → 98 → 5 → 71)と逆向きの並びになり、結果として「71 98 23」が出力されます。

再帰を使った実装

同じ問題は再帰を使っても解けます。現在のノードの値を出力した後、「次の次」のノードに対して自分自身を呼び出すことで、交互の出力を自然に実現できます。ここではC++らしい書き方として、フラグを引数で渡す方式を採用しました。

C++による再帰版コード

#include <iostream>
using namespace std;

struct Node {
    int data;
    Node* next;
};

// 交互ノードを再帰的に出力する関数
void printAlternateNode(Node* head, bool flag = true){
    if (head == NULL)
        return;
    if (flag)
        cout << head->data << " ";
    printAlternateNode(head->next, !flag);
}

void insertNode(Node** head_ref, int new_data){
    Node* new_node = new Node();
    new_node->data = new_data;
    new_node->next = (*head_ref);
    (*head_ref) = new_node;
}

int main(){
    Node* head = NULL;
    insertNode(&head, 23);
    insertNode(&head, 4);
    insertNode(&head, 98);
    insertNode(&head, 5);
    insertNode(&head, 71);
    printAlternateNode(head); // 出力:71 98 23
    return 0;
}

再帰版では、呼び出しごとにフラグ !flag を渡して真偽を反転させるため、静的変数やループなしで交互出力が実現できます。ロジックが非常に簡潔になるのが大きなメリットです。

計算量について

  • 時間計算量:O(n) ― リンクリストを一度だけ走査すればよいため、どちらの実装でも同様です。
  • 空間計算量:反復版はO(1)。一方、再帰版は呼び出しごとにスタックフレームが必要なため、最大でO(n)のメモリを消費します。

まとめ

リンクリストの交互ノードの出力は、フラグ変数による反復処理でも再帰でも簡単に実装できます。反復版は余分なメモリを使わず大規模なリストにも安全で、再帰版はコードが簡潔で読みやすいのが魅力です。リストのサイズや要件に応じて、適切な実装を選択するとよいでしょう。

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

    問題概要 この記事では、連結リスト(リンクリスト)が与えられたときに、その交互ノード(0、2、4…番目のノード)の値の合計を求める方法を解説します。 連結リストとは、リンク(ポインタ)によって順次接続されたデータ構造の列です。各ノードはデータ本体と、次のノードを指す参照を持っています。 今回の課題は、連結リストのうち位置 0、2、4、6 … にあるノード、つまり先頭から1つおきのノードの値をすべて加算することです。 入出力例 入力: 4 → 12 → 10 → 76 → 9 → 26 → 1 出力: 24 説明: 交互ノードを取り出すと − 4 + 10 + 9 + 1 = 24 解決の考

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

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