1,简介
在学习双链表时我们经常会遇到一些问题,大部分问题是双链表的表达与单链表不同而造成的,所以我用这篇文章来简要地写出双链表的基本操作,为大家提供参考。
2,定义基本操作
放在头文件进行定义
#pragma once
# include<stdio.h>
# include<stdlib.h>
# include<assert.h>
# include<malloc.h>
typedef int LTDataType;
typedef struct ListNode
{
LTDataType _data;
struct ListNode* _next;
struct ListNode* _prev;
}ListNode;
// 创建返回链表的头结点.
ListNode* ListCreate();
// 双向链表销毁
void ListDestory(ListNode* pHead);
// 双向链表打印
void ListPrint(ListNode* pHead);
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* pHead);
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* pHead);
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);
3,双链表的基本操作
1,引用头文件
# include"标头.h"
2,创建返回链表的头结点
在创建返回链表的头节点之前我们可以写一个创建结点的函数,以便后续使用
ListNode* BuyLTNode(LTDataType x)
{
ListNode* newnode = (ListNode *)malloc(sizeof(ListNode));//malloc函数创建动态内存
if (newnode == NULL)
{
perror("malloc fail");
return NULL;
}
newnode->_data = x;
newnode->_next = NULL;
newnode->_prev = NULL;
return newnode;
}
ListNode* ListCreate()
{
ListNode* phead = BuyLTNode(-1);
phead->_next = phead;
phead->_prev = phead;
return phead;
}
创建好后就可以创建头节点了。
3,双向链表销毁
在我们使用完后我们需要销毁创建的链表来释放内存
void ListDestory(ListNode* pHead)
{
assert(pHead);
ListNode* cur = pHead->_next;
while (cur != pHead)
{
ListNode* next = cur->_next;
free(cur);
cur = next;
}
free(pHead);
}
4,双向链表打印
打印双链表可谓是最简单的操作了,只需要循环下去即可
void ListPrint(ListNode* pHead)
{
assert(pHead);//提醒pHead是否为空,为空就会报错警告
ListNode* p = pHead;
while (p->_next != pHead)
{
printf("%d ", p->_data);
p = p->_next;
}
}
5,双向链表尾插
双链表的尾插比较方便不需要和单链表一样遍历整个链表,利用双链表的循环性,我们只需要找到头节点的上一个就可以找到尾结点
void ListPushBack(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* p = pHead->_prev;
ListNode* newnode=BuyLTNode(x);
p->_next = newnode;
newnode->_prev = p;
newnode->_next = pHead;
pHead->_prev = p;
}
6,双向链表尾删
尾删思路和尾插思路差不多
void ListPopBack(ListNode* pHead)
{
assert(pHead);
assert(pHead->_next != pHead);//链表为空发出警告
ListNode* p = pHead->_prev;
p->_prev->_next = pHead;
pHead->_prev = p->_prev;
free(p);
}
7,双向链表头插
找到头结点的下一个进行插入
void ListPushFront(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* p = pHead->_next;
ListNode* newnode = BuyLTNode(x);
p->_prev = newnode;
newnode->_next = p;
newnode->_prev = pHead;
pHead->_next = newnode;
}
8,双向链表头删
与头插思路差不多
void ListPopFront(ListNode* pHead)
{
assert(pHead);
assert(pHead->_next != pHead);
ListNode* p = pHead->_next;
ListNode* pp = pHead->_next->_next;
free(p);
pHead->_next = pp;
pp->_prev = pHead;
}
9,双向链表查找
利用循环找出值一样的结点
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* p = pHead->_next;
while (p != pHead)
{
if (p->_data == x)
{
return p;
}
p = p->_next;
}
return NULL;
}
10,双向链表在pos的前面进行插入
与头插思路差不多,只是把头结点的位置改为pos的位置
void ListInsert(ListNode* pos, LTDataType x)
{
assert(pos);
ListNode* p = pos->_prev;
ListNode* newnode = BuyLTNode(x);
newnode->_prev = p;
p->_next = newnode;
newnode->_next = pos;
pos->_prev = newnode;
}
11,双向链表删除pos位置的节点
与插入思路差不多
void ListErase(ListNode* pos)
{
assert(pos);
assert(pos->_next != pos);
ListNode* p = pos->_prev;
p->_next = pos->_next;
pos->_next->_prev = p;
free(pos);
}