各位少年 大家好,我是小敖,今天给大家分享双向链表,双向链表
双向链表是一个节点有个前缀指针还有一个后缀指针。这其实就像火车头一样前面一个火车 ,中间一个车厢,最后一个车头。关于指针指针问题 请看下文。
双向链表
1“”***
如上面火车头显示
***双向链表有一个前缀指针prev指向前一个节点,***next后缀指针指向下一个节点***
2 *双向链表通常有一个哨兵位,哨兵位就是多了个头节点,通常使用哨兵位以后,代码可以更简洁更方便。
**3 *双向链表的第一个节点头端prev指向 双向链表最后一个节点,然后双向链表最后一个节点也指向最后头节点(哨兵位节点)。
3
双向链表的初始化
1 首先我们知道双向链表最好有个哨兵位,哨兵位也是个节点 ,我们需要初始化加上创建。
2我们需要改变指针的指向,我们知道头节点prev指向尾节点,尾节点next指向头节点,但是此时只有哨兵位节点***
LTNode* LTInit()
{
LTNode* phead = BuyLTNode(-1);
phead->next = phead;
phead->prev = phead;
return phead;
}
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;
链表打印
1 我们得知道什么时候停下来,之前单链表是next为空就结束了,但双向链表是 最后一个节点等于哨兵位就停下来。
2第二点我们还得处理哨兵位 ,哨兵位是负一 ,我们并不想把他打印下来。所以要保存哨兵位下一个节点``
void LTPrint(LTNode* phead)
{
assert(phead);
printf("guard<==>");
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d<==>", cur->data);
cur = cur->next;
}
printf("\n");
尾部插入
>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;
1 *尾部插入的问题 如何寻找尾部节点?很显然我们双向链表头的prev指针是尾部节点,是不是就可以找到了,然后改变原先最后一个节点next指向,然后再改变新节点prev指向前一个节点,新节点next再指向头,头节点prev再指向尾部,如图。
头部插入问题如下*
void LTPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyLTNode(x);
LTNode* first = phead->next;
phead->next = newnode;
newnode->prev = phead;
newnode->next = first;
first->prev = newnode;
}
头插问题 首先我们得跳过哨兵位置,然后去插入 让哨兵位next等于新节点nenodw,newnode 的prev等于哨兵位,newnode的next等于原先头节点,原先头节点prev等于newnode。这里可能有点乱,我把代码贴出来 大家可以自己画图``
void LTPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyLTNode(x);
LTNode* first = phead->next;
phead->next = newnode;
newnode->prev = phead;
newnode->next = first;
first->prev = newnode;
}
链表尾删
```c
void LTPopBack(LTNode* phead)
{
assert(phead);
assert(!LTEmpty(phead));
`` LTNode* tail = phead->prev;
LTNode* tailPrev = tail->prev;
free(tail);
tailPrev->next = phead;
phead->prev = tailPrev;
}`
1.双向尾删需要注意的是
先找到尾部节点,用个结构体指针遍历存储起来他的地址,然后通过尾部链表的prev找到前一个节点进行释放,然后把链表重新连接起来尾部删除就完成了。
链表头删
void LTPopFront(LTNode* phead)
{
assert(phead);
assert(!LTEmpty(phead));
LTNode* first = phead->next;
LTNode* second = first->next;
phead->next = second;
second->prev = phead;
free(first);
}
1 双向链表头删 首先我们得跳过哨兵位,然后存储要释放掉节点,然后用一个结构体指针变量存储第二个节点,就是我们要留下来那个。然后让指针重新指向,最后释放掉。
链表查找
1 .链表的查到 第一个问题是我们要遍历链表 ,然后单链表终止条件是尾端NULL,但是双向尾端指向的是头节点,这个时候哨兵位用出就体现了,一旦等于哨兵位结束循环,假设我们查j到不到我们想要节点,我们看代码
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;
}
pos之前的位置插入
双向链表pos之前插入的位置,首先我们得找到pos前面的位置,然后在他们之间进行,改变四个指针指向就好 这里直接贴代码不多做解释
void LTInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* prev = pos->prev;
LTNode* newnode = BuyLTNode(x);
// prev newnode pos
prev->next = newnode;
newnode->prev = prev;
newnode->next = pos;
pos->prev = newnode;
}
删除pos的位置
我们通过之前查到函数,找到pos位置 然后查到就进行释放,让pos之前节点和pos后面节点互相指向就好
void LTErase(LTNode* pos)
{
assert(pos);
LTNode* posPrev = pos->prev;
LTNode* posNext = pos->next;
posPrev->next = posNext;
posNext->prev = posPrev;
free(pos);
}
释放双向链表
1 双向链表的释放需要注意的是,先不要释放哨兵位,因为我们需要来做循环释放结束条件,就跟前面查到一样最后释放掉他,每次让cur=next让他往下走到下一个节点处
```c
```c
void LTDestroy(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
}
下面贴完整的代码
#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;
}
void LTPrint(LTNode* phead)
{
assert(phead);
printf("guard<==>");
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d<==>", cur->data);
cur = cur->next;
}
printf("\n");
}
bool LTEmpty(LTNode* phead)
{
assert(phead);
return phead->next == phead;
}
void LTPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
LTInsert(phead, x);
//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* newnode = BuyLTNode(x);
//
// newnode->next = phead->next;
// phead->next->prev = newnode;
//
// phead->next = newnode;
// newnode->prev = phead;
//}
void LTPushFront(LTNode* phead, LTDataType x)
{
/*assert(phead);
LTNode* newnode = BuyLTNode(x);
LTNode* first = phead->next;
phead->next = newnode;
newnode->prev = phead;
newnode->next = first;
first->prev = newnode;*/
LTInsert(phead->next, x);
}
void LTPopBack(LTNode* phead)
{
assert(phead);
assert(!LTEmpty(phead));
LTErase(phead->prev);
/*LTNode* tail = phead->prev;
LTNode* tailPrev = tail->prev;
free(tail);
tailPrev->next = phead;
phead->prev = tailPrev;*/
}
//void LTPopFront(LTNode* phead)
//{
// assert(phead);
// assert(!LTEmpty(phead));
//
// LTNode* next = phead->next;
//
// /*phead->next = next->next;
// phead->next->prev = phead;
// free(next);*/
//
// phead->next = next->next;
// next->next->prev = phead;
// free(next);
//}
void LTPopFront(LTNode* phead)
{
assert(phead);
assert(!LTEmpty(phead));
LTErase(phead->next);
//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->next;
while (cur != phead)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
// 在pos之前插入
void LTInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* prev = pos->prev;
LTNode* newnode = BuyLTNode(x);
// prev newnode pos
prev->next = newnode;
newnode->prev = prev;
newnode->next = pos;
pos->prev = newnode;
}
// 删除pos位置的值
void LTErase(LTNode* pos)
{
assert(pos);
LTNode* posPrev = pos->prev;
LTNode* posNext = pos->next;
posPrev->next = posNext;
posNext->prev = posPrev;
free(pos);
}
void LTDestroy(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
}
#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);
#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;
}