出典書籍:
データ構造 (C 言語編) (第 2 版・マイクロコース編) Qin Feng、Tang Yaling、編集長、清華大学出版局、2021 年 12 月 書籍リンク: (http:
//www.tup.tsinghua
. edu.cn/booksCenter /book_09444301.html#)
1. リニアテーブル
1.1 単一リンクリストの基本操作の実装
1.1.1 空の単一リンクリストを作成する
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include<string.h>
#define MAXSIZE 100
このコードには、標準入出力ライブラリのヘッダー ファイル stdio.h、標準ライブラリのヘッダー ファイル stdlib.h、メモリ割り当て関連ライブラリのヘッダー ファイル malloc.h、およびメモリ割り当て関連ライブラリのヘッダー ファイル string.h が含まれています。文字列処理ライブラリ。最後の行は、MAXSIZE を 100 として定義するマクロ定義です。MAXSIZE は、後続のプログラムで 100 の代わりに使用できます。そうすることで、プログラムの変更や保守が容易になり、コードの可読性や保守性が向上します。
typedef int DataType;
typedef struct node{
DataType data; /*每个元素数据信息*/
struct node *next; /*存放后继元素的地址*/
} LNode,*LinkList;
このコードは、単一のリンク リスト構造を定義します。
DataType は、リンク リスト内の各要素のデータ情報を格納するために使用される int 型として定義されます。
struct ノードは、リンク リストのノードを表します。これには、ノードのデータ情報を格納するために使用される DataType タイプのデータ メンバーと、ノードのアドレスを格納するために使用される struct ノード タイプのネクスト ポインタが含まれます。次のノード。
LNode は struct ノード タイプのエイリアスであり、後続のコード記述に便利です。
LinkList は、リンク リストの操作を容易にするために、リンク リストの先頭ポインタを格納するために使用される、構造体ノード タイプのポインタ エイリアスです。
このコードは、DataType data と struct node *next という 2 つのメンバー変数を含むリンク リスト構造を定義します。DataType は要素のデータ型を表す型定義であり、必要に応じて変更できます。LNode はリンク リスト ノードを表し、データ メンバー変数にはノード データ が格納され、次のメンバー変数には次のノードへのポインターが格納されます。LinkList は、リンク リストのヘッド ノードを表すために使用される LNode を指すポインター タイプです。
//创建空单链表
LinkList Creat_LinkList( )
{
/*创建空单链表,入口参数:无;返回值:单链表的头指针,0代表创建失败,非0表成功*/
LinkList H;
H=(LinkList )malloc(sizeof(LNode));
if (H) /*确认创建头结点创建是否成功,若成功,修改单链表头结点的指针域为0表空表*/
H->next=NULL;
return H;
}
このコードは、空の単一リンク リストを作成する関数を実装します。関数名はCreat_LinkList、戻り値はLinkList (単一リンク リストの先頭ポインタ)です。作成に失敗した場合は 0 を返します。この関数では、まずLinkList 型の先頭ポインタ H
を定義し、次にmalloc 関数を使用して先頭ノードにメモリ空間を動的に割り当て、割り当てが成功したかどうかを判定します。割り当てが成功すると、ヘッド ノードの次のポインタ フィールドがNULL に設定され、単一リンク リストが空のリストであることを示します。最後にヘッドポインタHを返します。
H=(LinkList )malloc(sizeof(LNode));
このコード行は、動的メモリ内の LNode 構造体のスペースを割り当て、そのアドレスをポインタ変数 H に割り当てます。その役割は、リンクされたリストのヘッド ノードを作成することです。malloc 関数を呼び出すことでメモリ空間が確保されますが、このメモリ空間のサイズがsizeof(LNode) で計算されるLNode 構造体のサイズとなります。そして、確保された空間のアドレスが強制的にLinkList型に変換され、ポインタ変数Hに代入され、リンクリストの先頭ポインタとなります。
1.1.2 単一リンクリストの破棄
//销毁单链表
void Destroy_LinkList(LinkList *H)
{
/*销毁单链表,入口参数:单链表头指针的地址*/
LinkList p,q;
p= *H;
while ( p) /*释放单链表的所有结点*/
{
q=p;
p=p->next;
free(q);
} /*while */
*H=NULL;
}
このコードは、単結合リストの破棄操作であり、単結合リスト内のすべてのノードのメモリ空間を解放するために使用されます。
このうち、エントリパラメータは、単一リンクリストの先頭ポインタ(つまり、先頭ノードポインタを指すポインタ)のアドレスであり、ポインタのポインタは、先頭ポインタの値を変更して設定するために使用されます。それをNULLにします。
関数では、2 つのポインター p と q を定義します。 p は現在解放する必要があるノードを指し、 q は p の前のノードを指します。単一リンクされたリスト全体をループし、 p が null ポインターを指すまで各ノードのメモリ空間を順番に解放します。ループが終了すると、ヘッド ポインタの値は NULL に設定され、単一リンク リストが破棄されたことを示します。
ノードのメモリ空間を解放する前に、解放後に次のノードにアクセスできなくなる事態を避けるために、p を次のノードにポイントする必要があることに注意してください。
質問: p は現在解放する必要があるノードを指し、q は p の前のノードを指すのはなぜですか?
単一リンク リストを破棄するプロセスでは、リンク リスト内の各ノードを順番に解放する必要があります。現在のノードが解放されると、次のノードへのポインタを保持する必要があります。これは、現在のノードが解放された後、次のノードが現在のノードになります。したがって、ループ内で、p ポインタを使用して現在解放する必要があるノードを指し、q を使用して p の前のノードを指します。これにより、p を解放した後、次のノードを指すように q のポインタ フィールドが変更されます。リンク リストの整合性を維持しながらノードを削除します。
1.1.3 テーブルの長さを求める
//求表长
int Length_LinkList (LinkList H)
{
/*求单链表表长,入口参数:单链表头指针,出口参数:表长,-1表示单链表不存在。*/
LinkList p=H; /* p指向头结点*/
int count= -1; /*H带头结点所以从-1开始*/
while ( p) /* p所指的是第 count + 1 个结点*/
{
p=p->next;
count++;
} /*while */
return (count);
}
このコードは、単一リンク リストの長さを調べるもので、関数名はLength_LinkListです。入力パラメータは、単一リンク リストの先頭ポインタ Hであり、単一リンク リストの長さを返します。この関数では、最初にポインタ変数 p がヘッド ノードを指すように定義され、次にカウンタ カウントが定義されます。単一リンク リストのヘッド ノードはテーブル長にカウントされないため、初期値は -1 です。次に、ループを使用して単一リンク リストを走査します。ノードが走査されるたびに、走査が終了するまでカウンタ カウントが 1 ずつ増加します。最後に、カウンタ カウントに返される値は、単一リンク リストの長さになります。
なお、単連結リストが空の場合、 count の初期値は -1、このときに返される length 値は -1 となり、単連結リストが存在しないことを示します。
1.1.4 検索操作
シリアル番号で検索する
查找第i个数据元素
LinkList Locate_LinkList_Pos( LinkList H, int i)
{
/*i不正确或者链表不存在返回NULL,i==0返回头指针,否则返回第 i 结点的指针*/
LinkList p;
int j;
p=H; j=0;
while (p && j<i ) /*查找第i个结点*/
{
p=p->next;
j++;
} /*while*/
if(j==i)
{
return (p);
}
else{
printf("参数i错或单链表不存在\n");
return (NULL);
}
}
このコードは、単連結リスト内の i 番目のデータ要素の検索を実装します。ここで、 i は検索位置を表し、H は単連結リストの先頭ポインターです。i が 0 以下の場合、または単一リンクされたリストが存在しない場合は、NULL を返します。i が 0 より大きい場合、ヘッド ノードから単一リンク リストをたどって、i 番目のノードを見つけて、そのノードのポインタを返します。i が単一リンクリストの長さより大きい場合は、NULL を返します。
while (p && j<i ) /*查找第i个结点*/
{
p=p->next;
j++;
}
このコードは、単一リンク リストの i 番目のデータ要素が配置されているノードのポインターを見つけるために使用されます (i は入力パラメーター)。p ポインタは、最初は単一リンク リストのヘッド ノードを指します。j は 0 で、その後、リンク リストを継続的に走査します。ノードを走査するたびに、j が i に等しくなるか、j が i に到達するまで、j は 1 ずつ増分されます。リンクされたリストの終わり (p は NULL)。j が i に等しい場合、p は i 番目のデータ要素が配置されているノードのポインタを指し、そのポインタを返します。それ以外の場合は NULL を返します。
p && j<i
p && j<i は while ループの条件の 1 つです。この条件は、リンク リストが空ではなく、i 番目のノードに到達していない場合に、リンク リストの走査を続けることを意味します。リンクされたリストが空になるか、i 番目のノードに到達すると、ループは終了します。
Q: p は空のヘッド ノードですか?
p はデータ ノードではなくヘッド ノードであるため、データは保存されませんが、その次のポインタは単一リンク リストの最初のデータ ノードを指す可能性があるため、単一リンク リストをトラバースするときにヘッド ノードをスキップする必要があります。次にヘッド ノードからトラバースを開始します。このループでは、p の初期値がヘッド ノードです。p が NULL ではなく (つまり、単一リンク リストが空ではない)、現在通過しているノードの位置が検索対象の位置 i より小さい場合、続行します。最初のノード i 個が見つかるまで逆方向にトラバースするか、単一リンク リストの最後までトラバースします。
値による検索
//按值查找
LinkList Locate_LinkList_Value( LinkList H, DataType x)
{
/*在单链表中查找值为x的结点,入口参数:单链表指针,检索元素*/
/*出口参数:找到后返回其指针,否则返回NULL*/
LinkList p=H->next;
while ( p && p->data != x)
p=p->next;
return (p);
}
この関数が実装するのは、単一リンク リスト内の値によって要素のノードを検索し、そのノードのポインタを返すことです。このうち、エントリパラメータには単連結リストの先頭ポインタHと検索対象の要素xが含まれ、戻り値は見つかった要素のノードポインタ、見つからない場合はNULLとなります。
関数の具体的な実装では、単一リンク リストの最初の要素から順番に各要素を逆方向にトラバースします。要素が検索対象の要素 x と等しくない場合は、単一リンク リストの最後の要素まで逆方向にトラバースし続けます。リンクされたリストを走査するか、探していた要素 x を見つけます。要素が見つかった場合は、要素が存在するノードへのポインタを返し、それ以外の場合は NULL を返します。
1.1.5 挿入
//插入
int Insert_LinkList( LinkList H, int i, DataType x)
{
/*在单链表H的第i个位置前插入值为x的结点,入口参数:单链表,插入位置,插入元素*/
/*返回参数:成功标志,0不成功,1成功*/
LinkList p, q;
p= Locate_LinkList_Pos ( H, i-1); /*找第i-1个结点地址,见算法2.11 */
if (!p)
{
printf("i有误\n");
return (0);
}
q=(LinkList) malloc(sizeof(LNode));
if (!q)
{
printf("申请空间失败\n");
return (0);
} /*申请空间失败,不能插入*/
q->data=x;
q->next=p->next; /*新结点插入在第i-1个结点的后面*/
p->next=q;
return 1; /*插入成功,则返回*/
}
このコードは、単一リンク リストの i 番目の位置の前に値 x を持つノードを挿入することを実装します。具体的な実装は次のとおりです:
Locate_LinkList_Pos 関数を呼び出して、i-1 番目のノードのアドレス p を見つけます。i が間違っている場合は、 return 0 失敗を示します。
新しいノード q を割り当て、 q のデータ フィールドに x を格納します。
q を i-1 番目のノードの後ろに挿入します。つまり、q の次は p の次を指し、 p の次は q を指します。
挿入が成功した場合は 1 を返します。
ここで重要なのは、Locate_LinkList_Pos 関数を呼び出して i-1 番目のノードのアドレスを見つけることです。この関数の実装は前のコードで示されています。
q->data=x;
q->next=p->next; /*新结点插入在第i-1个结点的后面*/
p->next=q;
このコードでは、まず新しいノード q を作成し、そのデータ フィールドに x を割り当てます。次に、q の次のポインタを p の次のノード、つまり i 番目のノードにポイントし、i -1 番目のノードと i 番目のノードがそれぞれ新しいノード q の前後に接続されるようにします。 。最後に、p の次のポインターを q にポイントし、新しいノード q を i 番目の位置の前に挿入する操作を完了します。
1.1.6 削除
//删除
int Del_LinkList(LinkList H,int i)
{
/*删除单链表H上的第i个结点,入口参数:单链表,删除元素序号,返回参数:成功标志,
0不成功,1成功*/
LinkList p, q;
if (H==NULL||H->next==NULL)
{
printf("链表不存在或者空表不能删除\n");
return (0);
}
p= Locate_LinkList_Pos( H, i-1); /*找第i-1个结点地址,见算法2.11*/
if (p==NULL||p->next==NULL)
{
printf("参数 i 错\n");
return (0); /*第i个结点不存在*/
}
q=p->next; /*q指向第i个结点*/
p->next=q->next; /*从链表中删除*/
free(q); /*释放第i个结点空间 */
return (1);
}
これは、単連結リスト の削除操作関数です。その機能は、連結リストの i 番目のノードを削除することです。入力パラメータは、単連結リストの先頭ノードのポインタ H と、単連結リストの通し番号 i です。戻り値が 1 の場合は削除成功、0 の場合は削除失敗を意味します。この関数では、まず単一リンク リストが空のリストかどうかを判断し、空のリストである場合は、削除が失敗したことを示す 0 を返します。次に、単一リンク リストで i-1 番目のノードを検索し、見つかった場合は、i-1 番目のノードの次のポインタを i+1 番目のノードにポイントします。つまり、i 番目のノードをスキップして、リンク リストから削除します。 。最後に、i 番目のノードが占めていたスペースが解放され、削除が成功したことを示す 1 が返されます。
if (H==NULL||H->next==NULL)
{
printf("链表不存在或者空表不能删除\n");
return (0);
}
このコードは、リンクリストが存在するか空リストであるかを判定し、存在する場合は「リンクリストが存在しないか、空リストを削除できません」というプロンプトメッセージを出力し、削除に失敗したことを示す0を返します。リンクされたリストが存在しないか空のリストであるため、削除するノードがまったくありません。
H==NULL||H->next==NULL
このコードは、単一リンクされたリストが空かどうかを判断するために使用され、空のリストの場合は削除できません。判定方法は、単結合リストの先頭ノードのポインタHとその次のポインタがNULLかどうかで判定する。
具体的には、H ポインタが NULL の場合、単連結リスト自体が存在しないことを意味します。H- >next ポインタが NULL の場合、単連結リストにはヘッド ノードが 1 つだけあり、他のノードがないことを意味します。、単一リンクされたリストは空のリストです。単一リンクリストが空リストの場合、削除操作は実行できず、削除が失敗したことを示す 0 が直接返されます。
p==NULL||p->next==NULL
このコード行は、指定された位置 i が存在するかどうかを確認するために使用されます。指定された位置 i が存在しない場合、削除操作は実行されません。このコード行は、削除失敗のフラグ、つまり 0 を返します。
具体的には、このコード行には 2 つの判定条件があります。
p == NULL: i - 1 番目のノードが見つかったが、そのノードのポインタが NULL の場合、リンク リストの長さが i - 1 未満であることを意味します。つまり、指定された位置 i は存在しません。
p->next == NULL: i - 1 番目のノードが見つかったが、このノードの次のノード ポインタが NULL の場合、指定された位置 i のノードが存在しないことを意味します。つまり、指定された位置 i は存在します。存在しない。
上記 2 つの条件のどちらも満たされない場合は、指定された位置 i が存在し、削除できることを意味します。
q=p->next; /*q指向第i个结点*/
p->next=q->next; /*从链表中删除*/
free(q); /*释放第i个结点空间 */
このコードは、単一リンクリストHの i 番目の要素を削除する操作を実装します。まず、ポインタ p を使用して i-1 番目のノードのアドレスを見つけ、次にポインタ q を使用して i 番目のノードを指し、i-1 番目のノードの次のノードを i+1 番目のノードを指します。最初の i ノード。最後に、i 番目のノードのメモリ空間を解放します。
1.1.7 単一リンクリストの走査
//单链表的遍历
void DispList(LinkList L) /*输出线性表*/
{
LinkList p;
int i=1;
p=L->next;
printf(" NO DATA \n");
while(p)
{
printf(" %d %d",i,p->data);
printf("\n");
i++;
p=p->next;
}
printf("\n");
printf("目前表中元素总数:%d\n",i-1);
printf("\n");
}
これは、単一リンク リスト の要素を出力する関数です。関数名はDispListです。パラメータとして単一リンク リスト Lを受け取り、戻り値はありません。
関数内では、まずL の最初のノードを指すポインター p を定義し、次に while ループを使用して単一リンク リスト L を走査します。ノードが走査されるたびに、ノードのシリアル番号とデータ値が出力されます。、カウンタ i が 1 ずつインクリメントされ、最終的に要素の総数が単一リンク リストに出力されます。出力の形式は 2 列で、最初の列はノードのシリアル番号、2 番目の列はノードのデータ値です。
単一リンク リストのすべての要素を出力した後、関数は単一リンク リストの要素の合計数を出力します。
p=L->next;
このコードでは、L はヘッド ノードへのポインタであり、データは格納されず、単なる補助ノードです。したがって、単一リンク リスト全体を走査するには、 p をヘッド ノードの次のノード(実際にデータを格納する最初のノード)にポイントする必要があります。したがって、p=L->next; は、ポインタ p を単一リンク リストの最初のノードを指します。
printf(" %d %d",i,p->data);
このコード行の機能は、現在のノードの番号と対応するデータ値を出力することです。このうち、iは現在のノードの番号を示すカウンタであり、p->dataは現在のノードに格納されているデータ値を示す。
p=p->next;
このコード行は、単一リンク リストをトラバースするために、現在のノード ポインター p を次のノードにポイントします。
1.1.8 単一リンクリストの反転
//单链表的逆置
void Reverse_LinkList (LinkList H)
{
LinkList p,q;
p=H->next; /*p指向第一个数据结点*/
H->next=NULL; /*将原链表置为空表H*/
while (p)
{
q=p;
p=p->next;
q->next=H->next; /*将当前结点插到头结点的后面*/
H->next=q;
} /*while*/
}
このコードは、単一リンク リストの反転関数を実装します。まず、ポインタ p が単一リンク リストの最初のデータ ノードを指し、次にヘッド ノードの次のフィールドを NULL に設定します。これは、元のリンク リストを空のリストとして設定するのと同じです。次に、while ループに入り、毎回 p が指すノードを削除し、それを q に保存し、p が次のノードを指すようにします。次に、ヘッド ノードの後ろに q を挿入して、単一リンク リストの反転を実現します。p が空になるまで、つまり元のリンク リスト内のすべてのノードが反転されるまでループします。
このコードは、単連結リストの反転を実現するものです。基本的な考え方は、元のリンク リストの先頭ノードから開始し、各ノードを順番に取り出して、新しいリンク リストの先頭に挿入することです。
具体的な実装方法は次のとおりです。2つのポインター p と qを定義します。pは元のリンク リストの最初のデータ ノードを指し、q は p の後続ノードを指します。次に、元のリンク リストから p を切断し、新しいリンク リストの先頭に挿入します。次に、q が p の後続ノードをポイントし、元のリンク リストが走査されるまで上記の操作を繰り返します。最後に、元のリンク リストの先頭ノード ポインタが NULL に設定され、新しいリンク リストの先頭ノードは元のリンク リストの末尾ノードを指します。
これで、単一リンクリストの反転操作が完了します。