在单链表写入一组数据代码_【数据结构】单链表&&静态链表详解和代码实例

喜欢的话可以扫码关注我们的公众号哦,更多精彩尽在微信公众号【程序猿声】

01 单链表(Singly Linked List )

1.1 什么是单链表?

单链表是一种链式存储的结构。它动态的为节点分配存储单元。当有节点插入时,系统动态的为结点分配空间。在结点删除时,应该及时释放相应的存储单元,以防止内存泄露。由于是链式存储,所以操作单链表时,必须知道头结点或者头指针的位置。并且,在查找第i个节点时,必须找到第i-1个节点。

1.2 单链表的存储结构代码描述

对于链式存储,通过上一节的讲解相信大家已经了解得够清楚了。如下图所示:

下面我们来看看单链表存储是如何用代码来实现的。

//单链表的存储结构C语言代码

typedef struct SListNode

//数据域

struct SListNode * pnext;//指针域

由上面的结构我们可以看出,一个节点由存放数据的数据域和存放地址的指针域组成。假如p指向了第i个节点,那么p->data就是该节点存放的数据,而p->pnext自然就是指向下一个节点的指针。如下图所示:

那么接下来我们看看单链表的各个操作具体实现吧。(只讲几个关键步骤)

备注:下面的代码基于这样的一个单链表:

有一个头指针phead

有一个头结点node

头指针指向头结点,头结点位置记为0

1.3 单链表的读取

在拿到头指针以后,单链表的读取也并非一件难事。一开始设置一个计数变量,不断遍历链表,让计数器自增。找到合适的位置将数据读取出来。具体代码实现如下:

#define status bool

#define ERROR false

#define OK true

/*

status GetSListIndexNode(Node * phead,DType *e, int index){

int icount = 0; //计数器

//注:0号位为头结点,头结点不存放任何数据

if (phead->pnext == nullptr || index  GetSListLength()/*此处为链表长度*/)

return ERROR; //异常 处理

while (phead->pnext != nullptr)

if (icount == index)

return OK;

return ERROR;

1.4 单链表的插入

1.4.1 指定位置后插

其实链表的插入和删除都是很简单的操作,初学者只要抓住指针指向的节点,并加以区分开来,就很easy了。如下图:

图中,假如此时p指向了我们要插入的节点的位置。那么,怎样把我们的S节点给插入到p指向的节点之后?在这里我们先不要惊动p以及p后面的节点:

我们先让S节点指向p之后的节点(步骤①)

之后我们切断p和p后面那个节点的关系(步骤②)

最后让p节点的指针域指向s节点(步骤③),搞定

算法描述:

声明一个指针p指向链表头结点,向后遍历p=p->next,找到正确的位置。

新建一个结点s。

s->next = p->next ①

p->next = s ②③

具体代码如下:

#define status bool

#define ERROR false

#define OK true

/*

status InsertSListNodeFront(Node * phead, DType IData, int index){

if (phead->pnext == nullptr || index  GetSListLength())

return ERROR; //异常 处理

int iCount = 0; //计数器

nullptr; //备用

while (phead->pnext != nullptr)

if ( iCount == index )

new Node;

//前插

return OK;

return ERROR;

1.4.2 指定位置前插

咳咳,聪明的小伙伴,用脑子想想。指定位置前插 == 指定位置的前一个位置进行后插。懂了吧?直接看具体代码:

/*

status InsertSListNodeBack(Node * phead, DType IData, int index){

if (phead->pnext == nullptr || index  GetSListLength())

return ERROR; //异常 处理

int iCount = 0; //计数器

nullptr; //备用

while (phead->pnext != nullptr)

if (iCount == index)

new Node;

//后插就是后一个节点的前插咯

return OK;

return ERROR;

1.5 单链表的删除

单链表的删除其实也是很简单。只要比如要删除p指向的节点,只需要让p之前的节点的指针域直接指向p之后的节点,再把p给free就OK了。如下图:

算法描述:

声明一个指针p指向链表头结点,向后遍历p=p->next,找到要删除的节点位置。

q = p->next

p->next = q->next ①②

free(q) ③④

具体代码如下:

/*

//删除指定位置节点(e获取删除元素)

template 

status DeleteSListIndexNode(Node * phead, DType *e, int index){

int i = 0; //计数器

nullptr;

if (phead->pnext == nullptr || index  GetSListLength())

return ERROR; //异常 处理

while (phead->pnext != nullptr)

//保存备用

if (i == index)

//删除出局

return OK;

return ERROR;

代码应该不难,相信大家都能很容易看懂。

1.6.1 单链表的完整代码

好了,前面介绍了几个重要的操作,接下来请大家看看完整的代码吧。小编为了使用方便,就用C++的class和template将整个链表封装到了一个类里面,通过模板实现泛型编程。

/*

#pragma once //VC编译器防止头文件被重复包含的一条预编译指令

#define status bool

#define OK true

#define ERROR false

#define YES true

#define NO false

template 

class Node

public:

template 

class CSingleLinkList

private:

//链表头指针

public:

//构造,类被创建时调用

//析构,类被销毁时调用

public:

//初始化链表

status InitSList();

//获取链表长度

int GetSListLength();

//增加一个节点 前插法

status AddSListNodeFront(DType idata);

//增加一个节点 后插法

status AddSListNodeBack( DType idata);

//判断链表是否为空

status IsSListEmpty();

//获取指定位置节点值(注意,本程序规定0号为头节点,e获取删除元素)

status GetSListIndexNode(DType *e, int index);

//删除指定位置节点(e获取删除元素)

status DeleteSListIndexNode(DType *e, int index);

//查找链表中指定值(pindex获取位置0==>not found)

status SearchSListNode(DType SData, int *pindex);

//指定位置前插

status InsertSListNodeFront(DType IData, int index);

//指定位置后插

status InsertSListNodeBack(DType IData, int index);

//清空链表(保留根节点)

status ClearSList();

//销毁链表(all delete)

status DestorySList();

//尾部删除一个元素

status DeleteSListNodeBack();

//打印链表   此函数根据实际情况而定

void PrintSList();

/*

#include "SingleLinkList.h"

#include 

template 

cout <

template 

cout <

//初始化链表

template 

new Node;

if (ph != NULL)

nullptr;

this->phead = ph; //头结点

return OK;

return ERROR;

//获取链表长度(head_node is not included)

template 

int CSingleLinkList::GetSListLength()

int length = 0;

this->phead;

while (phead->pnext != nullptr)

return length;

//增加一个节点 前插法

template 

new Node;

if (pnode != NULL)

this->phead->pnext;

this->phead->pnext = pnode; //挂载

//printf("pnode = %p  pnode->pnext = %p this->phead->pnext = %p this->phead = %p\n", pnode, pnode->pnext, this->phead->pnext, this->phead);

return OK;

return ERROR;

//增加一个节点 尾插法

template 

new Node;

this->phead;

if (pnode != NULL)

while (phead->pnext != nullptr)

nullptr;

//挂载

return OK;

return ERROR;

//判断链表是否为空

template 

if (this->phead->pnext == nullptr)

return YES;

return NO;

//获取指定位置节点值(注意,本程序规定0号为头节点,e获取节点的值)

template 

int index)

this->phead;

int i = 0; //计数器

if (phead->pnext == nullptr || index  GetSListLength())

return ERROR; //异常 处理

while (phead->pnext != nullptr)

if (i == index)

return OK;

return ERROR;

//删除指定位置节点(e获取删除元素)

template 

int index)

this->phead;

int i = 0; //计数器

nullptr;

if (phead->pnext == nullptr || index  GetSListLength())

return ERROR; //异常 处理

while (phead->pnext != nullptr)

//保存备用

if (i == index)

//删除出局

return OK;

return ERROR;

//查找链表中指定值(pindex获取位置   0==>not found)

template 

int *pindex)

this->phead;

int iCount = 0;//计数器

while (phead->pnext != nullptr)

if (phead->data == SData)

return YES;

0;

return NO;

//指定位置前插

template 

int index)

this->phead;

if (phead->pnext == nullptr || index  GetSListLength())

return ERROR; //异常 处理

int iCount = 0; //计数器

nullptr; //备用

while (phead->pnext != nullptr)

if ( iCount == index )

new Node;

//前插

return OK;

return ERROR;

//指定位置后插

template 

int index)

this->phead;

if (phead->pnext == nullptr || index  GetSListLength())

return ERROR; //异常 处理

int iCount = 0; //计数器

nullptr; //备用

while (phead->pnext != nullptr)

if (iCount == index)

new Node;

//后插就是后一个节点的前插咯

return OK;

return ERROR;

//清空链表(保留根节点)

template 

this->phead;

nullptr; //防止那啥,野指针

//保留头节点

while (phead != nullptr)

delete q; //释放

this->phead->pnext = nullptr;

return OK;

//销毁链表(all delete)

template 

delete this->phead;//释放头结点

return OK;

template 

this->phead;

nullptr; //备用

if (phead->pnext == nullptr)

return ERROR; //链表都空了还删鸡毛

while (phead->pnext != nullptr)

nullptr;

delete phead;

return OK;

template 

void CSingleLinkList::PrintSList()

this->phead;

if (phead->pnext == nullptr || phead == nullptr)

cout <

return;

int i = 1;

cout <

while (phead->pnext != nullptr)

cout <data<

/*

#include 

#include "SingleLinkList.h"

#include "SingleLinkList.cpp"

using namespace std;

int main(){

int> * pMySList = new CSingleLinkList;

cout <IsSListEmpty() <

111);

222);

333);

cout <GetSListLength() <

444);

555);

666);

cout <IsSListEmpty() <

cout <GetSListLength() <

int GetElem, GetIndex;

2);

cout <

6);

cout <

4);

cout <

1);

cout <

3);

cout <

555, &GetIndex);

cout <

111, &GetIndex);

cout <

666, &GetIndex);

cout <

333, 1);

444, 4);

777, 3);

888, 5);

return 0;

代码如果有不正确的地方,欢迎大家来指正哈。

02 静态链表(circular linked list)

2.1 什么是静态链表?

我们把线性表的元素存放在数组中,这些元素由两个域组成:

数据域data

指针域cur

数据域是存放数据的,而指针域,这里和链表不同是,它存的不再是指向下一个节点的内存地址。而是下一个节点在数组中的下标。我们就把这种用数组描述的链表称为静态表,该方法也称之为游标实现法。如下图所示:

由上图我们需要注意以下几点:

我们对数组的第一个元素和最后一个元素做特殊处理,不存放数据。

把未使用的数组元素称为备用链表。

数组的第一个元素(下标为0)的cur域存放备用链表第一个节点的下标。

数组的最后一个元素的cur域存放第一个有数据的节点的下标,相当于链表中头结点的存在。链表为空时,其值为0。

如下图:

引出的问题:数组的长度定义的问题,无法预支。所以,为了防止溢出,我们一般将静态表开得大一点。

2.2 静态链表存储的代码描述

基于上面的讲解,我们来看看代码是怎么描述这种存储结构的。

//---------线性表的静态单链表存储结构--------

#define MAXSIZE 1000 /*假设链表最大长度为1000*/

typedef struct

int cur;   //为0时表示无指向

接下来我们讲解几个重要的操作实现。

2.3 静态链表的插入操作

前面我们讲动态链表的时候说过,增加和删除一个节点我们可以用malloc()和free()函数(C++可用new和delete)来实现。但是现在由于我们操作的是静态表,它可是用数组存的,可没有这种操作了。因此我们首先来自己实现一个静态表的malloc和free。

那么怎么辨别数组中哪些空间没有被使用呢?一个好的解决办法是,将所有未使用或者被删除的空间串成一个备用链表。插入节点时便可以从备用链表获取第一个未使用的空间的下标。因此我们在初始化的时候会做这样的工作:

void SListInit(SLinkList space){

int i;

for(i = 0; i 

1; //将所有结点链入备用链表

//备用链表头指针链像第二个结点

0].cur = space[1].cur;

//第一个结点作为链表的头结点

1].cur = 0;

分配内存

//若为0,则说明备用链表用完

int Malloc_SL(SLinkList space){

int i = space[0].cur;

//判断备用链表是否非空

if(space[0].cur)

//备用链表头指针指向第二个空结点

0].cur = space[i].cur;

return i;    //返回第一个空结点

上面的代码应该是没有难度的。写完了这个函数,我们来看看静态表中具体如何插入:

//在链表的第i个位置插入元素e

void SlistInsert(SLinkList space, int i, ElemType e){

//超出范围

if(i  SListLength(space)+1)

return;

int k = 1, j;

//使k指示要插入的结点的前一个结点

for(j = 0; j 

int v = Malloc_SL(space);

if(v)     //如果有空间

//链入链表

注意几点:

首先我们让k指向了要插入节点(记为X)的前一个位置(记为Y节点),前插法。

然后我们在静态表内申请空间,存放新的节点(记为N)。

把数据放进新申请的节点里面。

新节点N的cur域指向X节点,然后让Y节点指向N节点。

该过程不难理解。就不上图了……

2.4 静态链表的删除操作

删除同样需要自己实现free函数,我们来看看代码:

回收内存

//将下标为k的空闲结点回收到备用链表

void Free_SL(SLinkList space, int k){

//将备用链表链到k之后

0].cur;

//将k链到备用链表头之后

0].cur = k;

删除以后记得把空间重新挂载到备用链表上以便下一次使用。下面我们实现删除的代码:

//删除第i个位置的元素

void SListDelete(SLinkList space, int i){   //超出范围退出

if(i  SListLength(space))

return ;

int k = 1, j;

//使k指示要删除的结点的前一个结点

for(j = 0; j 

int temp = space[k].cur;

其实代码也很好理解了。假如X、Y、Z……等等排列,我们要删除Y。无非就是告诉X,你不要指向Y了,你直接指向Z,然后我们再把Y给free了。就直接变成了X、Z……简单吧。

2.5 静态链表的完整代码

熬了一个下午,终于写完了.哈哈,用了C++的类模板封装了一个静态链表,简单的增删查改功能都有了.具体可以看代码:

StaticLinkList.h

#pragma once

#include 

#define MAXSIZE 100

#define status bool

#define YES true

#define NO false

#define OK true

#define ERROR false

template 

class Component

public:

//数据域

int cur;       //cur域,指向下一个元素的下标

template 

class CStaticLinkList

public:

//静态表

//自定义malloc和free

public:

int MallocNodeSSL();

status FreeNodeSSL(int index);

public:

status InitList(); //初始化静态表

status BackAddList( DATATYPE AddData); //尾增加

status InsertNodeList(DATATYPE InsertData, int index);//指定位置插入

status DeleteNodeList(DATATYPE *DelData, int index); //指定位置删除

int SearchList(DATATYPE sData);       //搜索数据为sData的节点,返回其在数组中的下标,0表示失败

status GetIndexList(DATATYPE *gData, int index);//获取指定索引的节点数据

int GetLengthList(); //获取静态表的长度

status ClearList(); //清空静态表

status IsEmptyList(); //判断静态表是否为空

status IsFullList(); //判断静态表是否满了

void PrintList(); //打印静态表,此函数根据实际情况编写

public:

StaticLinkList.cpp

#include "StaticLinkList.h"

template 

cout <

template 

cout <

template 

int CStaticLinkList::MallocNodeSSL()

int index = StaticLinkList[0].cur; //把备用链表第一个节点拿出来用

if (StaticLinkList[0].cur) //判断是否还有位置

0].cur = StaticLinkList[index].cur; //让备用链表第二个节点上来顶替第一个的位置

return index; //返回0表示分配失败

template 

int index)

//将删除节点挂接到备用链表上

this->StaticLinkList[index].cur = this->StaticLinkList[0].cur;

this->StaticLinkList[0].cur = index;

return OK;

template 

int i;

for (i = 0; i 

1;//全部塞入备用链表

1].cur = 0;/*因为目前静态表为空,最后一个节点的cur域为0*/

return OK;

template 

//尾增加

if (IsFullList())

return ERROR;

int index = MAXSIZE - 1;

int last = index;

while (index != 0)

int k = MallocNodeSSL(); //获取空闲位置下标

if (k)

//存入数据

0;   //末尾指向0

return OK;

return ERROR;

//在List中第i个节点之前插入新的节点

template 

int index)//指定位置插入

int i, GetFree, pos;

1;//最后节点下标

if (index  GetLengthList() || IsFullList())

return ERROR; //位置异常处理

if (GetFree)

for (i = 0; i 

//定位

//插入

int q = StaticLinkList[MAXSIZE - 1].cur;

if (q == 0) //静态表为空

1].cur = 1;

return OK;

return ERROR;

//判断是否为空

template 

if (StaticLinkList[MAXSIZE-1].cur == 0)

return YES;

return NO;

//判断静态表是否满了

template 

if (GetLengthList() == MAXSIZE - 2) //因为首位不存数据,因此pass掉

return YES;

return NO;

template 

int CStaticLinkList::GetLengthList() //获取静态表的长度

int iCount = 0;

int k = MAXSIZE - 1;

while (StaticLinkList[k].cur != 0)

return iCount;

template 

int index)//指定位置删除

int i, pos, k;

1;//最后节点下标

if (index  GetLengthList() || IsEmptyList())

return ERROR; //位置异常处理

for (i = 0; i 

//定位到被删除节点的前一个节点

//获取数据

//让前一个节点直接指向后一个节点.把夹在中间的踢掉

//释放空间

return OK;

//搜索数据为sData的节点,返回其在静态表中的第i个位置,0表示没找到

template 

int CStaticLinkList::SearchList(DATATYPE sData)

int pos = StaticLinkList[MAXSIZE-1].cur;

int iCount = 1;

while (pos != 0)

if (StaticLinkList[pos].data == sData) //找到数据

return iCount;

//循环遍历

return 0;

template 

int index)//获取第index个节点数据

int i, pos;

1;//最后节点下标

if (index  GetLengthList() || IsEmptyList())

return ERROR; //位置异常处理

for (i = 0; i 

//定位到第index个节点

return OK;

template 

//清空静态表

//再初始化一次就是清空了

return  OK;

template 

void  CStaticLinkList::PrintList()

int pos = StaticLinkList[MAXSIZE - 1].cur;

if (pos == 0)

cout <

return;

cout <

cout <

int iCount = 1;

while (pos != 0)

cout <

//循环遍历

StaticLinkListTest.cpp测试代码

#include 

#include 

#include "StaticLinkList.h"

#include "StaticLinkList.cpp"

using namespace std;

int main(){

char get;

char> *p = new CStaticLinkList;

'a'); //pass

'b');

'c');

'd');

'e');

//p->ClearList();      //pass

2);  //pass

cout <

2);

cout <

'f');

cout <GetLengthList() <

3);  //pass

cout <

'h', 2);

'i', 3);

cout <GetLengthList() <

cout <SearchList('q') <

cout <SearchList('h') <

cout <SearchList('i') <

delete p;

return 0;

运行结果

运行结果

代码未经过严谨测试,如果有错,欢迎大家指正和交流啊.

这次就先到这里吧。更多精彩内容,请期待下回分解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值