您的位置:首页 > 理论基础 > 数据结构算法

写给初学者的数据结构——初识链表(带头结点)(C++实现)

2019-07-11 16:39 393 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/weixin_43796828/article/details/94901321

学习链表,这篇文章就够了。

目录

学习链表,这篇文章就够了。

1、准备

2、理解

3、操作

4、应用

5、参考资料

6、相关文章(Java和标准C实现)

全文一共3174字,准备、理解、操作大约需要40分钟的阅读时间,感谢遇见!

1、准备:

1、C语言允许用户自己建立由不同类型数据组成的结合型的数据结构,它成为结构体。如:

[code]struct student               //一个记录学生信息的结构体
{
int stid;                //学号
char name[20];           //姓名
char sex;                //性别
int score;               //分数
};

2、定义结构体变量、结构体指针。如:

[code]struct student temp;            //结构体变量
struct student *pt;             //结构体指针

3、C允许程序设计者用一个简单的名字代替复杂的类型形式。如:

[code]typedef struct student std;            //用std代替struct student

4、void *malloc(unsigned int size)

作用:在内存的动态存储区中分配一个长度为size的连续空间,返回分配区域的第一个字节的地址。如果此函数未能成功执行,则返回空指针(NULL).

理解:void* 不是“指向任何数据类型”,而是“不指向确定类型”。

如:

[code]int *p;
p = (int)malloc(9*sizeof(int));

说明:1、是不是和“ int p[9] ”很像呢?2、这里为int的强制转换。3、这里的强制转换只是产生了一个临时的中间值赋给p,并没有实际改变malloc函数本身的类型。

5、引用

C和C++使用&符号来指示变量的地址。C++给&符号赋予了另一个含义将其用来声明引用。引用经常被用作函数参数,使得函数中的变量名成为调用程序中的变量的别名。(好好理解这句话

再体会一下:

[code]int main()
{
int a = 5;
add(a);
printf("%d\n",a);

add1(a);
printf("%d\n",a);

return 0;
}

void add(int a)
{
a = a + 1;
}

void add1(int &a)
{
a = a + 1;
}

6、函数指针。如:

[code]Status(*compare)(ElemType,ElemType)

第1对圆括号把*和compare括起来,表明compare是一个指向函数的指针。因此,(*compare)是一个参数列表为(ElemType,ElemType)、返回类型为Status的函数。

作为函数的参数是数据指针最常见的用法之一,函数指针亦是如此。

2、理解:

1、什么是链表?网上有一个非常精辟的比喻:链表就像一列火车,火车头是头指针,一个车厢是一个结点

2、链表的每一个结点都应该包括两个部分:用户需要用的实际数据下一个结点的地址。

3、

3、操作:(为了更好的理解,请允许我先代入C++的“引用”)

0、初始

[code]#include<stdio.h>
#include<math.h>					//储存着OVERFLOW,值为3
#include<stdlib.h>
typedef int ElemType;
typedef int Status;
typedef int Boolean;

#define TRUE 	    1
#define FALSE 	    0
#define OK          1
#define ERROR 	    0
#define	INFEASIBLE -1

struct LNode
{
ElemType data;
struct LNode *next;
};

typedef struct LNode* LinkList;

1、建立(也就是令一个指针指向一个“结点“大小的存储空间的过程)

[code]Status InitList(LinkList &L)					//操作结果: 构造一个空的线性表L
{
L = (LinkList)malloc(sizeof(struct LNode));		//产生头结点,并使L指向此头结点

if(!L)									//存储分配失败
{
exit(OVERFLOW);
}
L->next = NULL;

return OK;
}

2、插入、删除、查找、替换

[code]Status ListInsert(LinkList L,int i,ElemType e)  //在带头节点的单链线性表L中第i个位置之前插入元素e
{
int j = 0;
LinkList p = L,s;

while(p && j<i-1)			//寻找第i-1个结点,令p指向它。p指向第一个结点的时候,j为1.
{
p = p->next;
j++;
}//这里有两个终止条件,一个是链表遍历完了,一个是找到了

if(!p || j>i-1)				//如果遍历完了没找到
{
return ERROR;
}

s = (LinkList)malloc(sizeof(struct LNode));	//生成新节点

//插入L中
s->data = e;
s->next = p->next;
p->next = s;

return OK;
}

Status ListDelete(LinkList L,int i,ElemType &e)	//在头结点的单链线性表l中,删除第i个元素,并由e返回其值
{
int j=0;
LinkList p=L,q;

while(p->next && j<i-1)	//寻找第i个结点,并令p指向其前趋,即第i-1个结点。p指向第一个结点的时候,j为1.
{
p = p->next;
j++;
}//与ListInsert对比一下?只有while里面的p->next不同

if(!p->next || j>i-1)			//删除位置不合理
{
return ERROR;
}

q = p->next;			       //删除并释放节点
p->next = q->next;
e = q->data;
free(q);

return OK;
}

Status DeleteElem(LinkList L,ElemType e)	//删除表中值为e的元素,并返回TRUE;如无次元素,则返回FALSE
{
LinkList p = L, q;
while(p)
{
q = p->next;
if(q && q->data==e)
{
p->next = q->next;
free(q);
return TRUE;
}
p = q;
}
return FALSE;
}

int LocateElem(LinkList L,ElemType e,Status(*compare)(ElemType,ElemType))
{
//初始条件:线性表L已存在,compare()是数据元素判定函数(满足为1,否则为0)
//操作结果:	返回L中第1个与e满足关系compare()的数据元素的位序.
//			若这样的元素不存在,则返回值为0

int i = 0;
LinkList p = L->next;

while(p)
{
i++;
if(compare(p->data,e))			//找到这样的数据元素
{
return i;
}
p = p->next;
}

return 0;
}

Status comp(ElemType c1,ElemType c2)
{
//数据元素判定函数(相等为TRUE,否则为FALSE)
if(c1==c2)
{
return TRUE;
}
else
{
return FALSE;
}
}

​
Status ReplaceElem(LinkList L,int i,ElemType e)		//用e取代表l中第i个元素的值
{
LinkList p = L;
int j = 0;
while(p->next && j<i) 				//j为1的时候p指向第一个结点
{
j++;
p = p->next;
}//这里有两个终止条件,一个是p指向空,即链表遍历完毕。另一个是j==i,即找到对应元素。

if(j==i)
{
p->data = e;
return OK;
}
else					//表中不存在第i个元素
{
return ERROR;
}
}

​

3、销毁、清空、判空、表长

[code]Status DestoryList(LinkList &L)
{
//初始条件: 线性表L已存在。
//操作结果: 销毁线性表L
LinkList q;					//L指向最后一个元素时结束,L指向了(n+1)次
while(L)
{
q = L->next;
free(L);
L = q;
}
return OK;
}

Status ClearList(LinkList L)
{
//初始条件: 线性表L已存在.
//操作结果: 将L重置为空表
LinkList p,q;
p = L->next;		//p指向第一个结点
while(p) 			//没到表尾
{
q = p->next;
free(p);
p = q;
}
L->next = NULL;

return OK;
}

Status ListEmpty(LinkList L)
{
//初始条件:线性表L已存在。
//操作结果:若为空表,则返回FAlSE;
if(L->next) 		//非空
{
return FALSE;
}else
{
return TRUE;
}
}

int ListLength(LinkList L)
{
//初始条件: 线性表L已存在。
//操作结果: 返回L中数据元素的个数

int i = 0;
LinkList p = L->next;	//p指向第一个结点
while(p)				//没到表尾
{
i++;
p = p->next;
}
return i;
}

4、 求值、前驱、后继、遍历

[code]Status GetElem(LinkList L,int i,ElemType &e)
{
//L为带头结点的单链表的头指针
//当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR

int j = 1;					//j为计数器
LinkList p = L->next ;		//p指向第一个结点

while(p && j<i)//p指向第2个结点时,j为2。所以j为i时,p指向第i个结点
{
p = p->next;
j++;
}

if(!p || j>i)				//第i个元素不存在
{
return ERROR;
}
e = p->data;				//取第i个元素

return OK;
}

Status GetFirstElem(LinkList L,ElemType &e)			//返回表头元素的值
{
LinkList p = L->next;
if(!p)
{
return ERROR;
}
else
{
e = p->data;
}
return OK;
}

Status PriorElem(LinkList L,ElemType cur_e,ElemType &pre_e)
{
//初始条件: 线性表L已存在
//操作结果: 若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱。
//			返回OK;否则操作失败,pre_e无定义,返回INFEASIBLE

LinkList q,p = L->next;		//p指向第一个结点

while(p->next)				//p所指结点有后继
{
q = p->next;			//q为p的后继,实质是从第二个判到最后一个
if(q->data==cur_e)
{
pre_e = p->data;
return OK;
}
p = q;
}

return INFEASIBLE;
}

Status NextElem(LinkList L,ElemType cur_e,ElemType &next_e)
{
//初始条件:线性表L已存在
//操作结果: 若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继
//			返回OK;否则操作失败,next_e无定义,返回INFEASIBLE

LinkList p = L->next;		//p指向第一个结点
while(p->next)				//p所指结点有后继
{
if(p->data==cur_e)		//实质是从第一个判到倒数第二个
{
next_e = p->next->data;
return OK;
}
p = p->next;
}

return INFEASIBLE;
}

void visit(ElemType c)
{
printf("%d ",c);
}

Status ListTraverse(LinkList L,void(*vi)(ElemType))
{
//初始条件: 线性表L已存在
//操作结果: 依次对L的每个数据元素调用函数vi().一旦vi()失败,则操作失败

LinkList p = L->next;
while(p)
{
vi(p->data);
p = p->next;
}
printf("\n");

return OK;
}

 5、头插、尾插、头删、尾删

[code]Status HeadInsert(LinkList L,ElemType e)
{
//初始条件:线性表L已存在。
//操作结果: 在L的头部插入新的数据元素e,作为链表的第一个元素

LinkList s;
s= (LinkList)malloc(sizeof(struct LNode));		//生成新结点
s->data = e;							//给结点赋值
s->next = L->next;						//插在表头
L->next = s;

return OK;
}

Status EndInsert(LinkList L,ElemType e)
{
//初始条件:线性表L已存在.
//操作结果:在L的尾部插入新的数据元素e,作为链表的最后一个元素

LinkList p = L;
while(p->next)				//使p指向表尾元素
{
p = p->next;
}

p->next = (LinkList)malloc(sizeof(struct LNode));			//在表尾生成新节点
p->next->data = e;									//给新节点赋值
p->next->next = NULL;								//表尾

return OK;
}

Status DeleteFirst(LinkList L,ElemType &e)
{
//初始条件: 线性表L已存在,且有不少于1个元素
//操作结果: 删除L的第一个数据元素,并由e返回其值

LinkList p=L->next;
if(p)
{
e = p->data;
L->next = p->next;
free(p);
return OK;
}
else
{
return ERROR;
}
}

Status DeleteTail(LinkList L,ElemType &e)
{
//初始条件:	线性表L已存在,且有不少于1个元素
//操作结构: 删除L的最后一个数据元素,并用e返回其值

LinkList p=L,q;

if(!p->next)		//链表为空
{
return ERROR;
}

while(p->next)
{
q = p;
p = p->next;
}
q->next = NULL;		//新尾结点的next域设为NULL
e = p->data;
free(p);

return OK;
}

6、非降序插入、非升序插入、非降序建表、非升序建表

[code]void InsertAscend(LinkList L,ElemType e)
{
//初始条件: 按非降序排列的线性表L已存在。
//操作结果: 在L中按非降序插入新的数据元素e

LinkList q=L,p=L->next;
while(p&&e>p->data)
{
q = p;
p = p->next;
}
q->next = (LinkList)malloc(sizeof(struct LNode));	//插在q后
q->next->data = e;
q->next->next = p;
}

void InsertDescend(LinkList L,ElemType e)
{
//初始条件:按非升序排列的线性表L已存在.
//操作结果:在L中按非升序插入新的数据元素e

LinkList q = L,p = L->next;
while(p && e<p->data)
{
q = p;
p = p->next;
}
q->next = (LinkList)malloc(sizeof(struct LNode));	//插在q后
q->next->data = e;
q->next->next = p;
}

Status CreatAscend(LinkList &L,int n)
{
//按非降序建立n个元素的线性表
int j;
LinkList p,q,s;

if(n<=0)
{
return ERROR;
}
InitList(L);
printf("请输入%d个元素:\n",n);

s = (LinkList)malloc(sizeof(struct LNode));		//第一个结点
scanf("%d",&s->data);
s->next = NULL;
L->next = s;

for(j=1; j<n; j++)
{
s = (LinkList)malloc(sizeof(struct LNode));	//其余结点
scanf("%d",&s->data);
q = L;
p = L->next;

while(p && p->data<s->data)
{
q = p;
p = p->next;						//指针后移
}
s->next = q->next;						//元素插在q的后面
q->next = s;
}

return OK;
}

Status CreatDescend(LinkList &L,int n)
{
//按非升序建立n个元素的线性表
int j;
LinkList p,q,s;

if(n<=0)
{
return ERROR;
}

InitList(L);
printf("请输入%d个元素:\n",n);

s = (LinkList)malloc(sizeof(struct LNode));		//第一个结点
scanf("%d",&s->data);
s->next = NULL;
L->next = s;

for(j=1; j<n; j++)
{
s = (LinkList)malloc(sizeof(struct LNode));	//其余节点
scanf("%d",&s->data);
q = L;
p = L->next;
while(p&&p->data>s->data)
{
q = p;
p = p->next;						//指针后移
}
s->next = q->next;						//元素插在q的后面
q->next = s;
}

return OK;
}

 

4、应用:只展示图片,其余自己动手!

5、参考资料:

《数据结构(C语言版)》——严蔚敏

《数据结构》——高一凡

《C++ Primer Plus(第6版)》

《C程序设计》——谭浩强

6、相关文章:

Java实现:https://blog.csdn.net/weixin_43796828/article/details/95611547

标准C实现:https://blog.csdn.net/weixin_43796828/article/details/95616496

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: