目录
链表的种类
(1)单向,双向
(2)带头,不带头(带有哨兵头的链表,不存放任何数据)
(3)循环,不循环(头尾相连)
了解完链表的基本的类别后我们来看看今天的主角——“双向”“带头”“循环”链表
“双向”
所谓双向,就是一个结点中存储除数据外还存储两个指针,一个指针指向下一个结点,一个指针指向上一个结点
typedef struct Dlist//一个结点
{
struct Dlist* prev;//指向上一个结点
struct Dlist* next;//指向下一个结点
int data;//数据
}Dlist;
“带头”
带头就是一个链表中的头是一个哨兵头,里面不存储任何数据
“循环”
所谓循环,就是将链表的头尾相连
Dlist* newnode =(Dlist* ) malloc(sizeof(Dlist));
newnode->next = newnode;
newnode->prev = newnode;
了解完成后我们来正式开始实现这个“完美链表”的增删查改
1.增
在增加前我们先来写一个申请结点的函数
Dlist* Buynewnode(int x)
{
Dlist* newnode =(Dlist* ) malloc(sizeof(Dlist));
newnode->next = newnode;
newnode->prev = newnode;
newnode->data = x;
return newnode;
}
1.1 头增
头增就是在链表的第一个结点(哨兵头后面的那个结点)前插入一个新结点
思路:
找到第一个结点phead->next拷贝到临时first里面(哨兵头后的第一个结点)
申请新节点
将新节点的next连接到first(原第一节点)
first的prve连接到newnode
新节点的prev连接到phead
phead的next连接到newnode
参考代码如下
void Dlistpushfront(Dlist* phead, int x)
{
Dlist* first = phead->next;//储存第一个结点的位置
Dlist* newnode = Buynewnode(x);
newnode->next = first;
newnode->prev = phead;
first->prev = newnode;
phead->next = newnode;
}
1.2 尾增
思路:找到尾结点,在尾节点后插入一个新结点
利用phead的prev找到尾结点放入tail中
将新节点的尾与phead连接
新结点的prev与tail连接
tail的尾与新节点连接
phead的prev与新节点连接
参考代码
void Dlistpushback(Dlist* phead, int x)
{
assert(phead);
Dlist* tail = phead->prev;
Dlist* newnode = Buynewnode(x);
tail->next = newnode;
newnode->next = phead;
newnode->prev = tail;
phead->prev = newnode;
}
1.3 pos增(任意位置前面增)
思路:找到pos前的结点记做prev
新申请结点头与prev连接,尾与pos连接
prev的尾与newnode连接,pos的头与newnode连接
参考代码如下:
void DlistInsert(Dlist* phead, Dlist* pos, int x)
{
Dlist* prev = pos->prev;
Dlist* newnode = Buynewnode(x);
prev->next = newnode;
pos->prev = newnode;
newnode->prev = prev;
newnode->next = pos;
}
2.删
2.1尾删
思路:
找到尾结点的前一节点记做prev,尾结点记做tail
将prev的尾指向哨兵头,哨兵头的头指向prev
释放尾结点tail
参考代码
void Dlistpopback(Dlist* phead)
{
assert(phead->next != phead);
Dlist* tail = phead->prev;
Dlist* prev = tail->prev;
phead->prev = prev;
prev->next = phead;
free(tail);
tail = NULL;
}
2.2头删
思路:找到第一个结点记做first(通过哨兵头的next)
找到第二个结点(将要作为新的第一个结点)(通过first的next)
连接后释放first即可
void Dlistpopfront(Dlist* phead)
{
assert(phead->next != NULL);
Dlist* first = phead->next;
Dlist* second = first->next;
phead->next = second;
second->prev = phead;
free(first);
first = NULL;
}
2.3pos删
找到要删除的位置的前一个和后一个,将他们连接起来,释放掉pos即可
void Dlistrese(Dlist* phead, Dlist* pos)
{
Dlist* prev = pos->prev;
Dlist* next = pos->next;
prev->next = next;
next->prev = prev;
free(pos);
pos = NULL;
}
3.查与改
3.1查
查找需要遍历各节点,所以我们需要用到循环。
因为我们的链表是循环链表,所以当我们查找完一圈后就回到了哨兵头此时循环结束
Dlist* DindNode(Dlist* phead, int x)
{
Dlist* cur = phead->next;//第一个结点
while (cur != phead)//遍历完一遍后cur会回到哨兵头此时循环结束
{
if (cur->data == x)
{
return cur;//找到了返回该节点的地址
}
cur = cur->next;
}
return NULL;//循环结束后没有返回说明没有找到,此时返回NULL
}
3.2改
改就是在查找的基础上来进行修改
Dlist* pos=DindNode(phead, 8);
if (pos)
{
DlistInsert(phead, pos, 857);
}