データ構造(C言語編) - 第2章 - 線形テーブル(2)

出典書籍:
データ構造 (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 に設定され、新しいリンク リストの先頭ノードは元のリンク リストの末尾ノードを指します。
これで、単一リンクリストの反転操作が完了します。

おすすめ

転載: blog.csdn.net/aaaccc444/article/details/130497025