2.1创建结构体变量
2.2创建新结点
2.3初始化链表
2.4打印结点数据
2.5头插数据
2.6尾插数据
2.7头删数据
2.8尾删数据
2.9查找数据
2.10随机插入数据
2.11随机删除数据
2.12计算链表元素个数
2.13销毁链表
一、基本结构
首先要明白双向链表里面包含什么:“双向”两个方向,用两个指针,一个头指针Prev和一个尾指针next ,如下图结点:
头结点不包含数据,后续结点用data存放数据
二、功能实现以及效果展示
菜单展示:
1.创建结构体变量,成员变量包含头结点prev和数据data以及尾指针next
typedef struct ListNode
{
struct ListNode* prev;
struct ListNode* next;
LTDataType data;
}ListNode;
2.创建新结点newnode,返回newnode的地址
使用malloc创建新结点,然后使其头指针和为指针为NULL
ListNode* BuyListNode(LTDataType x)
{
ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
if (newnode == NULL)
{
perror("");
return;
}
else
{
newnode->data = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
}
3.初始化双向链表
为了避免使用二级指针,可以在初始化双向链表时,返回头结点的地址。让ListNode*类型的指针plist存放头结点的地址
ListNode* plist = ListInit();
初始化双向链表:
ListNode* ListInit()
{
ListNode* phead = BuyListNode(0);
phead->next = phead;
phead->prev = phead;
return phead;
}
要让头结点的头指针和尾指针指向自己
4.打印结点数据
定义一个指针cur,用来指向头结点的后继节点,然后从该位置开始打印,每次打印完该结点的数据后,就使指针cur指向下一个结点,直到cur指向的结点是头结点时结束
void ListPrint(ListNode* phead)
{
ListNode* cur = phead->next;
while (cur != phead)
{
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
}
5.头插数据
定义头结点的后继节点为First,这样就避免了在插入数据时考虑指针顺序,创建一个新结点newnode,然后让newnode的尾结点指向First,再让First的头指针指向newnode,再连接newnode和头结点即可。
void ListPushFront(ListNode* phead, LTDataType x)
{
assert(phead);
ListNode* First = phead->next;
ListNode*newnode= BuyListNode(x);
phead->next = newnode;
newnode->prev = phead;
newnode->next = First;
First->prev = newnode;
/*ListInsert(phead->next, x);*/
}
头插法以及打印数据效果展示
6.尾插数据
头结点的前驱结点就是尾结点,所以,直接定义一个指向头结点的指针tail 然后创建一个新结点,存放要插入的数据,直接将newnode和tail 以及 newnode和头结点相连
void ListPushBack(ListNode* phead,LTDataType x)
{
//头结点phead的前驱结点就是尾结点
assert(phead);
ListNode* tail = phead->prev;
ListNode*newnode= BuyListNode(x);
newnode->prev = tail;
tail->next = newnode;
phead->prev = newnode;
newnode->next = phead;
/*ListInsert(phead, x);*/
}
尾插数据效果展示
7.头删数据
创建一个指针Second,让该指针先存放第二个结点的地址,避免第一个结点Frist被释放后,第二个结点找不到,先让头结点的尾结点指向Second,然后让Second的头指针指向头结点,然后释放第一个结点First
void ListPopFront(ListNode* phead)
{
assert(phead);
assert(phead->next != phead);
ListNode* First = phead->next;
ListNode* Second = First->next;
phead->next = Second;
Second->prev = phead;
free(First);
First = NULL;
//ListErase(phead->next);
}
头删数据效果展示
8.尾删数据
定义一个指针tail存放头结点的前驱结点也就是尾结点的地址,然后找到tail的前驱结点prev,然后将prev的尾指针指向头结点phead,然后phead的头指针指向prev,释放掉tail即可
void ListPopBack(ListNode* phead)
{
assert(phead);
assert(phead->next != phead);
ListNode* tail = phead->prev;
ListNode* prev = tail->prev;
prev->next = phead;
phead->prev = prev;
free(tail);
tail = NULL;
/*ListErase(phead->prev);*/
}
尾插数据效果展示
9.查找数据(效果展示与10和11搭配实现)
定义一个指针cur从第一个结点(非头结点)开始查询,如果找到了,就返回该结点的地址,如果找不到就返回NULL
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;
}
10.随机插入数据
一般与查找数据连用,定义一个指针pos接受查询到的数据的地址,然后再pos指向的结点的前面插入一个结点,创建一个新结点newnode,存放要插入的数据,然后找到pos的前驱结点prev,使prev的尾结点指向newnode,newnode的头结点指向prev,然后再将newnode跟pos相连
这里的data那里应该是数据x,之前的应该也是,画到一半才想起来QAQ
其实细心的UU可以观察到,头插数据和尾插数据都可以调用随机插入函数,只是头插数据的时候pos是phead->next 尾插数据时pos就是phead
头插 ListInsert(phead->next, x);
尾插 ListInsert(phead, x);
void ListInsert(ListNode* pos, LTDataType x)
{
assert(pos);
ListNode* newnode = BuyListNode(x);
ListNode* prev = pos->prev;
prev->next = newnode;
newnode->prev = prev;
newnode->next = pos;
pos->prev = newnode;
}
随机插入效果展示
11.随机删除数据
定义一个pos指针接受查询到的数据的地址,然后在定义一个指针prev指向pos的前驱结点,定义一个指针next指向pos的后继结点,然后让prev的尾指针指向next,再让next的头指针指向prev,最后释放掉pos即可
其实头删数据、尾删数据都可以通过随机删除实现
头删数据:ListErase(phead->next)
尾删数据:Li
void ListErase(ListNode* pos) { assert(pos); ListNode* prev = pos->prev; ListNode* next = pos->next; prev->next = next; next->prev = prev; free(pos); pos = NULL; }
stErase(phead->prev);
随机删除效果展示
12.修改数据
先找到想要修改的数据,然后再将要修改的数据输入即可
printf("请输入你想要修改的数据:\n");
scanf("%d", &x);
ListNode * pos2 = ListFind(plist, x);
if (pos2 == NULL)
{
printf("该数据不存在,请重新进行操作!\n");
}
else
{
printf("该数据存在,请输入数据进行修改:\n");
scanf("%d", &x);
pos2->data = x;
printf("修改成功!\n");
}
break;
效果展示
13.计算链表数据个数
定义一个指针cur指向头结点的后继结点,定义一个变量count计算数据个数,让cur依次向后移动,每次让count++;直到cur指向头结点时结束
int ListSize(ListNode* phead)
{
assert(phead);
ListNode* cur = phead->next;
int count = 0;
while (cur != phead)
{
count++;
cur = cur->next;
}
return count;
}
效果展示
14.销毁双向链表
定义一个指针cur指向第一个结点,然后从第一个结点开始释放,每次定义一个指针next指向cur的后一个结点,然后将cur释放,再将next的地址给cur,直到只剩下头结点,最后将头结点也释放
void ListDestory(ListNode* phead)
{
assert(phead);
ListNode* cur = phead->next;
while (cur != phead)
{
ListNode* next = cur->next;
free(cur);
cur = NULL;
cur = next;
}
free(phead);
phead = NULL;
}
如有问题评论就好,看到就会回嗷~ UU们如有帮助,点点赞呐~
头文件:List.h(函数声明)
//头文件
#pragma once
#include
#include
#include
typedef int LTDataType;
typedef struct ListNode
{
struct ListNode* prev;
struct ListNode* next;
LTDataType data;
}ListNode;
//初始化双向链表
ListNode* ListInit();
//打印双向链表
void ListPrint(ListNode* phead);
//销毁双向链表
void ListDestory(ListNode* phead);
//尾插法
void ListPushBack(ListNode* phead, LTDataType x);
//头插法
void ListPushFront(ListNode* phead, LTDataType x);
//尾删
void ListPopBack(ListNode* phead);
//头删
void ListPopFront(ListNode* phead);
//查找数据
ListNode*ListFind(ListNode* phead, LTDataType x);
//随机插入数据
void ListInsert(ListNode* pos, LTDataType x);
//随机删除数据
void ListErase(ListNode* pos);
//计算双向链表中的数据个数
int ListSize(ListNode* phead);
源文件List.c(函数功能实现)
#include"List.h"
//创建新结点
ListNode* BuyListNode(LTDataType x)
{
ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
if (newnode == NULL)
{
perror("");
return;
}
else
{
newnode->data = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
}
//初始化双向链表
ListNode* ListInit()
{
ListNode* phead = BuyListNode(0);
phead->next = phead;
phead->prev = phead;
return phead;
}
//打印双向链表
void ListPrint(ListNode* phead)
{
ListNode* cur = phead->next;
while (cur != phead)
{
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
}
//尾插法
void ListPushBack(ListNode* phead,LTDataType x)
{
//头结点phead的前驱结点就是尾结点
assert(phead);
ListNode* tail = phead->prev;
ListNode*newnode= BuyListNode(x);
newnode->prev = tail;
tail->next = newnode;
phead->prev = newnode;
newnode->next = phead;
/*ListInsert(phead, x);*/
}
//头插法
void ListPushFront(ListNode* phead, LTDataType x)
{
assert(phead);
ListNode* First = phead->next;
ListNode*newnode= BuyListNode(x);
phead->next = newnode;
newnode->prev = phead;
newnode->next = First;
First->prev = newnode;
/*ListInsert(phead->next, x);*/
}
//尾删
void ListPopBack(ListNode* phead)
{
assert(phead);
assert(phead->next != phead);
ListNode* tail = phead->prev;
ListNode* prev = tail->prev;
prev->next = phead;
phead->prev = prev;
free(tail);
tail = NULL;
/*ListErase(phead->prev);*/
}
//头删
void ListPopFront(ListNode* phead)
{
assert(phead);
assert(phead->next != phead);
ListNode* First = phead->next;
ListNode* Second = First->next;
phead->next = Second;
Second->prev = phead;
free(First);
First = NULL;
//ListErase(phead->next);
}
//查找数据
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;
}
//随机插入数据 在pos的前面插入数据
void ListInsert(ListNode* pos, LTDataType x)
{
assert(pos);
ListNode* newnode = BuyListNode(x);
ListNode* prev = pos->prev;
prev->next = newnode;
newnode->prev = prev;
newnode->next = pos;
pos->prev = newnode;
}
//随机删除数据 删除pos位置的数据
void ListErase(ListNode* pos)
{
assert(pos);
ListNode* prev = pos->prev;
ListNode* next = pos->next;
prev->next = next;
next->prev = prev;
free(pos);
pos = NULL;
}
//计算双向链表中的元素个数
int ListSize(ListNode* phead)
{
assert(phead);
ListNode* cur = phead->next;
int count = 0;
while (cur != phead)
{
count++;
cur = cur->next;
}
return count;
}
//销毁双向链表
void ListDestory(ListNode* phead)
{
assert(phead);
ListNode* cur = phead->next;
while (cur != phead)
{
ListNode* next = cur->next;
free(cur);
cur = NULL;
cur = next;
}
free(phead);
phead = NULL;
}
源文件text.c(功能测试)
#include"List.h"
void menu()
{
printf("*******************************************\n");
printf("**1.尾插数据 2.头插数据\n");
printf("**3.尾删数据 4.头删数据\n");
printf("**5.随机插入 6.随机删除\n");
printf("**7.修改数据 8.求结点个数\n");
printf("**9.打印链表 0.退出程序\n");
printf("*******************************************\n");
}
void text()
{
ListNode* plist = ListInit();
int input = 0;
int i = 0;
LTDataType x = 0;
do
{
menu();
scanf("%d", &input);
switch (input)
{
case 1:
printf("请输入你想插入的数据,以-1结束\n");
do
{
scanf("%d", &x);
if (x != -1)
{
ListPushBack(plist, x);
}
} while (x != -1);
printf("插入成功\n");
break;
case 2:
printf("请输入你想插入的数据,以-1结束\n");
do
{
scanf("%d", &x);
if (x != -1)
{
ListPushFront(plist, x);
}
} while (x != -1);
printf("插入成功\n");
break;
case 3:
ListPopBack(plist);
printf("删除成功!\n");
break;
case 4:
ListPopFront(plist);
printf("删除成功!\n");
break;
case 5:
printf("请先输入你要插入的位置的当前数据:\n");
scanf("%d", &x);
ListNode* pos = ListFind(plist, x);
if (pos == NULL)
{
printf("该数据不存在,请重新选择随机插入选项进行操作!\n");
}
else
{
printf("该数据存在,请输入您要插入的数据:\n");
scanf("%d", &x);
ListInsert(pos, x);
printf("插入成功!\n");
}
break;
case 6:
printf("请先输入你要删除的位置的当前数据:\n");
scanf("%d", &x);
ListNode* pos1 = ListFind(plist, x);
if (pos1 == NULL)
{
printf("该数据不存在,请重新选择随机删除选项进行操作!\n");
}
else
{
ListErase(pos1);
printf("删除成功!\n");
}
break;
case 7:
printf("请输入你想要修改的数据:\n");
scanf("%d", &x);
ListNode * pos2 = ListFind(plist, x);
if (pos2 == NULL)
{
printf("该数据不存在,请重新进行操作!\n");
}
else
{
printf("该数据存在,请输入数据进行修改:\n");
scanf("%d", &x);
pos2->data = x;
printf("修改成功!\n");
}
break;
case 8:
i=ListSize(plist);
printf("结点个数为%d个\n", i);
break;
case 9:
ListPrint(plist);
break;
case 0:
printf("退出程序!\n");
break;
default:
printf("输入的数字有误,请重新选择\n");
break;
}
} while (input);
}
int main()
{
text();
return 0;
}