写在前面:双向链表和单链表非常相似,会单链表的人完全可以不用看这篇文章。看过鄙人的单链表的话可以只看下面提及的几行代码,其余代码是完全一致的。
1.双向链表与单链表在结构定义上非常相似,只是双向链表比单链表多一个指针域指向该结点的直接前驱。(参考Two-way_Linked_List.h中的结构体定义第8行)
2.双向链表与单链表在大部分的基本操作上也非常相似,只是双向链表在头插法、尾插法、插入结点、删除结点操作上需要对前指针域进行更改。(参考Two-way_Linked_List.c中的Create_Head函数第21、22行、Create_Behind函数第41行、Insert_List函数146行、Delete_List函数165行)
3.因此,双向链表的测试函数可以直接沿用单链表的测试函数。
一、实验目的
1.掌握双向链表的定义。
2.掌握双向链表和单链表的区别和联系。
3.掌握双向链表的初始化、头插法、尾插法、打印、判断空表、清空链表、求表长、按址查找、按值查找、插入、删除等基本操作。
4. 了解双向链表和单链表的优缺点,能在不同情况下选择合适的存储方式。
二、实验要求
1. 写出双向链表的操作的详细算法步骤;
2. 使用C/C++实现双向链表,并编译运行。
三、实验过程
实验环境:visual studio 2017
实验步骤:
1.初始化链表L,定义函数名为Init_List;
2.头插法创建链表L,定义函数名为Create_Head;
3.尾插法创建链表L,定义函数名为Create_Behind;
4.打印链表L,定义函数名为Print_List;
5.判断链表L是否为空表,定义函数名为Empty_List;
6.清空链表L,定义函数名为Clear_List;
7.求链表L表长,定义函数名为Length_List;
8.查找第location个结点的元素值,定义函数名为Get_Data;
9.查找第一个值为e的元素的位置,定义函数名为Locate_Data;
10.在顺序表L第location个位置插入一个元素e,定义函数名为Insert_List;
11.将顺序表L第location个元素删除,定义函数名为Delete_List;
Two-way_Linked_List.h
#include<stdio.h> //调用头文件
#include<stdlib.h> //调用头文件
#define ELEMTYPE int //用#define定义一个标识符ELEMTYPE表示int
typedef struct Lnode //构建一个结构体存储链表信息
{
ELEMTYPE data; //数据域——存储元素值
struct Lnode* prior; //前指针域——指向该结点的直接前驱
struct Lnode* next; //指针域——指向该结点的直接后继
}LNode,*List; //LNode为结构体类型别名,List为结构体指针
void Init_List(List* L); //声明初始化函数-Init_List
void Create_Head(List* L); //声明头插法创建链表函数-Create_Head
void Create_Behind(List* L); //声明尾插法创建链表函数-Create_Behind
void Print_List(List L); //声明打印函数-Print_List
bool Empty_List(List L); //声明判断空表函数-Empty_List
void Clear_List(List* L); //声明清空函数-Clear_List
int Length_List(List L); //声明求表长函数-Length_List
void Get_Data(List L, int location, ELEMTYPE* e);
//声明按位置查找元素值函数-Get_Data
int Locate_Data(List L, ELEMTYPE e);
//声明按值查找元素位置函数-Locate_Data
void Insert_List(List* L, int i, ELEMTYPE e); //声明插入函数-Insert_List
void Delete_List(List* L, int i); //声明删除函数-Delete_List
Two-way_Linked_List.c
#include "Linked_List.h" //调用头文件
void Init_List(List* L) //初始化链表L
{
(*L) = (List)malloc(sizeof(LNode)); //用malloc给链表L分配一块内存
(*L)->prior = (*L)->next = NULL; //将头指针置为空
}
void Create_Head(List* L) //头插法创建链表L
{
int length; //定义整型变量length表示链表L表长
printf("Please input the length:"); //提示输入表长length
scanf("%d", &length); //输入表长length
for (int n = 1; n <= length; n++) //向链表L中添加n个结点
{
List p; //定义结构体指针类型变量p
p = (List)malloc(sizeof(LNode)); //用malloc给p分配一块内存
printf("Please input the data:"); //提示输入结点p的数据域
scanf("%d", &p->data); //输入结点p的数据域
p->next = (*L)->next; //p作为新的首元结点,后指针域指向原来的首元结点
(*L)->next->prior = p; //原来的首元结点的前指针域指向新首元结点-p
p->prior = (*L); //前指针域指向头结点
(*L)->next = p; //更新首元结点
}
}
void Create_Behind(List* L) //尾插法创建链表L
{
int length; //定义整型变量length表示链表L长度
List rear = (*L); //rear指向尾结点,初始时指向头结点
printf("Please input the length:"); //提示输入表长length
scanf("%d", &length); //输入表长length
for (int n = 1; n <= length; n++) //向链表L中添加n个结点
{
List p; //定义结构体指针类型变量p
p = (List)malloc(sizeof(LNode)); //用malloc给p分配一块内存
printf("Please input the data:"); //提示输入结点p的数据域
scanf("%d", &p->data); //输入结点p的数据域
p->next = NULL; //p为尾结点,后指针域为空
p->prior = rear; //p的前指针域指向原来的尾结点
rear->next = p; //原来的尾结点rear的后指针域指向现在的尾结点p
rear = p; //更新尾结点
}
}
void Print_List(List L) //打印链表L
{
List p = L; //定义结构体指针类型变量p为头结点
printf("The linked list is:"); //提示输出链表L
while (p->next != NULL) //扫描链表L
{
p = p->next; //当前p为原来p的后继结点
printf("%d ", p->data); //打印p的数据域
}
printf("\n"); //换行
}
bool Empty_List(List L) //判断链表是否为空
{
if (L->next == NULL) //链表为空
{
printf("This list is empty.\n"); //表示链表L为空
return true; //返回true
}
else //链表L不为空
{
printf("This list isn't empty.\n"); //表示链表L不为空
return false; //返回false
}
}
void Clear_List(List* L) //清空链表L
{
List p = (*L)->next,q; //定义结构体指针变量q,p为首元结点
while (p != NULL) //扫描链表
{
q = p->next; //q为p的后继结点
free(p); //释放结点p所占空间
p = NULL; //防止出现悬空指针(失效指针)
p = q; //当前p为原来p的后继结点
}
(*L)->next = NULL; //清空链表L后头指针为空
}
int Length_List(List L) //求链表L表长(元素个数)
{
int length = 0; //定义整型变量length记录链表表长
L = L->next; //L为首元结点
while (L != NULL) //扫描链表L
{
length++; //表长length加1
L = L->next; //当前L为原来L的后继结点
}
return length; //返回链表L表长
}
void Get_Data(List L, int location,ELEMTYPE* e)
//从首元结点开始查找第location个结点并将该结点的数据域赋值给e(按位置查找元素值)
{
int length = (Length_List(L)); //定义整型变量length表示链表L表长
if (location<1 || location>length) //判断location的值是否合法
{
printf("get failed\n"); //提示location的值不合法
return; //结束函数
}
else //合法
{
List p = L; //定义结构体指针类型变量p
for (int j = 0; j < location; j++) //扫描链表顺序存取
{
p = p->next; //当前p为原来p的后继结点
}
(*e) = p->data; //将第location个结点的数据域赋值给e
}
}
int Locate_Data(List L, ELEMTYPE e) //查找元素e在链表L中的位置(按值查找元素位置)
{
List p=L; //定义结构体指针类型变量p
for (int location = 1; location <= Length_List(L); location++) //扫描链表L
{
p = p->next; //当前p指向原来p的后继结点
if (p->data == e) //判断p指向的结点的数据域与e是否相等
return location; //相等则返回元素e的位置location
}
printf("locate failed\n"); //提示查找失败
return 0; //查找失败返回0
}
void Insert_List(List* L, int location, ELEMTYPE e) //在第location个结点上插入元素e
{
if (location > Length_List(*L) + 1 || location < 1) //判断location的值是否合法
{
printf("insert failed\n"); //location的值非法
return; //退出函数
}
List p = (*L); //定义结构体指针类型变量p
for (int n = 1; n < location; n++)//查找链表的第location-1个结点
{
p = p->next; //当前p为原来p的后继结点
}
List q; //定义结构体指针类型变量q
q = (List)malloc(sizeof(LNode)); //用malloc给q分配一块内存
q->data = e; //元素e存放至结点q的数据域
q->prior = p; //结点q的前指针域指向第location-1个结点p
q->next = p->next; //p的后继结点存放至q的后指针域
p->next = q; //q为p的后继结点
}
void Delete_List(List* L, int location) //删除第location个结点
{
if (location<1 || location>Length_List(*L)) //判断location的值是否合法
{
printf("delete failed\n"); //location的值非法
return; //退出函数
}
List p = (*L); //定义结构体指针类型变量p
for (int n = 1; n < location ; n++) //查找链表的第location-1个结点
{
p = p->next; //当前p为原来p的后继结点
}
List q = p->next; //定义结构体指针类型变量q为第location个结点
p->next = q->next; //第location-1个结点的后指针域改为第location个结点的后继结点
q->next->prior = q->prior; //第location+1个结点的前指针域指向第location-1个结点
free(q); //释放第location个元素的空间
q = NULL; //防止出现悬空指针(无效指针)
}
以下为测试函数
1.测试初始化函数Init_List;头插法函数Create_Head;打印函数Print_List.
#include "Linked_List.h" //调用头文件
int main(void) //main()函数
{
List L; //定义结构体指针变量L
Init_List(&L); //调用Init_List函数将链表L初始化
Create_Head(&L); //调用Create_Head函数用头插法创建链表L
Print_List(L); //调用Print_List函数打印链表L
return 0; //返回值
}
2.测试尾插法函数Create_Behind.
#include "Linked_List.h" //调用头文件
int main(void) //main()函数
{
List L; //定义结构体指针变量L
Init_List(&L); //调用Init_List函数将链表L初始化
Create_Behind(&L); //调用Create_Behind函数用尾插法创建链表L
Print_List(L); //调用Print_List函数打印链表L
return 0; //返回值
}
3.测试判断空表函数Empty_List.
#include "Linked_List.h" //调用头文件
int main(void) //main()函数
{
List L; //定义结构体指针变量L
Init_List(&L); //调用Init_List函数将链表L初始化
Empty_List(L); //调用Empty_List函数判断链表L是否为空
Create_Behind(&L); //调用Create_Behind函数用尾插法创建链表L
Empty_List(L); //调用Empty_List函数判断链表L是否为空
return 0; //返回值
}
4.测试清空链表函数Clear_List.
#include "Linked_List.h" //调用头文件
int main(void) //main()函数
{
List L; //定义结构体指针变量L
Init_List(&L); //调用Init_List函数将链表L初始化
Create_Behind(&L); //调用Create_Behind函数用尾插法创建链表L
Empty_List(L); //调用Empty_List函数判断链表L是否为空
Clear_List(&L); //调用Clear_List函数清空链表L
Empty_List(L); //调用Empty_List函数判断链表L是否为空
return 0; //返回值
}
5.测试求表长函数Length_List.
#include "Linked_List.h" //调用头文件
int main(void) //main()函数
{
List L; //定义结构体指针变量L
Init_List(&L); //调用Init_List函数将链表L初始化
Create_Head(&L); //调用Create_Head函数用头插法创建链表L
printf("The length of this list is:%d\n", Length_List(L));
//输出表长
return 0; //返回值
}
6.测试按址查找函数Get_Data.
#include "Linked_List.h" //调用头文件
int main(void) //main()函数
{
List L; //定义结构体指针变量L
Init_List(&L); //调用Init_List函数将链表L初始化
Create_Head(&L); //调用Create_Head函数用头插法创建链表L
Print_List(L); //调用Print_List函数打印链表L
ELEMTYPE e; //定义查找到的元素e
int location; //定义待查找位置location
printf("Please input the location of the element you want to get:"); //提示输入待查找位置location
scanf("%d", &location); //输入待查找位置location
Get_Data(L,location,&e); //调用Get_Data函数按址查找
printf("The element is:%d", e); //输出查找到的元素e
return 0; //返回值
}
7.测试按值查找函数Locate_List
#include "Linked_List.h" //调用头文件
int main(void) //main()函数
{
List L; //定义结构体指针变量L
Init_List(&L); //调用Init_List函数将链表L初始化
Create_Behind(&L); //调用Create_Behind函数用头插法创建链表L
Print_List(L); //调用Print_List函数打印链表L
ELEMTYPE e; //定义待查找结点的数据域e
printf("Please input the element you want to search:");
//提示输入待查找结点的数据域e
scanf("%d", &e); //输入带查找元素e
printf("The location of this element is:%d", Locate_Data(L, e));
//调用Locate_Data函数按值查找结点位置并输出
return 0; //返回值
}
8.测试插入函数Insert_List
#include "Linked_List.h" //调用头文件
int main(void) //main()函数
{
List L; //定义结构体指针变量L
Init_List(&L); //调用Init_List函数将链表L初始化
Create_Behind(&L); //调用Create_Behind函数用头插法创建链表L
Print_List(L); //调用Print_List函数打印链表L
ELEMTYPE e; //定义待查找结点的数据域e
printf("Please input the element you want to search:"); //提示输入待查找结点的数据域e
scanf("%d", &e); //输入带查找元素e
printf("The location of this element is:%d", Locate_Data(L, e));
//调用Locate_Data函数按值查找结点位置并输出
return 0; //返回值
}
9.测试删除函数Delete_List
#include "Linked_List.h" //调用头文件
int main(void) //main()函数
{
List L; //定义结构体指针变量L
Init_List(&L); //调用Init_List函数将链表L初始化
Create_Behind(&L); //调用Create_Behind函数用头插法创建链表L
Print_List(L); //调用Print_List函数打印链表L
int location; //定义待删除结点位置location
printf("The location of the element to be deleted is:");
//提示输入删除结点的位置
scanf("%d", &location); //输入需要删除结点的位置
Delete_List(&L, location);
//调用Delete_List函数删除第location个结点
Print_List(L); //调用Print_List函数打印链表L
printf("The length of this list is:%d\n", Length_List(L));
//输出表长
return 0; //返回值
}
四、实验结果及分析
测试函数 | 测试用例 | 运行结果 |
Init_List Creat_Head Print_List | 5 1,2,3,4,5 | Please input the length:5 Please input the data:1 Please input the data:2 Please input the data:3 Please input the data:4 Please input the data:5 The linked list is:5 4 3 2 1 |
-1 | Please input the length:-1 The linked list is: | |
Create_Behind | 5 1,2,3,4,5 | Please input the length:5 Please input the data:1 Please input the data:2 Please input the data:3 Please input the data:4 Please input the data:5 The linked list is:1 2 3 4 5 |
0 | Please input the length:0 The linked list is: | |
Empty_List | 5 1,2,3,4,5 | This list is empty. Please input the length:5 Please input the data:1 Please input the data:2 Please input the data:3 Please input the data:4 Please input the data:5 This list isn't empty. |
0 | This list is empty. Please input the length:0 This list is empty. | |
Clear_List | 5 1,2,3,4,5 | Please input the length:5 Please input the data:1 Please input the data:2 Please input the data:3 Please input the data:4 Please input the data:5 This list isn't empty. This list is empty. |
-1 | Please input the length:-1 This list is empty. This list is empty. | |
Length_List | 5 1,2,3,4,5 | Please input the length:5 Please input the data:1 Please input the data:2 Please input the data:3 Please input the data:4 Please input the data:5 The length of this list is:5 |
-1 | Please input the length:-1 The length of this list is:0 | |
Get_Data | 5 1,2,3,4,5 5 | Please input the length:5 Please input the data:1 Please input the data:2 Please input the data:3 Please input the data:4 Please input the data:5 The linked list is:5 4 3 2 1 Please input the location of the element you want to get:5 The element is:1 |
6 | Please input the location of the element you want to get:6 get failed The element is:-858993460 | |
0 | Please input the location of the element you want to get:0 get failed The element is:-858993460 | |
Locate_Data | 5 1,2,3,4,5 4 | Please input the length:5 Please input the data:1 Please input the data:2 Please input the data:3 Please input the data:4 Please input the data:5 The linked list is:1 2 3 4 5 Please input the element you want to search:4 The location of this element is:4 |
6 | Please input the element you want to search:6 locate failed The location of this element is:0 | |
Insert_List | 5 1,2,3,4,5 6 2 | Please input the length:5 Please input the data:1 Please input the data:2 Please input the data:3 Please input the data:4 Please input the data:5 The linked list is:1 2 3 4 5 The element to be inserted is:6 The location of the element to be inserted is:6 The linked list is:1 2 3 4 5 6 The length of this list is:6 |
6 0 | The element to be inserted is:6 The location of the element to be inserted is:0 insert failed The linked list is:1 2 3 4 5 The length of this list is:5 | |
6 7 | The element to be inserted is:6 The location of the element to be inserted is:7 insert failed The linked list is:1 2 3 4 5 The length of this list is:5 | |
Delete_List | 5 1,2,3,4,5 5 | Please input the length:5 Please input the data:1 Please input the data:2 Please input the data:3 Please input the data:4 Please input the data:5 The linked list is:1 2 3 4 5 The location of the element to be deleted is:5 The linked list is:1 2 3 4 The length of this list is:4 |
0 | The location of the element to be deleted is:0 delete failed The linked list is:1 2 3 4 5 The length of this list is:5 | |
6 | The location of the element to be deleted is:6 delete failed The linked list is:1 2 3 4 5 |