歴史上最強のデータ構造----二重循環リンクリストの実装(センチネルビット付き)
1.二重リンクリストの基本構造
アイコン:
2.二重リンクリストの基本構造の実装
コード:
typedef int LTDataType;
typedef struct ListNode
{
LTDataType data;//存储的数据
struct ListNode* next;//存储下一个节点的地址
struct ListNode* prev;//存储上一个节点的地址
}ListNode;
3.二重リンクリストの初期化
アイコン:
コード:
最初のもの:2番目のレベルのポインターを渡します
void ListInit(ListNode** phead);//初始化函数的声明
void ListInit(ListNode** phead)//为什么要传二级指针,因为改变的是结构体的指针,所以要传结构体的指针
{
assert(phead);
*phead = BuyListNode(-1);//此处是开辟一个哨兵位的节点,存储的是无效数据,-1也是随便给的
(*phead)->next = (*phead);
(*phead)->prev = (*phead);
//这两行代码主要是使哨兵位的next指针和prev指针自己指向自己,形成双向循环结构
}
//下面是main函数中调用初始化函数的地方,为了方便理解二级指针
int main()
{
ListNode*phead = NULL;//初始化之后,phead就会指向哨兵位,即NULL发生了改变,即改变的是结构体的指针,所以要传二级指针
ListNode(&phead);
return 0;
}
2番目:セカンダリポインタを渡さないでください
ListNode* ListInit(ListNode* phead);//初始化函数的声明
ListNode* ListInit()//此处为什么不传二级指针,因为在函数中会将开辟的哨兵位的地址作为返回值返回
{
ListNode*phead = NULL;
phead = BuyListNode(-1);//此处是开辟一个哨兵位的节点,存储的是无效数据,-1也是随便给的
phead->next = phead;
phead->prev = phead;
//这两行代码主要是使哨兵位的next指针和prev指针自己指向自己,形成双向循环结构
}
int main()
{
ListNode*phead = ListInit();
return 0;
}
4.二重リンクリストのテール挿入
アイコン:
コード:
void ListPushBack(ListNode* phead, LTDataType x)
{
assert(phead);//哨兵位不可为空
ListNode* newnode = BuyListNode(x);
ListNode* tail = phead->prev;
tail->next = newnode;//(1)
newnode->prev = tail;//(2)
newnode->next = phead;//(3)
phead->prev = newnode;//(4)
}
5.二重リンクリストのテール削除
アイコン:
コード:
void ListPopBack(ListNode* phead)
{
assert(phead);
assert(phead->next == phead);//链表为空
ListNode* tail = phead->prev;
ListNode* tailPrev = tail->prev;
free(tail);
tail = NULL;
tailPrev->next = phead;//(1)
phead->prev = tailPrev;//(2)
}
6.二重リンクリストのヘッダー
アイコン:
コード:
void ListPushFront(ListNode* phead,LTDataType x)
{
assert(phead);
ListNode* next = phead->next;
ListNode* newnode = BuyListNode(x);
phead->next = newnode;//(1)
newnode->prev = phead;//(2)
next->prev = newnode;//(3)
newnode->next = next;//(4)
}
7.二重リンクリストの先頭を削除します
アイコン:
コード:
void ListPopFront(ListNode* phead)
{
assert(phead);
assert(phead->next != phead);
ListNode* nextNext = phead->next->next;
free(phead->next);
phead->next = nextNext;//(1)
nextNext->prev = phead;//(2)
}
8.二重リンクリストの印刷
コード:
void ListPrint(ListNode* phead)
{
assert(phead);
ListNode* cur = phead->next;
while (cur != phead)//当cur = phead的时候说明已经遍历完一遍了
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
9.二重リンクリストの要素を検索します
コード:
ListNode*ListFind(ListNode* phead, LTDataType x)
{
assert(phead);
ListNode* cur = phead->next;
while (cur!=phead)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
注:最初にcurをphead-> nextに設定するのはなぜですか?
1. pheadはセンチネルビットに格納されている有効なデータではないため、これから検索することはできません。
2.同時に、pheadは検索の終了条件として使用されます。この要素から開始することはできません。この要素から検索を開始すると、検索は最初から停止します。
10.二重リンクリストの特定の要素を削除します
アイコン:
コード:
void ListErase(ListNode* pos)
{
assert(pos);
ListNode* next = pos->next;
ListNode* prev = pos->prev;
free(pos);
pos = NULL;//此处可以置空也可以不置空,因为pos只是函数中的局部变量
prev->next = next;//(1)
next->prev = prev;//(2)
}
キャメルケースの命名規則:
- 関数名とタイプ名のすべての単語を大文字にします
- 変数:最初の単語は小文字、後続の単語は大文字
11.二重リンクリストへの特定の要素の事前挿入
アイコン:
コード:
void ListInsertBefore(ListNode* phead, ListNode* pos, LTDataType x)
{
assert(pos);
ListNode* newnode = BuyListNode(x);
ListNode* prev = pos->prev;
prev->next = newnode;//(1)
newnode->prev = prev;//(2)
pos->prev = newnode;//(3)
newnode->next = pos;//(4)
}
12.二重リンクリストの破棄
アイコン:
コード:
1つ目:第2レベルのポインタが渡されると、この時点でセンチネルビットのスペースが解放されます。
void ListDestory(ListNode** phead)
{
assert(phead);
ListNode* cur = (*phead)->next;
while (cur!=(*phead))//结束条件就是不等于头节点
{
ListNode* next = cur->next;//存储当前释放节点的下一个节点的地址
free(cur);//释放当前节点
cur = next;//将节点向后推移
}
free(*phead);//释放哨兵位节点
*phead = NULL;//防止内存泄漏
}
2番目:セカンダリポインタが渡されない場合、この時点でセンチネルビットのスペースは解放されません。
void ListDestory(ListNode* phead)
{
assert(phead);
ListNode* cur = phead->next;
while (cur != phead)
{
ListNode* next = cur->next;
free(cur);
cur = cur->next;
}
cur = NULL;
}