1.摘要
2.顺序存储结构的缺点
3.什么是单链表
4.头指针与头节点(哨兵位)
5.单向无头非循环链表的代码实现
5.1.功能实现的需求
5.2.头文件
5.3.接口文件
5.4.测试文件
6.参考文献
本文主要介绍单项无头非循环链表的原理及代码的实现。
上节我们讲了线性表的顺序存储结构,也就是用动态开辟内存的方式存储物理地址连续的数据。
不知道细心的你有没有发现,在我们对顺序表进行头插、头删、中间位置插入删除操作的时候,当删除了那个位置的数据后,就需要将后面的所有数据整体向前移动一个位置,这对于时间上的开销是很大的,时间复杂度为O(N)。
我们当然想要解决一下插入删除时的时间复杂度太高的问题,最好变成O(1),那该多好啊!
于是,链式存储结构便应运而生。
不知大家有没有看过电影《无间道》呢?
让我们来想象一下一个卧底警察的故事:
你是一名警察,由于执行特殊的任务,只能和上级保持单线联系;同时,你也只能和你的一个线人保持单线联系。也就是说你的上级不能直接和你的线人联系。
如果有一天,很不幸,你在执行任务时发生了意外,壮烈牺牲了。那么,就再也没有人能够知道你的线人是谁了。
你看:你的上级,你,你的线人,就向链条上的一个个节点一样,被连在了一起。前面的节点只能找到与之对应的后面的一个节点,同样,假如某一个节点断了,就再也找不回后面的节点了。
这种类似“单线联系”的结构,也就是由若干个一个当前数据+下一级地址这样的结构单元组成的结构,就是我们的链表结构。
让我们给出链表的定义:链表是物理存储结构上非连续、逻辑结构上连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
为了表示 每个数据元素ai与其直接后继数据元素ai+1之间的逻辑关系,对数据元素ai而言,出了存储其本身的信息之外,还需要存储一个指示其直接后继的信息(即直接后继的存储位置)。
我们把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。指针域中存储的信息称做指针或链。这两部分信息组成数据元素ai的存储映像,称为节点。
显然,节点是由存放数据元素的数据域和存放后继节点地址的指针域组成。
事实上,节点与节点之间是通过前一个节点的指针域进行连接的。我们完全可以想象有一个无形的箭头从前一个节点指向下一个节点,虽然事实上这个箭头并不存在。
另外,值得注意的是,最后一个节点的指针域存放的是NULL
,所以今天我们讨论的链表是非循环链表。
下面有两个概念值得我们加以区分一下:头指针与头节点。
头指针:指向链表第一个节点的指针,若链表有头节点,则是指向头节点的指针。
头节点:头节点也叫哨兵位,位于第一个存有有效数据的结点之前。它只有指针域,而数据域不存放有效数据。这样,在第一个有效数据节点之前插入数据或删除第一个节点的操作就和其他节点保持一致了。头节点并不是必要的。
好了,讲了那么多理论,总得上手敲代码,我们现在就来实现一下最简单的链表——单向无头非循环链表。
我们需要实现链表的初始化、增删查改、销毁等功能。
我们用一个结构体来存放这个单链表的信息:
typedef struct SLList {
SLListDataType data;
struct SLList* next;
}SLList;
#pragma once
#include
#include
#include
typedef int SLListDataType;
typedef struct SLList {
SLListDataType data;
struct SLList* next;
}SLList;
// ADD
void SLListPushBack(SLList** pphead, SLListDataType x);
void SLListPushFront(SLList** pphead, SLListDataType x);
// DELETE
void SLListPopBack(SLList** pphead);
void SLListPopFront(SLList** pphead);
// FIND
SLList* SLListFind(SLList* phead, SLListDataType x);
// MODIFY
void SLListInsertAfter(SLList* phead, SLListDataType x);
void SLListEraseAfter(SLList* phead);
// PRINT
void SLListPrint(const SLList* phead);
void SLListDestroy(SLList** pphead);
#include "SLList.h"
SLList* SLListBuyNewCode(SLListDataType x)
{
SLList* ret = (SLList*)malloc(sizeof(SLList));
if(!ret)
{
printf("Malloc Failed!\n");
exit(-1);
}
ret->data = x;
ret->next = NULL;
return ret;
}
void SLListPrint(const SLList* phead)
{
if (phead == NULL)
{
printf("NULL\n");
}
else
{
SLList* cur = phead;
while (cur)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
}
void SLListPushBack(SLList** pphead, SLListDataType x)
{
assert(pphead);
if (*pphead == NULL)
{
// node == 0
//Create the first node
SLList* newnode = SLListBuyNewCode(x);
*pphead = newnode;
}
else
{
SLList* cur = *pphead;
while (cur->next != NULL)
{
cur = cur->next;
}
SLList* newnode = SLListBuyNewCode(x);
cur->next = newnode;
}
}
void SLListPushFront(SLList** pphead, SLListDataType x)
{
assert(pphead);
if (*pphead == NULL)
{
*pphead = SLListBuyNewCode(x);
}
else
{
SLList* prev = SLListBuyNewCode(x);
prev->next = *pphead;
*pphead = prev;
}
}
void SLListPopBack(SLList** pphead)
{
//Method 1
assert(pphead);
assert(*pphead);
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
SLList* cur = *pphead;
while (cur->next->next != NULL)
{
cur = cur->next;
}
free(cur->next);
cur->next = NULL;
}
//Method 2
//SLList* cur = *pphead;
//if (cur->next == NULL)// only one node
//{
// free(cur);
// cur = NULL;
//}
//else // >= 2 nodes
//{
// SLList* prev = NULL;
// while (cur->next != NULL)
// {
// prev = cur;
// cur = cur->next;
// }
// free(cur);
// prev->next = NULL;
//}
}
void SLListPopFront(SLList** pphead)
{
assert(pphead);
assert(*pphead);
if ((*pphead)->next == NULL) // only 1 node
{
free(*pphead);
*pphead = NULL;
}
else // >= 2 nodes
{
SLList* cur = *pphead;
*pphead = (*pphead)->next;
free(cur);
cur = NULL;
}
}
SLList* SLListFind(SLList* phead, SLListDataType x)
{
SLList* cur = phead;
while (cur->next)
{
if (cur->data == x)
return cur;
else
cur = cur->next;
}
return cur->next;
}
void SLListInsertAfter(SLList* phead, SLListDataType x)
{
assert(phead);
SLList* tail = phead->next;
phead->next = SLListBuyNewCode(x);
phead->next->next = tail;
}
void SLListEraseAfter(SLList* phead)
{
assert(phead);
if (!phead->next)
{
printf("No data after\n");
return;
}
else
{
SLList* tmp = phead->next;
phead->next = tmp->next;
free(tmp);
tmp = NULL;
}
}
void SLListDestroy(SLList** pphead)
{
assert(pphead);
SLList* cur = *pphead;
while (cur)
{
SLList* tmp = cur->next;
free(cur);
cur = tmp;
tmp = NULL;
}
*pphead = NULL;
}
#include "SLList.h"
Test1()
{
SLList* phead = NULL;
SLListPushBack(&phead,1);
SLListPrint(phead);
SLListPushBack(&phead, 2);
SLListPrint(phead);
SLListPushBack(&phead, 3);
SLListPrint(phead);
SLListPushFront(&phead, 0);
SLListPrint(phead);
SLListPopBack(&phead);
SLListPrint(phead);
if (SLListFind(phead,1))
{
SLListInsertAfter(SLListFind(phead,1), 22);
SLListPrint(phead);
}
if (SLListFind(phead, 1))
{
SLListEraseAfter(SLListFind(phead, 1));
SLListPrint(phead);
}
SLListDestroy(&phead);
SLListPrint(phead);
}
int main()
{
Test1();
return 0;
}
1.《大话数据结构》p55