在之前的博客中,我写了有关线性表的顺序存储结构的顺序表,顺序表的特点是逻辑关系上,相邻的两个元素在物理位置上也相邻,因此可以随机存取表中任一元素,但是,它在进行插入删除操作时,需要移动大量的数据元素,故而在下面的内容中,讨论的是线性表的另一种存储结构(链式存储结构)中的不带头节点的单链表,由于链表不要求逻辑上相邻的元素在物理位置上也相邻,所以它没有顺序表的弱点,但是也失去了它的优点。
附上我的有关顺序表的博客链接:
静态顺序表
在下面的讨论中,我们讨论的是不带头结点的单链表,我们用一张图感受一下不带头结点的单链表如何存储数据:
在下面的具体内容有:单链表结点的定义和单链表相关操作接口以及附上完整的代码,包括头文件,接口实现文件和测试文件,而操作接口包括:
1、初始化 2、获得一个结点 3、尾插 4、尾删 5、头插 6、头删 7、查找指定元素 8、指定位置插入 9、指定位置删除 10、删除指定元素 11、删除所有指定元素 12、打印 13、销毁 14、获取链表长度
链表的定义:一种链式存储的线性表,用一组地址任意的存储单元存放线性表的数据元素,称存储单元为一个结点。
//结构体定义单链表的结点
typedef int DataType;
typedef struct Node
{
DataType data; //当前节点所保存的元素
struct Node* next; //指向链表中下一个结点
}Node,List,*pNode,*pList;
在上面的定义中,我们使用了关键字 typedef 使得结点中元素类型为 DataType ,有助于之后修改数据类型;而将 struct Node 创建新名为 Node 和 List ,和将 struct Node* 创建新名为 pNode 和 pList ,是为了帮助我们书写、阅读和理解之后的代码。
1、初始化
void InitList(pList* pplist)
{
assert(pplist != NULL);
*pplist = NULL;
}
注:pplist是外部传进来的一个变量的地址,不敢为空指针,故需断言。在之后的代码中就不解释了。
2、获得一个结点
因为在之后的插入中,我们需要得到一个结点进行插入操作,所以我先把获得一个结点的代码写出,代码如下:
pNode BuyNode(DataType d)
{
pNode tmp = (pNode)malloc(sizeof(Node)); //申请一个结点大小的空间
//判断是否申请空间成功
if (tmp == NULL)
{
perror("BuyNode::malooc");
return NULL;
}
//给新的结点赋值
tmp->data = d;
tmp->next = NULL;
return tmp;
}
3、尾插
核心:找到最后一个结点
void PushBack(pList* pplist,DataType d)
{
pNode newNode = BuyNode(d); //获得一个新的结点,存放我们要插入的数据
assert(pplist != NULL);
//判断获取新的结点是否成功
if (newNode == NULL)
{
exit(EXIT_FAILURE);
}
//空链表
if (*pplist == NULL)
{
*pplist = newNode;
}
//非空链表,遍历单链表找到尾结点,将尾结点指向新结点
else
{
pNode cur = *pplist;
while (cur->next != NULL)
{
cur = cur->next;
}
cur->next = newNode;
}
}
4、尾删
核心:找到倒数第二个结点
void PopBack(pList* pplist)
{
assert(pplist != NULL);
//链表为空,无可删除数据,直接返回
if (*pplist == NULL)
{
return;
}
//只有一个结点
if ((*pplist)->next == NULL)
{
free(*pplist);
*pplist = NULL;
}
//一个以上的结点
else
{
pNode cur = *pplist;
while(cur->next->next != NULL)
{
cur = cur->next;
}
//循环结束,cur指向倒数第二个结点,释放最后一个结点
free(cur->next);
cur->next = NULL;
}
}
5、头插
核心:无需考虑是否为空链表
void PushFront(pList* pplist, DataType d)
{
pNode newNode = BuyNode(d); //获得一个新结点
assert(pplist != NULL);
//判断是否申请成功
if (newNode == NULL)
{
exit(EXIT_FAILURE);
}
//头插
newNode->next = *pplist;
*pplist = newNode;
}
6、头删
void PopFront(pList* pplist)
{
assert(pplist != NULL);
//链表为空,无可删除数据,直接返回
if (*pplist == NULL)
{
return;
}
//链表不为空,将 pplist 指向第二个结点,此时第二个结点为第一个结点,需注意释放被删除的空间。
else
{
pNode del = *pplist;
*pplist = del->next;
free(del);
del = NULL;
}
}
7、查找指定元素
核心:返回的是所查找的元素的地址,方法是遍历查找。
pNode Find(pList plist, DataType d)
{
pNode cur = plist;
while (cur)
{
if (cur->data == d)
{
return cur;
}
cur = cur->next;
}
return NULL; //没找到
}
8、指定位置插入
核心:指定位置是否为第一个位置,不是的话,是否存在。
void Insert(pList* pplist, pNode pos, DataType d)
{
pNode newNode = NULL;
assert(pplist != NULL);
assert(*pplist != NULL);
assert(pos != NULL);
//第一个位置——>头插
if (pos == *pplist)
{
newNode = BuyNode(d);
newNode->next = *pplist;
*pplist = newNode;
}
else
{
pNode cur = *pplist;
while (cur && cur->next != pos) //顺序不可以颠倒
{
cur = cur->next;
}
if (cur != NULL)
{
newNode = BuyNode(d);
newNode->next = pos;
cur->next = newNode;
}
}
}
9、指定位置删除
核心:同上,位置问题
void Erase(pList* pplist, pNode pos)
{
assert(pplist != NULL);
assert(pos != NULL);
if (*pplist == NULL)
{
return;
}
//第一个结点
if (*pplist == pos)
{
//头删
pNode del = pos;
*pplist = (*pplist)->next;
free(del);
del = NULL;
}
//不是第一个结点
else
{
pNode cur = *pplist;
while (cur&&cur->next != pos)
{
cur = cur->next;
}
if (cur != NULL)
{
cur->next = pos->next;
free(pos);
pos = NULL;
}
}
}
10、删除指定元素
核心:根据前面写的查找指定元素和指定位置删除就可以实现
void Remove_1(pList* pplist, DataType d)
{
pNode cur = NULL;
pNode prev = NULL;
assert(pplist != NULL);
cur = *pplist;
while (cur)
{
if (cur->data == d)
{
//是第一个结点
if (*pplist == cur)
{
PopFront(pplist);
}
//非第一个结点
else
{
prev->next = cur->next;
free(cur);
cur = NULL;
}
return;
}
else
{
prev = cur;
cur = cur->next;
}
}
}
void Remove_2(pList* pplist, DataType d)
{
pNode pos = NULL;
assert(pplist != NULL);
pos = Find(*pplist, d);
if (pos != NULL)
{
Erase(pplist, pos);
}
}
11、删除所有指定元素
核心:与删除指定元素略有差异,需注意删除一个以后,将cur指向删除元素后一位。
void RemoveAll(pList* pplist, DataType d)
{
pNode cur = NULL;
pNode prev = NULL;
assert(pplist != NULL);
cur = *pplist;
while (cur)
{
if (cur->data == d)
{
//是第一个结点
if (*pplist == cur)
{
PopFront(pplist);
cur = *pplist;
}
//非第一个结点
else
{
prev->next = cur->next;
free(cur);
cur = prev;
}
}
else
{
prev = cur;
cur = cur->next;
}
}
}
12、打印
void PrintList(pList plist)
{
//无需断言,允许链表为空
//遍历打印
pNode cur = plist;
while (cur)
{
printf("%d--->", cur->data);
cur = cur->next;
}
printf("over\n");
}
13、销毁
void DestroyList(pList* pplist)
{
pNode cur = *pplist;
assert(pplist != NULL);
//从前向后销毁
while (cur != NULL)
{
pNode del = cur; //保存要销毁的结点
cur = cur->next; //使cur下一个要被销毁的结点
free(del);
del = NULL;
}
*pplist = NULL;
}
14、获取链表长度
int GetListLength(pList plist)
{
pNode cur = plist;
int count = 0; //计数
while (cur) //遍历
{
count++;
cur = cur->next;
}
return count;
}
//LinkList.h
#ifndef __LINKLIST_H__
#define __LINKLIST_H__
#include
#include
#include
typedef int DataType;
typedef struct Node
{
DataType data;
struct Node* next;
}Node, *pNode, List, *pList;
void InitList(pList* pplist);
pNode BuyNode(DataType d);
void PushBack(pList* pplist,DataType d);
void PopBack(pList* pplist);
void PushFront(pList* pplist, DataType d);
void PopFront(pList* pplist);
pNode Find(pList plist, DataType d);
void Insert(pList* pplist,pNode pos, DataType d);
void Erase(pList* pplist, pNode pos);
void Remove_1(pList* pplist, DataType d);
void Remove_2(pList* pplist, DataType d);
void RemoveAll(pList* pplist, DataType d);
void PrintList(pList plist);
void DestroyList(pList* pplist);
int GetListLength(pList plist);
#endif //__LINKLIST_H__
LinkList.c
#include "LinkList.h"
void InitList(pList* pplist)
{
assert(pplist != NULL);
*pplist = NULL;
}
pNode BuyNode(DataType d)
{
pNode tmp = (pNode)malloc(sizeof(Node));
if (tmp == NULL)
{
perror("BuyNode::malooc");
return NULL;
}
tmp->data = d;
tmp->next = NULL;
return tmp;
}
void PushBack(pList* pplist,DataType d)
{
pNode newNode = BuyNode(d);
assert(pplist != NULL);
if (newNode == NULL)
{
exit(EXIT_FAILURE);
}
if (*pplist == NULL) //空链表
{
*pplist = newNode;
}
else
{
pNode cur = *pplist;
while (cur->next != NULL)
{
cur = cur->next;
}
cur->next = newNode;
}
}
void PopBack(pList* pplist)
{
assert(pplist != NULL);
//链表为空,无可删除数据,直接返回
if (*pplist == NULL)
{
return;
}
//只有一个结点
if ((*pplist)->next == NULL)
{
free(*pplist);
*pplist = NULL;
}
//一个以上的结点
else
{
pNode cur = *pplist;
while(cur->next->next != NULL)
{
cur = cur->next;
}
//循环结束,cur指向倒数第二个结点,释放最后一个结点
free(cur->next);
cur->next = NULL;
}
}
void PushFront(pList* pplist, DataType d)
{
pNode newNode = BuyNode(d);
assert(pplist != NULL);
if (newNode == NULL)
{
exit(EXIT_FAILURE);
}
newNode->next = *pplist;
*pplist = newNode;
}
void PopFront(pList* pplist)
{
assert(pplist != NULL);
if (*pplist == NULL)
{
return;
}
else
{
pNode del = *pplist;
*pplist = del->next;
free(del);
del = NULL;
}
}
pNode Find(pList plist, DataType d)
{
pNode cur = plist;
while (cur)
{
if (cur->data == d)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void Insert(pList* pplist, pNode pos, DataType d)
{
pNode newNode = NULL;
assert(pplist != NULL);
assert(*pplist != NULL);
assert(pos != NULL);
//第一个位置——>头插
if (pos == *pplist)
{
newNode = BuyNode(d);
newNode->next = *pplist;
*pplist = newNode;
}
else
{
pNode cur = *pplist;
while (cur && cur->next != pos) //顺序不可以颠倒
{
cur = cur->next;
}
if (cur != NULL)
{
newNode = BuyNode(d);
newNode->next = pos;
cur->next = newNode;
}
}
}
void Erase(pList* pplist, pNode pos)
{
assert(pplist != NULL);
assert(pos != NULL);
if (*pplist == NULL)
{
return;
}
//第一个结点
if (*pplist == pos)
{
//头删
pNode del = pos;
*pplist = (*pplist)->next;
free(del);
del = NULL;
}
//不是第一个结点
else
{
pNode cur = *pplist;
while (cur&&cur->next != pos)
{
cur = cur->next;
}
if (cur != NULL)
{
cur->next = pos->next;
free(pos);
pos = NULL;
}
}
}
void Remove_1(pList* pplist, DataType d)
{
pNode cur = NULL;
pNode prev = NULL;
assert(pplist != NULL);
cur = *pplist;
while (cur)
{
if (cur->data == d)
{
//是第一个结点
if (*pplist == cur)
{
PopFront(pplist);
}
//非第一个结点
else
{
prev->next = cur->next;
free(cur);
cur = NULL;
}
return;
}
else
{
prev = cur;
cur = cur->next;
}
}
}
void Remove_2(pList* pplist, DataType d)
{
pNode pos = NULL;
assert(pplist != NULL);
pos = Find(*pplist, d);
if (pos != NULL)
{
Erase(pplist, pos);
}
}
void RemoveAll(pList* pplist, DataType d)
{
pNode cur = NULL;
pNode prev = NULL;
assert(pplist != NULL);
cur = *pplist;
while (cur)
{
if (cur->data == d)
{
//是第一个结点
if (*pplist == cur)
{
PopFront(pplist);
cur = *pplist;
}
//非第一个结点
else
{
prev->next = cur->next;
free(cur);
cur = prev;
}
}
else
{
prev = cur;
cur = cur->next;
}
}
}
void PrintList(pList plist)
{
pNode cur = plist;
while (cur)
{
printf("%d--->", cur->data);
cur = cur->next;
}
printf("over\n");
}
void DestroyList(pList* pplist)
{
pNode cur = *pplist;
assert(pplist != NULL);
while (cur != NULL)
{
pNode del = cur;
cur = cur->next;
free(del)
del = NULL;
}
*pplist = NULL;
}
int GetListLength(pList plist)
{
pNode cur = plist;
int count = 0;
while (cur)
{
count++;
cur = cur->next;
}
return count;
}
test.c
#include "LinkList.h"
void TestPushBack()
{
pList plist = NULL;
InitList(&plist);
PushBack(&plist, 1);
PushBack(&plist, 2);
PushBack(&plist, 3);
PushBack(&plist, 4);
PrintList(plist); //1 2 3 4
printf("len = %d\n", GetListLength(plist)); //4
DestroyList(&plist);
PrintList(plist); //
}
void TestPopBack()
{
pList plist = NULL;
InitList(&plist);
PushBack(&plist, 1);
PushBack(&plist, 2);
PushBack(&plist, 3);
PushBack(&plist, 4);
PrintList(plist); //1 2 3 4
PopBack(&plist);
PrintList(plist); //1 2 3
PopBack(&plist);
PrintList(plist); //1 2
PopBack(&plist);
PrintList(plist); //1
PopBack(&plist);
PrintList(plist); //
DestroyList(&plist);
PrintList(plist); //
}
void TestPushFront()
{
pList plist = NULL;
InitList(&plist);
PushFront(&plist, 1);
PushFront(&plist, 2);
PushFront(&plist, 3);
PushFront(&plist, 4);
PrintList(plist); //4 3 2 1
DestroyList(&plist);
PrintList(plist); //
}
void TestPopFront()
{
pList plist = NULL;
InitList(&plist);
PushFront(&plist, 1);
PushFront(&plist, 2);
PushFront(&plist, 3);
PushFront(&plist, 4);
PrintList(plist); //4 3 2 1
PopFront(&plist);
PrintList(plist); //3 2 1
PopFront(&plist);
PrintList(plist); //2 1
PopFront(&plist);
PrintList(plist); //1
PopFront(&plist);
PrintList(plist); //
DestroyList(&plist);
PrintList(plist); //
}
void TestFind()
{
pList plist = NULL;
pNode pos = NULL;
InitList(&plist);
PushFront(&plist, 1);
PushFront(&plist, 2);
PushFront(&plist, 3);
PushFront(&plist, 4);
PrintList(plist); //4 3 2 1
pos = Find(plist, 3);
if (pos != NULL)
{
printf("%d\n", pos->data);
}
DestroyList(&plist);
PrintList(plist); //
}
void TestInsert()
{
pList plist = NULL;
pNode pos = NULL;
InitList(&plist);
PushFront(&plist, 1);
PushFront(&plist, 2);
PushFront(&plist, 3);
PushFront(&plist, 4);
PrintList(plist); //4 3 2 1
pos = Find(plist, 3);
if (pos != NULL)
{
Insert(&plist, pos, 5);
}
PrintList(plist); //4 5 3 2 1
pos = Find(plist, 4);
if (pos != NULL)
{
Insert(&plist, pos, 6);
}
PrintList(plist); //6 4 5 3 2 1
DestroyList(&plist);
PrintList(plist); //
}
void TestErase()
{
pList plist = NULL;
pNode pos = NULL;
InitList(&plist);
PushFront(&plist, 1);
PushFront(&plist, 2);
PushFront(&plist, 3);
PushFront(&plist, 4);
PrintList(plist); //4 3 2 1
pos = Find(plist, 3);
if (pos != NULL)
{
Erase(&plist, pos);
}
PrintList(plist); //4 2 1
pos = Find(plist, 4);
if (pos != NULL)
{
Erase(&plist, pos);
}
PrintList(plist); //2 1
DestroyList(&plist);
PrintList(plist); //
}
void TestRemove()
{
pList plist = NULL;
InitList(&plist);
PushFront(&plist, 1);
PushFront(&plist, 2);
PushFront(&plist, 3);
PushFront(&plist, 1);
PushFront(&plist, 4);
PushFront(&plist, 2);
PushFront(&plist, 3);
PrintList(plist); //3 2 4 1 3 2 1
Remove_1(&plist, 2);
PrintList(plist); //3 4 1 3 2 1
Remove_2(&plist, 3);
PrintList(plist); //4 1 3 2 1
RemoveAll(&plist, 1);
PrintList(plist); //4 3 2
DestroyList(&plist);
PrintList(plist); //
}
int main()
{
TestPushBack();
TestPopBack();
TestPushFront();
TestPopFront();
TestFind();
TestInsert();
TestErase();
TestRemove();
system("pause");
return 0;
}