C++で昇順ソート済み循環リンクリストへ値を挿入する方法
ここでは、昇順にソートされた循環リンクリストのノードが1つ与えられたとき、値 insertVal を適切な位置に挿入し、挿入後もソートされた状態を保つ関数を実装する方法を解説します。
問題の概要
引数として渡されるノードは、リスト内の任意の1ノードへの参照であり、必ずしも先頭ノードとは限りません。挿入に適した位置が複数存在する場合は、どこに挿入しても構いません。
また、以下のようなケースにも対応する必要があります。
- リストが空の場合:新しい単一ノードからなる循環リンクリストを作成し、そのノードへの参照を返す
- リストが空でない場合:元のノードをそのまま返す
例として、head = [3,4,1]、insertVal = 2 が与えられた場合、出力は [3,4,1,2] となります。
アルゴリズムの手順
この問題は、次の手順で解くことができます。
headが null(空リスト)の場合:valを持つ新しいノードをheadとするhead->next = headと設定し、自己参照の循環リストを作る
- それ以外の場合:
curr = head->next、prev = headと初期化するtempとしてvalを持つ新しいノードを生成し、フラグdone = falseを用意する- 無限ループの中で以下を判定する:
- 通常の挿入ケース:
curr->val >= valかつprev->val <= valのときは、prevとcurrの間にtempを挿入し、done = trueとしてループを抜ける - 境界をまたぐケース:
prev->val > curr->val(最大値と最小値の境目)のとき、prev->val <= valまたはval <= curr->valであれば同様に挿入してループを抜ける curr == headとなったら一周したのでループを抜ける- それ以外は
prev = curr、curr = curr->nextとしてポインタを進める
- 通常の挿入ケース:
- ループ後も
done == falseの場合(全ノードが同一の値など)は、headの直前にtempを挿入する
headを返す
C++による実装例
以下の実装を見ると、より理解が深まります。
#include <bits/stdc++.h>
using namespace std;
class Node {
public:
int val;
Node* next;
Node() {}
Node(int _val) {
val = _val;
next = NULL;
}
Node(int _val, Node* _next) {
val = _val;
next = _next;
}
};
class Solution {
public:
Node* insert(Node* head, int val) {
if(!head){
head = new Node(val);
head->next = head;
}
else{
Node* curr = head->next;
Node* prev = head;
Node* temp = new Node(val);
bool done = false;
while(1){
if (curr->val >= val && prev->val <= val) {
prev->next = temp;
temp->next = curr;
done = true;
break;
}
if (prev->val > curr->val) {
if (prev->val <= val || val <= curr->val) {
prev->next = temp;
temp->next = curr;
done = true;
break;
}
}
if (curr == head)
break;
prev = curr;
curr = curr->next;
}
if(!done){
temp->next = head;
prev->next = temp;
head = temp;
}
}
return head;
}
};
main(){
Solution ob;
Node *head = new Node(3);
head->next = new Node(4);
head->next->next = new Node(1, head);
ob.insert(head, 2);
Node *temp = head;
if (head != NULL){
do{
cout << temp->val << " ";
temp = temp->next;
}
while (temp != head);
}
}入力例
node *head = new Node(3); head->next = new Node(4); head->next->next = new Node(1, head); insertVal = 2
出力結果
3 4 1 2
まとめ
このアルゴリズムでは、リストを一周しながら「挿入値が前後ノードの間に収まる位置」または「最大値と最小値の境界をまたぐ位置」を探索します。計算量はノード数を N とすると O(N)、必要な追加メモリは新ノード1つ分のみで O(1) です。空リストや全ノードが同値といったエッジケースも正しく処理できる点がポイントです。
-
【C++】循環リンクリストのノード値の合計を求める方法
この記事では、循環リンクリスト(Circular Linked List)が与えられたときに、すべてのノードの値の合計を求めるプログラムをC++で作成する方法を解説します。 やるべきことはシンプルで、リンクリストを構成する全ノードの値を順番に読み取り、それらを加算していくだけです。 前提知識:重要な定義 リンクリストとは リンクリスト(連結リスト)とは、各データ(ノード)をポインタによるリンクで相互に接続したデータ構造の列です。配列と異なり、メモリ上の連続した領域を必要とせず、動的な挿入や削除に強いという特徴があります。 循環リンクリストとは 循環リンクリストはリンクリストの変形の一種で、先
-
C++で循環リンクリストのノード数をカウントする方法
ノードから構成される循環リンクリスト(Circular Linked List)が与えられ、そのリスト内に存在するノードの総数を求めるのが課題です。 循環リンクリストとは、連結リストの一種であり、最初の要素が最後の要素を指し、最後の要素が最初の要素を指すという特徴を持つデータ構造です。片方向リンクリスト(Singly Linked List)でも双方向リンクリスト(Doubly Linked List)でも、この循環リンクリストとして実装することが可能です。 以下のプログラムでは、片方向リンクリストを循環リンクリストとして実装し、その中に含まれるノード数をカウントする方法を紹介します。 具体