権力者の恩恵を求めるよりも、自分の力で未来を切り拓きたい
記事ディレクトリ
過去のレビュー:
皆さんこんにちは、ジニンです。
この記事では、単一リンク リストよりもパフォーマンスが優れたリンク リスト、つまりデータの挿入、削除、検索をより効率的に実装できる二重リンク リストについて紹介します。
記事の前半は二重リンクリストの対応する名前とソースコード、記事の後半は二重リンクリストの実装の具体的な説明です。
二重線リンクリストの各インターフェースの関数名または変数名
LTデータタイプ | 二重リンクリストのデータ型の名前変更 |
リストノード | 二重リンクリスト構造 |
LTノード | 二重リンクリストの名前変更 |
LTノードを購入する | ノードを作成する |
LTI | ノードを初期化する |
LTプリント | 二重リンクリストを印刷する |
LTPプッシュバック | テールプラグ |
LTポップバック | 末尾削除 |
LTPushFront | プラグ |
LTポップフロント | 頭の削除 |
LTサイズ | 二重リンクリスト要素の数を計算する |
LTFind | リンクされたリスト要素を検索する |
LT挿入 | pos の前にノードを挿入 |
LTErase | 位置 pos のノードを削除します |
二重リンクリストインターフェース実装ソースコード
クイックインデックス [ヘッダーファイルと関数宣言]
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int LTDataType;//重命名
typedef struct ListNode
{
LTDataType Data;
struct ListNode* next;
struct ListNode* prev;
}LTNode;
LTNode* BuyLTNode(LTDataType x); //创建一个新节点
LTNode* LTInit(); //哨兵位的头结点
void LTPrint(LTNode*phead);//打印双链表
void LTPushBack(LTNode* phead, LTDataType x);//尾插
void LTPopBack(LTNode* phead);//尾删
void LTPushFront(LTNode* phead, LTDataType x);//头插
void LTPopFront(LTNode* phead);//头删
LTNode* LTFind(LTNode* phead, LTDataType x);//寻找结点
void LTInsert(LTNode*phead,LTNode* pos, LTDataType x); //在pos之前插入结点
二重リンクリストインターフェイスの実装
LTNode* BuyLTNode(LTDataType x)
{
LTNode* nownode =(LTNode*)malloc(sizeof(LTNode));
if (nownode == NULL)
{
perror("malloc fail");
}
nownode->Data = x;
nownode->next = NULL;
nownode->prev = NULL;
return nownode;
}
LTNode* LTInit()
{
LTNode* phead = BuyLTNode(0);
phead->next = phead;
phead->prev = phead;
return phead;
}
void LTPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* tail = phead->prev;
LTNode* nownode = BuyLTNode(x);
nownode->next = phead;
nownode->prev = tail;
tail->next = nownode;
phead->prev = nownode;
}
void LTPrint(LTNode* phead)
{
assert(phead);
printf("phead<=>");
LTNode* cur = phead;
while (cur->next!=phead)
{
printf("%d<=>", cur->next->Data);
cur = cur->next;
}
printf("\n");
}
void LTPopBack(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);//只有哨兵位的时候不能删
LTNode* tail = phead->prev;
LTNode* tailPrev = tail->prev;
tailPrev->next = tail->next;
phead->prev = tailPrev;
free(tail);
}
void LTPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* first = phead->next;
LTNode* nownode = BuyLTNode(x);
nownode->next = first;
nownode->prev = phead;
phead->next = nownode;
first->prev = nownode;
}
void LTPopFront(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);//只有哨兵位的时候不能删除
LTNode* first = phead->next;
LTNode* second = first->next;
phead->next = second;
second->prev = phead;
free(first);
}
LTNode* LTFind(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* cur = phead;
while (cur->next != phead)
{
if (cur->next->Data == x)
return cur->next;
cur = cur->next;
}
return NULL;
}
void LTInsert(LTNode* phead, LTNode* pos, LTDataType x)
{
assert(phead);
assert(pos);
LTNode* cur = phead;
while (cur->next != pos)
{
cur = cur->next;
}
LTNode*nownode = BuyLTNode(x);
nownode->next = pos;
nownode->prev = cur;
cur->next = nownode;
pos->prev = nownode;
}
void LTErase(LTNode* phead, LTNode* pos)
{
assert(pos&&phead);
assert(pos->next != pos);
assert(phead->next != phead);
LTNode* cur = phead;
LTNode* posNext = pos->next;
while (cur->next != pos)
{
cur = cur->next;
}
cur->next = posNext;
posNext->prev = cur;
free(pos);
}
二重連結リストの構成解析
二重リンク リストは、単一リンク リストと比較して、二重リンク リストのノードには 2 つのポインター ドメインがあり、1 つは次のノードを指し、もう 1 つは前のノードを指します。デフォルトの二重リンク リストにはセンチネル ビットを持つヘッド ノードがあります。センチネル ビットの先頭 ノードは、最初の有効なノードのアドレス (phead->next) と最後の有効なノードのアドレス (phead->prev) を格納します。
シングルリンクリストとダブルリンクリストの論理図の比較
シングルリンクリストとダブルリンクリストの物理図の比較
二重リンクリストの定義と初期化
二重リンクリストにはデータフィールドと 2 つのポインタフィールドがあり、1 つのポインタは次のノードのアドレスを指し、もう 1 つのポインタは前のノードのアドレスを指し、二重リンクリストの構造は再び名前が変更されます。
二重リンク リストが新しいノードを開くときは、まずノード サイズのスペースを開き、その次の NULL を空にポイントしてから、データ フィールドの値を x に割り当てる必要があります。
二重リンクリストの挿入と削除
二重リンク リストを削除するには、まずセンチネル ビットのヘッド ノードは削除できないことを明確にします。これは、二重リンク リスト全体の構造サポートであるためです。削除するときは、削除するノードの前のノードと次のノードを見つけて、前のノードの次のポインタを次のノードにポイントし、次のノードの prev ポインタを前のノードにポイントします。最後に、削除されたノードのスペースが解放されます。
二重リンクリストの挿入では、理論的には任意の位置にノードを挿入できます。初期化時には、新しく作成されたノードを定義するポインター フィールドはすべて空であるためです。したがって、挿入するときは、挿入されたノードの 2 つのポインタ フィールドを変更し、その next ポインタが次のノードを指すようにし、その prev ポインタが前のノードを指すようにする必要があります。同様に、前のノードの next ポインタがこの新しいノードを指し、next ポインタの prev ポインタがこの新しいノードを指します。