1. 二重リンクリストの概念
二重リンク リストは、先頭の二重循環リンク リストです。
単一リンク リストの学習を終えた後、これは非常に単純であると感じました。その主なパフォーマンスはヘッド ノードを持つことであり、リンク リストは決して空ではなく、2 次ポインタは必要ありません。前または次のノードを見つけることができます。ノードを介して、その端は Ringed に接続されます。
主な構造はprev 、 next 、 dataの 3 つの構造で構成されており、 prev までで前のノードを見つけられれば、 next については特に説明する必要はありません。
写真が示すように
もうナンセンスではありません。コードにアクセスしてください。
2. 二重リンクリストの実装
2.1 構造設計
typedef int LTDataType;
typedef struct ListNode
{
struct ListNode* next;
struct ListNode* prev;
LTDataType data;
}LTNode;
2.2 インターフェースの概要
二重リンクリストの各インターフェースを実装する
typedef int LTDataType;
typedef struct ListNode
{
struct ListNode* next;
struct ListNode* prev;
LTDataType data;
}LTNode;
LTNode* LTInit();
void LTPrint(LTNode* phead);
bool LTEmpty(LTNode* phead);
void LTPushBack(LTNode* phead, LTDataType x);
void LTPushFront(LTNode* phead, LTDataType x);
void LTPopBack(LTNode* phead);
void LTPopFront(LTNode* phead);
LTNode* LTFind(LTNode* phead, LTDataType x);
// 在pos之前插入
void LTInsert(LTNode* pos, LTDataType x);
// 删除pos位置的值
void LTErase(LTNode* pos);
void LTDestroy(LTNode* phead);
初期化したとき、ここにはパラメータがありませんでした。なぜでしょうか?
双方向リンク リストはリーダーであり、ヘッド ノードがあり、これをセンチネルビットと呼びます。センチネル ビットには有効なデータが格納されないため、リンク リストの最初のノードはセンチネル ビットの後の最初のノードになります。
注: ここでの操作に第 2 レベルのポインタを使用することもできますが、第 2 レベルのポインタは扱いにくく、将来のコードで誤って使用されやすいため、センチネル ビットを直接使用して問題を解決します。第 2 レベルのポインタを使用します。
2.3 初期化
センチネル ビットを作成し、循環リンク リストに変換します。
ただ彼に自分自身を指ささせてください
LTNode* LTInit()
{
LTNode* phead = BuyLTNode(-1);
phead->next = phead;
phead->prev = phead;
return phead;
}
2.4 ノードの作成
要素を挿入する必要がある場合は、ノードを作成し、値を直接保存し、2 つのポインターを null に設定して、ノードのアドレスを返すだけです。
LTNode* BuyLTNode(LTDataType x)
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
if (newnode == NULL)
{
perror("malloc fail");
return NULL;
}
newnode->data = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
2.5テールプラグ
私たちのアイデアは、tail->nextのリンクをnewnodeに直接接続し、次にnewnode->prevをtailに接続するというものです。最後に、phead の前と次を接続できます。
void LTPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* tail = phead->prev;
LTNode* newnode = BuyLTNode(x);
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
}
2.6プラグ
ヘッドの挿入は、リンク リストの最初のノードとセンチネルの位置の間のリンクを切断することですが、切断する前に、まず切断されるデータを保存してから、挿入する新しいノードを変更する必要があります。
void LTPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* first = phead->next;
LTNode* newnode = BuyLTNode(x);
phead->next = newnode;
newnode->prev = phead;
newnode->next = first;
first->prev = newnode;
}
2.7 末尾の削除
ここでも、 tail の前のノードである tailprev を見つけて、tail ノードを直接解放する必要があります。
ここで注意してください。監視役職のみがある場合は削除できません。
void LTPopBack(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);//防止把哨兵位删掉
LTNode* tail = phead->prev;
LTNode* tailprev = tail->prev;
free(tail);
phead->prev = tailprev;
tailprev->next = phead;
}
2.8 ヘッダーの削除
ヘッドを削除するには、センチネル ビットの次の ノードであるリンク リストの最初のノードを削除する必要があります 。センチネル ビット と 2 番目のノードの間のリンク関係を変更し 、 最初のノードを解放する 必要があります 。
void LTPopFront(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);
LTNode* first = phead->next;
LTNode* firstnext = first->next;
free(first);
firstnext->prev = phead;
phead->next = firstnext;
}
2.9 検索
二重リンクリストの場合 、 NULLノード を指さず、リングなので停止できません。したがって、ループのカットオフ条件を! = pheadに設定する必要があります。この条件は、リンクされたリストが 1 回走査されていることを意味します。
LTNode* LTFind(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
2.10 pos の前に挿入
pos の位置で前のノード posprev から pos の prev までを検索し、posprev と新しいノード newnode の間のリンク、および newnode と pos の間のリンクを変更します。
void LTInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* newnode = BuyListNode(x);
LTNode* posprev = pos->prev;
newnode->prev = posprev;
posprev->next = newnode;
newnode->next = pos;
pos->prev = newnode;
}
2.11 pos の前に挿入
pos の位置を削除するには、pos の前のノード posprev と次のノード posnextを見つけて、prev と next の間のリンクを接続し、pos ノードを解放するだけです。
void LTErase(LTNode* pos)
{
assert(pos);
LTNode* posprev = pos->prev;
LTNode* posnext = pos->next;
free(pos);
posprev->next = posnext;
posnext->prev = posprev;
}
2.12 空かどうかの判定
ここで、この関数を追加できます。次の phead が phead と等しい場合、リンクされたリストが空で、センチネル自体だけが残っていることを意味します。この時点では、 の削除を続行することはできません。ここで、この関数を使用して直接実行できます。上記の主張を置き換えます
bool LTEmpty(LTNode* phead)
{
assert(phead);
return phead->next == phead;
}
ここで、次のように省略することもできます。
void LTPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
LTInsert(phead, x);
}
void LTPushFront(LTNode* phead, LTDataType x)
{
LTInsert(phead->next, x);
}
void LTPopBack(LTNode* phead)
{
assert(phead);
assert(!LTEmpty(phead));
LTErase(phead->prev);
}
void LTPopFront(LTNode* phead)
{
assert(phead);
assert(!LTEmpty(phead));
LTErase(phead->next);
}
2.13 印刷
void LTPrint(LTNode* phead)
{
assert(phead);
printf("guard<==>");
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d<==>", cur->data);
cur = cur->next;
}
printf("\n");
}
2.14 破壊
この時点でリンク リストのすべてのノードを直接削除し、ループを容易にするために削除プロセス中に次のノードを記憶します。
注: 関数内でセンチネル ビットを解放し、空にしたいと考えています。センチネルビットのアドレスがわかっているので解放することは可能ですが、空にすることで完了することはできません。私のセンチネルは仮パラメータであるため 、仮パラメータを変更しても実際のパラメータには影響しないため、 メイン関数で センチネルを空にする必要もあります
void LTDestroy(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
}
完全なコード
リスト.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int LTDataType;
typedef struct ListNode
{
struct ListNode* next;
struct ListNode* prev;
LTDataType data;
}LTNode;
LTNode* LTInit();
void LTPrint(LTNode* phead);
bool LTEmpty(LTNode* phead);
void LTPushBack(LTNode* phead, LTDataType x);
void LTPushFront(LTNode* phead, LTDataType x);
void LTPopBack(LTNode* phead);
void LTPopFront(LTNode* phead);
LTNode* LTFind(LTNode* phead, LTDataType x);
// 在pos之前插入
void LTInsert(LTNode* pos, LTDataType x);
// 删除pos位置的值
void LTErase(LTNode* pos);
void LTDestroy(LTNode* phead);
リスト.c
#include "List.h"
LTNode* BuyLTNode(LTDataType x)
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
if (newnode == NULL)
{
perror("malloc fail");
return NULL;
}
newnode->data = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
LTNode* LTInit()
{
LTNode* phead = BuyLTNode(-1);
phead->next = phead;
phead->prev = phead;
return phead;
}
bool LTEmpty(LTNode* phead)
{
assert(phead);
return phead->next == phead;
}
void LTPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* tail = phead->prev;
LTNode* newnode = BuyLTNode(x);
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
}
void LTPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* first = phead->next;
LTNode* newnode = BuyLTNode(x);
phead->next = newnode;
newnode->prev = phead;
newnode->next = first;
first->prev = newnode;
}
void LTPopBack(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);//防止把哨兵位删掉
LTNode* tail = phead->prev;
LTNode* tailprev = tail->prev;
free(tail);
phead->prev = tailprev;
tailprev->next = phead;
}
void LTPopFront(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);
LTNode* first = phead->next;
LTNode* firstnext = first->next;
free(first);
firstnext->prev = phead;
phead->next = firstnext;
}
LTNode* LTFind(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void LTInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* newnode = BuyListNode(x);
LTNode* posprev = pos->prev;
newnode->prev = posprev;
posprev->next = newnode;
newnode->next = pos;
pos->prev = newnode;
}
void LTErase(LTNode* pos)
{
assert(pos);
LTNode* posprev = pos->prev;
LTNode* posnext = pos->next;
free(pos);
posprev->next = posnext;
posnext->prev = posprev;
}
void LTDestroy(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
}
void LTPrint(LTNode* phead)
{
assert(phead);
printf("guard<==>");
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d<==>", cur->data);
cur = cur->next;
}
printf("\n");
}
テスト.c
#include"List.h"
void TestList1()
{
LTNode* plist = LTInit();
LTPushBack(plist, 1);
LTPushBack(plist, 2);
LTPushBack(plist, 3);
LTPushBack(plist, 4);
LTPrint(plist);
LTPopBack(plist);
LTPrint(plist);
LTPopBack(plist);
LTPrint(plist);
LTPopBack(plist);
LTPrint(plist);
LTPopBack(plist);
LTPrint(plist);
//LTPopBack(plist);
//LTPrint(plist);
LTDestroy(plist);
plist = NULL;
}
void TestList2()
{
LTNode* plist = LTInit();
LTPushFront(plist, 1);
LTPushFront(plist, 2);
LTPushFront(plist, 3);
LTPushFront(plist, 4);
LTPrint(plist);
LTPopFront(plist);
LTPrint(plist);
LTPopFront(plist);
LTPrint(plist);
LTPopFront(plist);
LTPrint(plist);
LTPopFront(plist);
LTPrint(plist);
/*LTPopFront(plist);
LTPrint(plist);*/
LTDestroy(plist);
plist = NULL;
}
void TestList3()
{
LTNode* plist = LTInit();
LTPushFront(plist, 1);
LTPushFront(plist, 2);
LTPushFront(plist, 3);
LTPushFront(plist, 4);
LTPrint(plist);
LTNode* pos = LTFind(plist, 3);
if (pos)
{
LTInsert(pos, 30);
}
LTPrint(plist);
LTDestroy(plist);
plist = NULL;
}
int main()
{
TestList3();
return 0;
}
以上が今日の共有です。気に入っていただけましたら、Sanlian をサポートしてください。また次回お会いしましょう。