個人ホームページ:みなさん、ようこそ->砂漠の下のコトカケヤナギ
みんな、あなたはとても美しいです
記事が役に立ったら
ワンクリックでブロガーをサポートできます
あなたの懸念のすべてのオンスは私が忍耐するための原動力です
☄: この問題の焦点:データ構造の章での単一リンクリストの実装
皆さんが幸せな勉強をして、毎日働いてくれることを願っています。
データ構造を学び、最初に描くことを学び、この号でシャオ・フヤンと一緒に絵の旅を始めましょう!
コンテンツ
単一リンクリストとは何ですか?
リンクリストは、物理ストレージ構造の観点からは、非連続的かつ非順次的なストレージ構造です。データ要素の論理的な順序は、リンクリスト内のポインターのリンク順序によって実現されます。
チェーンへの連結に似ています。
リンクリストの実際のアドレスの保存
リンクリストの一般的な図は次のとおりです。
実際、保存するリンクリストの実際のアドレスを確認する必要があります。
コードは次のように表示されます。
SLTNode* d1 = (SLTNode*)malloc(sizeof(SLTNode)); assert(d1); SLTNode* d2 = (SLTNode*)malloc(sizeof(SLTNode)); assert(d2); SLTNode* d3 = (SLTNode*)malloc(sizeof(SLTNode)); assert(d3); d1->data = 1; d2->data = 2; d3->data = 3; d1->next = d2; d2->next = d3; d3->next = NULL; SListPrint(d1);
これは、単一リンクリストの実際のアドレスです。
リンクリストを作成する
まず、リンクリストを作成し、構造体を使用して作成する必要がありますが、これは言うまでもありません。このヘッダーファイルの構造体定義の詳細はすべてコードに含まれています。構造がわからない場合は、☞構造の詳細説明に進んでください。
#pragma once//防止头文件重复包含 #define _CRT_SECURE_NO_WARNINGS//防止scanf函数报错 #include <stdio.h> #include <stdlib.h> #include <assert.h>//断言头文件 typedef int SLTDataType;//把int重命名 typedef struct SListNode//结构体重命名 { SLTDataType data; struct SListNode *next;//这个不可以使用重命名后的名字 }SLTNode;
アプリケーションノード
リンクリストはありますが、リンクリストのメンバーはまだいません!では、ノードを申請する必要がありますが、いつノードを申請する必要がありますか?挿入する選択はノードが必要になるため、ノードに直接適用する関数を記述し、デフォルトでNULLを指すことができるデータをアタッチすることもできます。
コードを見てください:
SLTNode* BuySListNode(SLTDataType x) { SLTNode* newnode = (SLTNode *)malloc(sizeof(SLTNode)); assert(newnode); newnode->next = NULL; newnode->data = x; return newnode; }
関数に割り当てる値を直接渡し、ノード用のスペースを開き、アサーションを作成し、スペースが適用されるかどうかを判断してから、データに割り当てる値を割り当て、ノードポインターをポイントすることができます。 NULLに。
リンクリストの終わり
まず、渡すパラメータについて考えてみましょう。
最初は挿入される値であり、次にリンクリストのアドレス、または直接リンクリストですか?リンクリストがNULLの場合はどうなりますか?
まず、概略図を見てみましょう。
すでにノードがあり、値が配置されているので、それを行うにはどうすればよいですか?d3のnextのNULLを、新しく開いたノードにポイントするだけですよね?次に、画像を見てください。
こんな感じです。(最も重要なことは、plistが変更されていないことです)
しかし、リンクリストに要素がない場合はどうなりますか?次に、画像を見てください。
このとき、plistはNULLなので、plistをdのアドレスに向けると、次のノードがNULLであるdは1つだけになります。(注:plistはnullポインターから新しいノードdを指すように変更されました)
コードを見てください(多くの人がデモンストレーションするのは間違っているでしょう):
void SListPushBack(SLTNode *pphead, SLTDataType x) { SLTNode* newnode = BuySListNode(x); if (pphead == NULL) { pphead = newnode; } else { SLTNode* cur = pphead; while (cur->next) { cur = cur->next; } cur->next = newnode; } }
この書き方に何か問題はありますか?
デバッグしたところ、plistが変更されておらず、リンクリストが挿入されていないことがわかりました。次に、画像を確認します。
結論に達しました:
ポインタを渡す代わりに、このポインタが指すものを変更できます。第2レベルのポインタの形式を渡し、それを逆参照して、plistアドレスを変更する効果を実現する必要があります。したがって、リンクリストは、第2レベルのポインターを渡し、第2レベルのポインターを渡します。第1レベルへのポインターを使用する場合は、ヘッドノードを使用して逆参照する必要があります。
したがって、正しいコードは次のとおりです。
void SListPushBack(SLTNode **pphead, SLTDataType x) { SLTNode* newnode = BuySListNode(x); if (*pphead == NULL) { *pphead = newnode; } else { SLTNode* cur = *pphead; while (cur->next) { cur = cur->next; } cur->next = newnode; } }
リンクリストヘッダーが使用される場所がリンクリストヘッダーを逆参照するように変更されていることを除いて、他に何も変更されていません。
リンクリストヘッダー
plistが変更されたかどうかという問題をまだ検討する必要がありますか?
これは確かに、ヘッドプラグはヘッドを交換するためのものです!つまり、リンクリストの先頭が変更されたため、アドレスを渡す必要があります。
理解するために写真を見てみましょう:
これは最初のケースです。plistのアドレスを新しいノードに入れ、plistを新しいノードのアドレスに変更します。
下の図を見てみましょう。
plistはありませんが、ppheadがあり、直接間接参照はヘッドノードです。
注意点:
最初にステップ1を実行し、次にステップ2を実行する必要があります。そうしないと、ヘッドノードが上書きされ、将来のノードが見つからないため、最初にステップ1を実行してから、ステップ2を実行する必要があります。
次の図は、最初にステップ2を実行し、ステップ1 (無限ループ)を実行した結果を示しています。
正しいコードは次のとおりです。
void SListPushFrond(SLTNode **pphead, SLTDataType x) { SLTNode* newnode = BuySListNode(x); newnode->next = *pphead; *pphead = newnode; }
リンクリストの先頭を削除します。
リンクリストの先頭の削除は非常に簡単です。ノードが1つしかない場合は、削除後にNULLになり、リンクリストのポイントが変更されるため、セカンダリポインタを渡します。
次の図を参照してください。
元のヘッドノードを2番目のノードにポイントしてから、元のヘッドノードを解放するだけです。
知っておく必要があるのは:
2番目のノードのアドレスを保存するには、最初にノードポインタを作成する必要があります
次に、元のヘッドポインタ(* pphead)を解放する必要があります
次に、2番目のノードのアドレスを* ppheadに割り当て、それを新しいリンクリストのヘッドポインターにします。
* ppheadの次のノードがNULLであるため、ノードである場合も可能です。
最初にNULLを格納し、次に*ppheadノードを解放します
次に、* ppheadにNULLを入れると、NULLになり、条件を満たします。
コードは次のとおりです。
void SListPopFrond(SLTNode **pphead) { assert(pphead); SLTNode* next = (*pphead)->next; free(*pphead); *pphead = next; }
リンクリストのテール削除
リンクリストの末尾の削除については、さらに検討する必要があります。ノードが1つある場合は、複数のノードとは別に分析されます。
または、最初に画像を見てください。
この場合は、直接解放してからNULLに設定してください。
この場合、d3ノードを解放してから、d2ノードをNULLにポイントする必要があります。
では、どうやってd2を見つけるのでしょうか?次に、画像を見てください。
これがコードを書く実際のロジックです。以下はコードです。コードのコメント部分は、次のポインターに2回アクセスすることによる、2番目の方法です。
void SListPopBack(SLTNode **pphead) { assert(*pphead); if ((*pphead)->next == NULL) { free(*pphead); *pphead = NULL; } else { //SLTNode *cur = *pphead; //while (cur->next->next != NULL) //{ // cur = cur->next; //} //free(cur->next); //cur->next = NULL; SLTNode *tail = *pphead; SLTNode *tailprev = NULL; while (tail->next != NULL) { tailprev = tail;//失误三 tail = tail->next; } free(tail); tailprev->next = NULL; } }
リンクリスト要素を検索します。
検索は非常に簡単です。検索をブルートフォースするだけです。
コードを見てください:
SLTNode* SListFind(SLTNode *pphead, SLTDataType x) { SLTNode* cur = pphead; while (cur) { if (cur->data == x) { return cur; } cur = cur->next; } return NULL; }
注意点:
まず、最初にヘッドポインタを保存する必要があります。ヘッドポインタは変更しないでください。
次に、コードの堅牢性を考慮し、他の機能インターフェイスで使用する必要があるため、戻り値をノードポインターとして設定します。見つからない場合は、NULLを返します。
リンクリスト要素を変更する
また、変更して検索で使用するのも非常に簡単です。コードを見てください。
言うまでもなく、見つかった添え字を使用して変更を加えることができます。
void SListModify(SLTNode *pos, SLTDataType x) { assert(pos); pos->data = x; }
リンクリストの任意の位置の後に挿入します
リンクリスト内の任意の位置の後に挿入します。最初に位置を見つけてから挿入します。
最初に写真を見てください:
この挿入位置がわかっているので、ポインティングを変更して、画像を確認するだけです。
注意すべき点は、最初にステップ1を実行することです。ステップ2では、順序を逆にしないでください。逆にしないと、ヘッドプラグのような無限ループになります。
特定のコードは次のとおりです。
void SListInsertAfter(SLTNode *pos, SLTDataType x) { assert(pos); SLTNode* newnode = BuySListNode(x);//注意下面代码的位置 newnode->next = pos->next; pos->next = newnode; }
リンクリストの任意の場所に追加する
リンクリストの任意の位置の後に挿入するのはより複雑です。ここに少し分析があります:
まず、ノードが1つしかない場合は、ヘッドを接続するのと同じです。
ヘッドプラグと同じように、繰り返さない〜
複数のノードがある場合、それは複雑になります〜最初に画像を見てみましょう:
この位置にある前のノードの値をどのように知ることができますか?この方法は、実際にはテールの削除と同じです。変数を使用してトラバースし、ループの終了条件はp1-> next!=この位置になります。
下の写真を見てください:
最後にコードを見てください:
void SListInsertBefore(SLTNode **pphead, SLTNode *pos, SLTDataType x) { assert(pos); if (pos == *pphead) { SListPushFrond(pphead, x); } else { SLTNode *prve = *pphead; while (prve->next != pos) { prve = prve->next; } SLTNode* newnode = BuySListNode(x); newnode->next = pos; prve->next = newnode; } }
リンクリスト内の任意の位置の後に削除します
ポジションの削除は比較的簡単です。注意する必要があるのは1つだけです。
つまり、リンクリストの要素が1の場合、フォローアップはなく、削除することはできません。
または、下の画像を見てください。
ノードは1つだけで、次の要素は削除できず、他の要素は直接上書きされてから、pos位置のノードが解放されます。
コードを直接見てみましょう。
void SListEraseAfter(SLTNode *pos) { assert(pos); SLTNode *next = pos->next; if (next) { pos->next = next->next; free(next); next = NULL; } }
リンクリスト内の任意の位置を削除します
これはもう少し面倒です。少し分析してみましょう。
まず、この位置がリンクリストの先頭にある場合は、先頭を削除します。
または写真を見てください:
他の位置で削除された場合は、任意の位置に挿入した場合と同じです。ポインタノードを作成してトラバースする必要があります。ループの終了条件は、p1-> next!= posであり、リンクします。壊れたリンクリスト。
次に、画像を見てください。
コードは次のとおりです。
void SListErase(SLTNode **pphead, SLTNode *pos) { assert(pphead); assert(pos); if (*pphead == pos) { SListPopFrond(pphead); } else { SLTNode *prve = *pphead; while (prve->next != pos) { prve = prve->next; } prve->next = pos->next; free(pos); pos = NULL; } }
リンクリストの印刷
リンクリストのアドレスを渡さなくても印刷できます。リンクリストの内容は変更されません。もう1つのポイントは、トラバースする一時変数を作成することです。
コードを見てみましょう:
void SListPrint(SLTNode* pphead) { SLTNode* cur = pphead; while (cur) { printf("%d->", cur->data); cur = cur->next; } printf("NULL\n"); }
空きリストスペース
これについては触れられていませんが、リンクリストをNULLに設定する必要があるため、リンクリストのアドレスを渡す必要があります。
コードを見てください:
void SListDestroy(SLTNode **pphead) { assert(pphead); SLTNode *cur = *pphead; while (cur) { SLTNode *next = cur->next; free(cur); cur = next; } *pphead = NULL; }
全体的なコードヘッダーファイルについて:
#pragma once #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <assert.h> typedef int SLTDataType; typedef struct SListNode { SLTDataType data; struct SListNode *next; }SLTNode; void SListPrint(SLTNode* pphead);//打印 SLTNode* BuySListNode(SLTDataType x);//申请新节点 void SListPushBack(SLTNode **pphead, SLTDataType x);//尾插 void SListPushFrond(SLTNode **pphead, SLTDataType x);//头插 void SListPopFrond(SLTNode **pphead);//头删 void SListPopBack(SLTNode **pphead);//尾删 SLTNode* SListFind(SLTNode *pphead, SLTDataType x);//查找 void SListInsertBefore(SLTNode **pphead, SLTNode *pos, SLTDataType x);//任意位置前插 void SListInsertAfter(SLTNode *pos, SLTDataType x);//任意位置后插 void SListErase(SLTNode **pphead, SLTNode *pos);//删除任意位置 void SListEraseAfter(SLTNode *pos);//任意位置后删 void SListDestroy(SLTNode **pphead);//释放链表 void SListModify(SLTNode *pos, SLTDataType x);//修改链表
全体的なコードソースファイルについて:
#include "SList.h" void SListPrint(SLTNode* pphead) { SLTNode* cur = pphead; while (cur) { printf("%d->", cur->data); cur = cur->next; } printf("NULL\n"); } SLTNode* BuySListNode(SLTDataType x) { SLTNode* newnode = (SLTNode *)malloc(sizeof(SLTNode)); assert(newnode); newnode->next = NULL; newnode->data = x; return newnode; } void SListPushBack(SLTNode **pphead, SLTDataType x) { SLTNode* newnode = BuySListNode(x); if (*pphead == NULL) { *pphead = newnode; } else { SLTNode* cur = *pphead; while (cur->next) { cur = cur->next; } cur->next = newnode; } } void SListPushFrond(SLTNode **pphead, SLTDataType x) { SLTNode* newnode = BuySListNode(x); newnode->next = *pphead; *pphead = newnode; } void SListPopBack(SLTNode **pphead) { assert(*pphead); if ((*pphead)->next == NULL) { free(*pphead); *pphead = NULL; } else { //方法二 //SLTNode *cur = *pphead; //while (cur->next->next != NULL) //{ // cur = cur->next; //} //free(cur->next); //cur->next = NULL; SLTNode *tail = *pphead; SLTNode *tailprev = NULL; while (tail->next != NULL) { tailprev = tail; tail = tail->next; } free(tail); tailprev->next = NULL; } } void SListPopFrond(SLTNode **pphead) { assert(pphead); SLTNode* next = (*pphead)->next; free(*pphead); *pphead = next; } SLTNode* SListFind(SLTNode *pphead, SLTDataType x) { SLTNode* cur = pphead; while (cur) { if (cur->data == x) { return cur; } cur = cur->next; } return NULL; } void SListInsertBefore(SLTNode **pphead, SLTNode *pos, SLTDataType x) { assert(pos); if (pos == *pphead) { SListPushFrond(pphead, x); } else { SLTNode *prve = *pphead; while (prve->next != pos) { prve = prve->next; } SLTNode* newnode = BuySListNode(x); newnode->next = pos; prve->next = newnode; } } void SListInsertAfter(SLTNode *pos, SLTDataType x) { assert(pos); SLTNode* newnode = BuySListNode(x);//注意下面代码的位置 newnode->next = pos->next; pos->next = newnode; } void SListErase(SLTNode **pphead, SLTNode *pos) { assert(pphead); assert(pos); if (*pphead == pos) { SListPopFrond(pphead); } else { SLTNode *prve = *pphead; while (prve->next != pos) { prve = prve->next; } prve->next = pos->next; free(pos); pos = NULL; } } void SListEraseAfter(SLTNode *pos) { assert(pos); SLTNode *next = pos->next; if (next) { pos->next = next->next; free(next); next = NULL; } } void SListDestroy(SLTNode **pphead) { assert(pphead); SLTNode *cur = *pphead; while (cur) { SLTNode *next = cur->next; free(cur); cur = next; } *pphead = NULL; } void SListModify(SLTNode *pos, SLTDataType x) { assert(pos); pos->data = x; }
———————————————————————————————————————————