【C言語】双方向リンクリストで任意の位置にノードを挿入する方法
リンクリストとは
リンクリストは動的なメモリ確保(動的メモリ割り当て)を利用するデータ構造で、複数のノードがつながった集合体です。
各ノードは「データ部」と「リンク部(ポインタ)」という2つの要素で構成されており、ポインタによって次のノード(双方向リンクリストの場合は前のノードも)と接続されています。
リンクリストの種類
C言語で扱われる主なリンクリストには、以下の4種類があります。
- 単方向リンクリスト(Singly Linked List)
- 双方向リンクリスト(Doubly Linked List)
- 循環単方向リンクリスト(Circular Singly Linked List)
- 循環双方向リンクリスト(Circular Doubly Linked List)
双方向リンクリストの構造
双方向リンクリストでは、各ノードが前のノードへのポインタ(preptr)と次のノードへのポインタ(nextptr)の両方を持っています。そのため、リストを前方向・後方向のどちらにもたどれるのが特徴です。以下の図は、双方向リンクリストの構造イメージです。

サンプルプログラム
以下は、双方向リンクリストを使用して任意の位置にノードを挿入するCプログラムの完全なコード例です。
#include <stdio.h>
#include <stdlib.h>
struct node {
int num;
struct node * preptr;
struct node * nextptr;
}*stnode, *ennode;
void DlListcreation(int n);
void DlLinsertNodeAtBeginning(int num);
void DlLinsertNodeAtEnd(int num);
void DlLinsertNodeAtAny(int num, int pos);
void displayDlList(int a);
int main(){
int n,num1,a,insPlc;
stnode = NULL;
ennode = NULL;
printf("\n\n Doubly Linked List : Insert a node at any position :\n");
printf("-----------------------------------------------------------------------------------\n");
printf(" Input the number of nodes : ");
scanf("%d", &n);
DlListcreation(n);
a=1;
displayDlList(a);
printf(" Input the position ( 1 to %d ) to insert a new node : ",n+1);
scanf("%d", &insPlc);
printf(" Input data for the position %d : ", insPlc);
scanf("%d", &num1);
DlLinsertNodeAtAny(num1,insPlc);
a=2;
displayDlList(a);
return 0;
}
/* リンクリストの作成 */
void DlListcreation(int n){
int i, num;
struct node *fnNode;
if(n >= 1){
stnode = (struct node *)malloc(sizeof(struct node));
if(stnode != NULL){
printf(" Input data for node 1 : "); /* 最初のノードにデータを代入 */
scanf("%d", &num);
stnode->num = num;
stnode->preptr = NULL;
stnode->nextptr = NULL;
ennode = stnode;
for(i=2; i<=n; i++){
fnNode = (struct node *)malloc(sizeof(struct node));
if(fnNode != NULL){
printf(" Input data for node %d : ", i);
scanf("%d", &num);
fnNode->num = num;
fnNode->preptr = ennode;
fnNode->nextptr = NULL;
ennode->nextptr = fnNode;
ennode = fnNode;
}
else{
printf(" Memory can not be allocated.");
break;
}
}
}
else{
printf(" Memory can not be allocated.");
}
}
}
/* 任意の位置にノードを挿入 */
void DlLinsertNodeAtAny(int num, int pos){
int i;
struct node * newnode, *tmp;
if(ennode == NULL){
printf(" No data found in the list!\n");
}
else{
tmp = stnode;
i=1;
while(i<pos-1 && tmp!=NULL){
tmp = tmp->nextptr;
i++;
}
if(pos == 1){
DlLinsertNodeAtBeginning(num); /* 先頭への挿入 */
}
else if(tmp == ennode){
DlLinsertNodeAtEnd(num); /* 末尾への挿入 */
}
else if(tmp!=NULL){
newnode = (struct node *)malloc(sizeof(struct node));
newnode->num = num;
newnode->nextptr = tmp->nextptr;
newnode->preptr = tmp;
if(tmp->nextptr != NULL){
tmp->nextptr->preptr = newnode; /* n+1番目のノードを新ノードに接続 */
}
tmp->nextptr = newnode; /* n-1番目のノードを新ノードに接続 */
}
else{
printf(" The position you entered, is invalid.\n");
}
}
}
/* 先頭にノードを挿入 */
void DlLinsertNodeAtBeginning(int num){
struct node * newnode;
if(stnode == NULL){
printf(" No data found in the list!\n");
}
else{
newnode = (struct node *)malloc(sizeof(struct node));
newnode->num = num;
newnode->nextptr = stnode;
newnode->preptr = NULL;
stnode->preptr = newnode;
stnode = newnode;
}
}
/* 末尾にノードを挿入 */
void DlLinsertNodeAtEnd(int num){
struct node * newnode;
if(ennode == NULL){
printf(" No data found in the list!\n");
}
else{
newnode = (struct node *)malloc(sizeof(struct node));
newnode->num = num;
newnode->nextptr = NULL;
newnode->preptr = ennode;
ennode->nextptr = newnode;
ennode = newnode;
}
}
/* リンクリストの表示 */
void displayDlList(int m){
struct node * tmp;
int n = 1;
if(stnode == NULL) {
printf(" No data found in the List yet.");
}
else{
tmp = stnode;
if (m==1) {
printf("\n Data entered in the list are :\n");
}
else{
printf("\n After insertion the new list are :\n");
}
while(tmp != NULL){
printf(" node %d : %d\n", n, tmp->num);
n++;
tmp = tmp->nextptr; /* 現在のポインタを次のノードへ移動 */
}
}
}挿入処理のポイント
このプログラムの中核となるのは DlLinsertNodeAtAny() 関数です。指定された位置に応じて、処理が次のように分岐します。
- 位置が1の場合: 先頭への挿入関数
DlLinsertNodeAtBeginning()を呼び出します。 - 位置が末尾の場合: 末尾への挿入関数
DlLinsertNodeAtEnd()を呼び出します。 - 中間の位置の場合: 指定位置の直前のノードまでポインタを進め、新しいノードの前後のポインタを付け替えて挿入します。
また、無効な位置が入力された場合にはエラーメッセージを表示するようになっている点も、実用的なポイントです。
実行結果
上記のプログラムをコンパイルして実行すると、以下のような出力が得られます。
Doubly Linked List : Insert node at any position: ----------------------------------------------------------------------------------- Input the number of nodes : 5 Input data for node 1 : 23 Input data for node 2 : 12 Input data for node 3 : 11 Input data for node 4 : 34 Input data for node 5 : 10 Data entered in the list are : node 1 : 23 node 2 : 12 node 3 : 11 node 4 : 34 node 5 : 10 Input the position ( 1 to 6 ) to insert a new node : 5 Input data for the position 5 : 78 After insertion the new list are : node 1 : 23 node 2 : 12 node 3 : 11 node 4 : 34 node 5 : 78 node 6 : 10
この実行例では、5つのノードからなるリストの5番目の位置にデータ「78」を挿入しています。挿入後は元の5番目だった「10」が6番目に移動し、リスト全体が正しく更新されていることが確認できます。
-
C言語で連結リストを使った優先度付きキューの実装方法
本記事では、整数値の「データ」と「優先度」が与えられたとき、指定された優先度に従って連結リスト(リンクリスト)を構築し、結果を表示する方法を解説します。 優先度付きキューとは キューはFIFO(First In, First Out:先入れ先出し)方式のデータ構造であり、最初に挿入された要素が最初に取り除かれます。 一方、優先度付きキュー(プライオリティキュー)は、要素の挿入・削除を「優先度」に基づいて行えるキューの一種です。キュー、スタック、連結リストなどのデータ構造を用いて実装でき、以下のルールに従って動作します。 優先度が最も高いデータ(要素)は、優先度が低いものよりも先に処理される。
-
C言語で連結リストの末尾からn番目のノードを取得するプログラム
n個のノードからなる連結リストが与えられたとき、その末尾からn番目のノードを出力するのが本記事の目的です。プログラムはリスト内のノードの並び順を変更してはならず、あくまで末尾から数えてn番目に位置するノードの値を表示するだけでなければなりません。具体例入力 -: 10 20 30 40 50 60 N = 3 出力 -: 40上記の例では、先頭ノードから順に「count − n」個目までのノード(10, 20, 30, 40, 50, 60)を走査し、末尾から3番目のノードとして 40 が得られます。効率的なアプローチリスト全体を最後まで走査しなくても、以下の手順で目的のノードを見つけられ