之前我们完成了顺序表的一些操作,今天我们就来说一说链表。
链表是一种常见的重要的数据结构。它是动态地进行存储分配的一种结构。它可以根据需要开辟内存单元。链表有一个“头指针”变量,以head表示,它存放一个地址。该地址指向一个元素。链表中每一个元素称为“结点”,每个结点都应包括两个部分:一为用户需要用的实际数据,二为下一个结点的地址。因此,head指向第一个元素:第一个元素又指向第二个元素;……,直到最后一个元素,该元素不再指向其它元素,它称为“表尾”,它的地址部分放一个“NULL”(表示“空地址”),链表到此结束。
链表分为很多种,有头结点,无头结点,有环,无环,单链表,双链表,排列组合就是8种。即有头有环单链表,有头无环单链表,有头有环双链表,有头无环双链表,无头有环单链表,无头无环单链表,无头有环双链表,无头无环双链表。今天我们要实现的是有头结点有环的单链表。
说完链表的种类我们来说一说链表的内容,链表是由结构体和指针构成的,我们定义一个名为LinkNode的结构体,里面存一个数据,一个指向下一个结点的指针。
下面我们就来实现这个无头无环的单链表的后续操作。
LinkList.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
typedef char LinkType; //定义链表中的结构为char类型
typedef struct LinkNode //定义一个结构体,里面存一个数据,一个指向下一个结点指针。
{
LinkType data;
struct LinkNode *next;
}LinkNode;
void LinkListInit(LinkNode **head); //初始化函数
LinkNode* LinkCreatNewNode(LinkType value); //创建一个新的结点
void LinkListPushBack(LinkNode **head,LinkType value); //尾插
void LinkListPopBack(LinkNode **head); //尾删
void LinkListPushFront(LinkNode **head,LinkType value); //头插
void LinkListPopFront(LinkNode **head); //头删
LinkNode* LinkListFind(LinkNode *head,LinkType to_find); //查找链表中的元素所在的位置
void LinkListInsert_before(LinkNode **head,LinkNode *pos,LinkType value); //在pos之前插入一个元素
void LinkListInsert_before2(LinkNode **head,LinkNode *pos,LinkType value); //在pos之前插入一个元素
void LinkListInsert_After(LinkNode **head,LinkNode *pos,LinkType value); //在pos之后插入一个元素
void LinkListErase(LinkNode **head,LinkNode *pos); //删除指定位置的元素
void LinkListRemove(LinkNode **head,LinkType to_delete); //删除指定值的元素
void LinkListRemoveAll(LinkNode **head,LinkType to_delete); //删除所有指定值的元素
int LinkListEmpty(LinkNode *head); //判断链表是否为空
size_t LinkListSize(LinkNode *head); //求链表的长度
void LinkListReversePrint(LinkNode *head); //打印逆序链表
LinkList.c
#include <stdio.h>
#include <stdlib.h>
#include "LinkList.h"
LinkNode* CreatNewNode(LinkType value)
{
LinkNode* NewNode = (LinkNode*)(malloc(sizeof(LinkNode)));
NewNode->data = value;
NewNode->next = NULL;
return NewNode;
}
void LinkListPrint(LinkNode* head) //打印函数
{
if(head == NULL)
{
printf("空链表\n");
return;
}
{
LinkNode* cur = head;
while(cur)
{
printf("[%c|%p]",cur->data,cur);
cur = cur->next;
}
printf("\n");
}
}
void DestoryNode(LinkNode** Delete) //销毁函数
{
free(*Delete);
}
void LinkListInit(LinkNode** head) //初始化函数
{
if(head == NULL) //非法输入
{
printf("非法输入\n");
return;
}
if(*head == NULL) //空链表
{
printf("空链表\n");
return;
}
*head = NULL;
}
void Swap(LinkType *a,LinkType *b) //交换函数
{
LinkType tmp = *a;
*a = *b;
*b = tmp;
}
void LinkListPushBack(LinkNode** head,LinkType value) //尾插函数
{
if(head == NULL) //非法输入
{
printf("非法输入\n");
return;
}
if(*head == NULL) //空链表
{
(*head) = CreatNewNode(value); //如果为空链表,创建一个新结点,头指针指向这个新结点
}
else
{
LinkNode* begin = *head; //如果链表不为空,头指针指向begin这个结点
while(begin->next) //遍历一遍链表
{
begin = begin->next;
}
begin->next = CreatNewNode(value); //遍历完链表后,在begin的下一个结点处创建一个新结点,实现尾插
}
}
void LinkListPopBack(LinkNode** head) //尾删函数
{
if(head == NULL) //非法输入
{
printf("非法输入\n");
return;
}
if(*head == NULL) //空链表
{
printf("空链表,无法删除\n");
return;
}
LinkNode* pop = *head; //如果不为空链表,头指针指向pop结点
while(pop->next->next) //遍历链表,直到pop的下一个位置的下一个位置为空退出循环
//这里说一下,pop->next->next为空,则pop->next指向的就是最后一个结点的位置
{
pop = pop->next;
}
LinkNode* Delete = pop->next; //所以pop->next指向的结点就是要删除的那个结点,定义那个结点为Delete结点,也就是最后一个结点
pop->next = NULL; //现在让pop->next指向空
DestoryNode(&Delete); //销毁Delete结点
}
void LinkListPushFront(LinkNode** head,LinkType value) //头插
{
if(head == NULL) //非法输入
{
printf("非法输入\n");
return;
}
if(*head == NULL)
{
LinkNode* NewNode = CreatNewNode(value); //链表为空时,创建一个新结点
*(head) = NewNode; //头指针指向新结点
}
else
{
LinkNode* NewNode = CreatNewNode(value); //链表不为空时,也创建一个新结点
NewNode->next = *head; //让头指针指向新结点的下一个结点,所以现在所说的下一个结点就是原来的第一个结点,
//那么现在的新结点就是第一个结点的位置
*head = NewNode; //头指针再指向新结点
}
}
void LinkListPopFront(LinkNode** head) //头删
{
if(head == NULL) //非法输入
{
printf("非法输入\n");
return;
}
if(*head == NULL) //空链表
{
printf("空链表,无法删除\n");
return;
}
else
{
LinkNode* Delete = (*head); //定义一个Delete结点,头指针指向Delete结点,Delete就是第一个结点,也就是要删除的结点
(*head) = (*head)->next; //头指针指向头指针的next
DestoryNode(&Delete); //销毁Delete结点
}
}
LinkNode* LinkListFind(LinkNode* head,LinkType to_find) //根据元素值查找结点
{
if(head == NULL) //空链表
{
printf("空链表\n");
return;
}
else
{
LinkNode* cur = head;
while(cur) //遍历链表
{
if(cur->data == to_find)
{
return cur; //当cur结点的元素等于我们要寻找的元素时,返回cur结点的地址
}
cur = cur->next;
}
return ;
}
}
void LinkListInsert_before(LinkNode** head, LinkNode* pos,LinkType value) //在任意元素之前插入,需要遍历链表
{
if(head == NULL)
{
printf("非法输入\n");
return;
}
if(*head == NULL)
{
printf("空链表\n");
return;
}
if(pos == NULL)
{
return;
}
if(pos == (*head)) // =是赋值,==是判断,当pos的位置是头指针所指向的位置时
{
LinkNode* NewNode = CreatNewNode(value); //创建一个新结点
NewNode->next = *head; //新结点的next指向头指针指向的结点
*head = NewNode; //让头指针指向新结点
}
else //当pos的位置不是头指针所指向的位置时
{
LinkNode* cur = *head; //头指针指向cur结点
while((cur->next)!=pos) //遍历链表,直到cur的next指向pos位置的结点时
{
cur = cur->next;
}
LinkNode* NewNode = CreatNewNode(value); //创建一个新结点
NewNode->next = pos; //结点的next指向pos位置的结点
cur->next = NewNode; //cur的next指向NewNode结点
}
}
void LinkListInsert_before2(LinkNode** head,LinkNode* pos,LinkType value) //在任意元素之前插入,不需要遍历链表
{
if(head == NULL)
{
printf("非法输入\n");
return;
}
if(*head == NULL)
{
printf("空链表\n");
return;
}
if(pos == (*head)) // =是赋值,==是判断,当pos的位置是头指针所指向的位置时
{
LinkNode* NewNode = CreatNewNode(value); //创建一个新结点
NewNode->next = *head; //新结点的next指向头指针指向的结点
*head = NewNode; //让头指针指向新结点
}
else
{
LinkNode* NewNode = CreatNewNode(value);
LinkNode* afterNode = pos->next;
pos->next = NewNode;
NewNode->next = afterNode;
Swap(&NewNode->data,&pos->data);
}
}
void LinkListInser_after(LinkNode** head,LinkNode* pos,LinkType value) //在任意元素之后插入
{
if(head == NULL) //非法输入
{
printf("非法输入\n");
return;
}
if(*head == NULL) //空链表
{
printf("空链表\n");
}
if(pos == NULL)
{
return;
}
LinkNode* cur = *head; //头指针指向cur结点
while(cur) //遍历链表
{
if(cur == pos) //当cur等于pos位置的结点时
{
LinkNode* afterNode = cur->next; //定义一个afterNode结点,让cur的next指向afterNode结点
cur->next = NewNode; //现在让cur的next指向NewNode结点
NewNode->next = afterNode; //NewNode的next指向afterNode结点
}
cur = cur->next;
}
}
void LinkListErase(LinkNode** head,LinkNode* pos) //删除任意位置元素
{
if(head == NULL) //非法输入
{
printf("非法输入\n");
return;
}
if(*head == NULL) //空链表
{
printf("空链表\n");
return;
}
if(pos == NULL)
{
return;
}
LinkNode* cur = *head;
while(cur) //遍历链表
{
if(cur->next == pos) //直到cur的next指向pos位置的结点
{
cur->next = pos->next; //cur的next指向pos的next
DestoryNode(&pos); //销毁pos位置的结点
}
cur = cur->next;
}
}
void LinkListRemove(LinkNode** head,LinkType delete) //删除链表中第一个指定元素
{
if(head == NULL) //非法输入
{
printf("非法输入\n");
return;
}
if(*head == NULL) //空链表
{
printf("空链表\n");
return;
}
LinkNode* cur = *head; //头指针指向cur结点
LinkListErase(head,(LinkListFind(*head,delete))); //调用LinkListFind函数找到delete元素的结点地址,
//再用LinkListErase函数删除delete元素的结点
}
void LinkListRemoveAll(LinkNode** head,LinkType delete) //删除所有指定元素
{
if(head == NULL) //非法输入
{
printf("非法输入\n");
return;
}
if(*head == NULL) //空链表
{
printf("空链表\n");
return;
}
LinkNode* cur = *head; //头指针指向cur结点
while(cur) //当cur不为空时,循环
{
LinkListErase(head,(LinkListFind(*head,delete))); //调用LinkListFind函数找到delete元素的结点地址
//再用LinkListErase函数删除delete元素的结点
cur = cur->next; //遍历链表
}
}
int LinkListEmpty(LinkNode* head) //判断链表是否为空
{
if(head == NULL)
{
printf("空链表\n"); //空链表返回1
return 1;
}
else
{
printf("链表不为空\n"); //非空链表返回0
return 0;
}
}
size_t LinkListSize(LinkNode* head) //计算链表长度
{
if(head == NULL)
{
printf("空链表\n"); //空链表
return;
}
size_t count;
LinkNode* cur = head;
while(cur)
{
count++;
cur = cur->next; //遍历链表,count++
}
return count;
}
void LinkListReverse(LinkNode* head) //链表逆序
{
if(head == NULL)
{
return;
}
else
{
LinkListReverse(head->next); //这里利用递归思想
printf("[%c|%p]",head->data,head);
}
}
//测试函数 test.c
void test_LinkListPushBack() //尾插测试函数
{
LinkNode* pnod;
LinkListInit(&pnod);
printf("*******尾插*******\n");
LinkListPushBack(&pnod,'a'); // 调用尾插插入四个元素
LinkListPushBack(&pnod,'b');
LinkListPushBack(&pnod,'c');
LinkListPushBack(&pnod,'d');
LinkListPrint(pnod);
}
void test_LinkListPopBack() //尾删测试函数
{
LinkNode* pnod;
LinkListInit(&pnod);
printf("*******尾删*******\n");
LinkListPushBack(&pnod,'a'); //先调用尾插插入两个元素
LinkListPushBack(&pnod,'b');
LinkListPrint(pnod);
LinkListPopBack(&pnod); //调用尾删删除最后一个元素
LinkListPrint(pnod);
}
void test_LinkListPushFront() //头插测试函数
{
LinkNode* pnod;
LinkListInit(&pnod);
printf("*******头插*******\n");
LinkListPushBack(&pnod,'a'); //调用头插插入三个元素
LinkListPushBack(&pnod,'b');
LinkListPushBack(&pnod,'c');
LinkListPrint(pnod);
}
void test_LinkListPopFront() //头删测试函数
{
LinkNode* pnod;
LinkListInit(&pnod);
printf("*******头删*******\n");
LinkListPushBack(&pnod,'a'); //调用头插插入两个元素
LinkListPushBack(&pnod,'b');
LinkListPushBack(&pnod,'c');
LinkListPrint(pnod);
LinkListPopFront(&pnod); //调用头删删除第一个元素
LinkListPrint(pnod);
}
void test_LinkListFind()
{
LinkNode* pnod;
LinkListInit(&pnod);
printf("*******根据元素值找结点*******\n");
LinkListPushBack(&pnod,'a'); //调用头插插入三个元素
LinkListPushBack(&pnod,'b');
LinkListPushBack(&pnod,'c');
LinkListPrint(pnod);
LinkNode* i = LinkListFind(pnod,'a'); //找到a的结点地址
printf("找到的结点地址为:%p\n",i);
}
void test_LinkListInsert_before()
{
LinkNode* pnod;
LinkListInit(&pnod);
printf("*******任意位置之前插入元素*******\n");
LinkListPushBack(&pnod,'a'); // 调用尾插插入四个元素
LinkListPushBack(&pnod,'b');
LinkListPushBack(&pnod,'c');
LinkListPushBack(&pnod,'d');
LinkListPrint(pnod);
LinkListInsert_before(&pnod,(LinkListFind(pnod,'b')),'m'); //在b之前插入m
LinkListPrint(pnod);
}
void test_LinkListInsert_before2()
{
LinkNode* pnod;
LinkListInit(&pnod);
printf("*******任意位置之前插入元素2*******\n");
LinkListPushBack(&pnod,'a'); // 调用尾插插入四个元素
LinkListPushBack(&pnod,'b');
LinkListPushBack(&pnod,'c');
LinkListPushBack(&pnod,'d');
LinkListPrint(pnod);
LinkListInsert_before2(&pnod,(LinkListFind(pnod,'b')),'m'); //在b之前插入m
LinkListPrint(pnod);
}
void test_LinkListInsert_after()
{
LinkNode* pnod;
LinkListInit(&pnod);
printf("*******任意位置之后插入元素*******\n");
LinkListPushBack(&pnod,'a'); // 调用尾插插入四个元素
LinkListPushBack(&pnod,'b');
LinkListPushBack(&pnod,'c');
LinkListPushBack(&pnod,'d');
LinkListPrint(pnod);
LinkListInser_after(&pnod,(LinkListFind(pnod,'b')),'m'); //在b之后插入m
LinkListPrint(pnod);
}
void test_LinkListErase()
{
LinkNode* pnod;
LinkListInit(&pnod);
printf("*******根据结点位置删除任意位置元素*******\n");
LinkListPushBack(&pnod,'a'); //调用尾插插入三个元素
LinkListPushBack(&pnod,'b');
LinkListPushBack(&pnod,'c');
LinkListPrint(pnod);
LinkListErase(&pnod,(LinkListFind(pnod,'b'))); //删除b元素
LinkListPrint(pnod);
}
void test_LinkListRemove()
{
LinkNode* pnod;
LinkListInit(&pnod);
printf("*******根据元素值删除任意元素*******\n");
LinkListPushBack(&pnod,'a');
LinkListPushBack(&pnod,'b');
LinkListPushBack(&pnod,'c');
LinkListPrint(pnod);
LinkListRemove(&pnod,'b'); //删除b元素
LinkListPrint(pnod);
}
void test_LinkListRemoveAll()
{
LinkNode* pnod;
LinkListInit(&pnod);
printf("*******根据元素值删除任意元素*******\n");
LinkListPushBack(&pnod,'a');
LinkListPushBack(&pnod,'b');
LinkListPushBack(&pnod,'b');
LinkListPushBack(&pnod,'c');
LinkListPrint(pnod);
LinkListRemoveAll(&pnod,'b'); //删除所有的b元素
LinkListPrint(pnod);
}
void test_LinkListEmpty()
{
LinkNode* pnod;
LinkListInit(&pnod);
LinkListPushBack(&pnod,'a');
printf("*******判断链表是否为空*******\n");
int i = LinkListEmpty(pnod);
printf("返回值为:%d\n",i);
}
void test_LinkListSize()
{
LinkNode* pnod;
LinkListInit(&pnod);
printf("*******链表长度*******\n");
LinkListPushBack(&pnod,'a');
LinkListPushBack(&pnod,'b');
LinkListPushBack(&pnod,'c');
LinkListPrint(pnod);
size_t i = LinkListSize(pnod);
printf("链表长度为:%d\n",i);
}
void test_LinkListReverse()
{
LinkNode* pnod;
LinkListInit(&pnod);
printf("*******链表逆序排列*******\n");
LinkListPushBack(&pnod,'a');
LinkListPushBack(&pnod,'b');
LinkListPushBack(&pnod,'c');
LinkListPrint(pnod);
printf("\n");
}
//main.c
int main()
{
test_LinkListPushBack();
test_LinkListPopBack();
test_LinkListPushFront();
test_LinkListPopFront();
test_LinkListFind();
test_LinkListInsert_before();
test_LinkListInsert_before2();
test_LinkListInsert_after();
test_LinkListErase();
test_LinkListRemove();
test_LinkListRemoveAll();
test_LinkListEmpty();
test_LinkListSize();
test_LinkListReverse();
}
运行结果