数据结构:线性表中链表的建立和基本操作(C)

链表是一种物理存储上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。

建立结点结构体

C 复制代码
//定义一个结点
#include<stdio.h>
typedef int ElemType;
typedef struct node
{
    ElemType e;//数据域
    struct node *next;//指针域
}LinkList;
           

链表是由一个个结点所构成,首先定义一个结点的结构体。

该结构体包括存放数据的数据域和指向结点所需的指针域,分别由ElemType e 和 node *next所定义,该指针域直接指向后继元素。

每一个链表中的结点即是该LinkList结构体。

每当需要一个链表时,即可调用这个模子(结构体),完成一个链表的创建。

链表的初始化

C 复制代码
//初始化结点中的指针 令指针域为空
void Initnode(LinkList *n)
{
    n->next=NULL;
}

链表的初始化分为结点和表的初始化。

定义Initnode函数完成对指针域的初始化。

C 复制代码
//创建一个空表     
void InitLinkList(LinkLinst * &h) //头指针->头结点
{
    h=new LinkList; //动态要一个结点
    Initnode(h);    //指针域初始化
}

定义InitLinkList函数完成对一个空链表的建立。

函数参数中'&'可理解为对建立的头结点需进行地址的访问。

函数内部第一条语句通过一个new函数向系统内存动态的取得一个头结点,一个头结点即是一个空链表的标志。

最后完成对指针域的初始化。

链表的创建

C 复制代码
//创建一个链表
void CreateLink(LinkList * &h)
{
    LinkList *p,*t;//p指向最后一个结点,t动态要一个结点
    p=h;
    while(1)
{       ElemType e;
        scanf("%d",&e);  //输入数据
        if(e==-1) break;       
        t=new LinkList;  //尾部获取新结点
        Initnode(t);     //新结点初始化
        t->data=e;       //所输入数据存储到的新的结点的数据域中  
        p->next=t;       
        p=p->next;       //指针的定向移动
} 

链表的建立可采取头插法和尾插法,此处采用尾插法。

定义CreateLink函数完成对链表的建立。

函数内部定义两个指针,分别用来指向后继结点和获取新结点。

通过一个while函数,在两个指针的定向移动中完成对链表的输入。

定义e变量来存放输入的元素。

此处采用的是数据-1为链表的终止标志。(if语句)


基本操作

1.取值

C 复制代码
//取值
void GetElem(LinkList *h,int i,ElemType &e)
{
    LinkList *p=h->next;
    int j=1;
    while(p&&j<i)        //指针移动
   {    p=p->next;
        j++;
   }    
    if(!p||i<j) return; //判断i是否合法
    e=p->data;          //将该结点的值存放在e中
}

    

定义GetElem函数完成取值操作。

在函数内部从首结点开始完成指针的移动,在移动时要保证p指针不能为空且j不能与i相等,一旦相等,说明到达了该地址的结点,循环结束。

if循环来判断i是否合法。

最后将p指针所指的结点数据保存在e中,完成取值操作。

2.查找

C 复制代码
//查找
void Find(LinkList *h,ElemTyep e)
{
    LinkList *p=h->next;
    int j=1;
    while(p&&p->data!=e)
    {    p=p->next;
         j++;
    }
    if(p) return j;
    return 0;
}

定义Find函数完成查找操作。

查找操作与取值操作十分相似,在函数内部的while循环中,首先保证p指针不为空,其次保证在指针在数据域中的筛选,当输入值e与某结点数据相等时,结束循环。

若此时p不为空则返回坐标j,完成查找操作。

3.插入

C 复制代码
//插入(插在第i个结点之前)
void Insert(LinkList *&h,int i,ElemType e)
{
    LinkList *p=h,*t;
    int j=0;       //此处从头结点开始插入,j=0
    while(p&&j<i-1)
    {    p=p->next;
         j++;
    }
    if{!p||i<1} return;
    t=new LinkList;
    InList(t);
    t->data=e;
    t->next=p->next;
    p->next=t;
}    
    

定义Insert函数完成插入操作。

由于插入操作可以从头结点后开始(即插入第一个结点),故此时定义的指针p需指向头结点且j=0。

t为新结点(插入结点)。

t->next=p->next; 语句完成t结点与i结点的链接。

p->next=t; 语句完成t与i-1结点的链接。

4.删除

C 复制代码
//删除
void Del(LinkList * &h,int i)
{
	LinkList *p=h,*q;
	int j=0;
	while(p->next&&j<i-1){p=p->next;j++;}
	if(!(p->next)||i<1) return;
	q=p->next;
	p->next=q->next;
	delete q;

定义Del函数完成删除操作。

和查找类似,由于可在头结点后进行操作,故将指针p的初始位置指向头结点且j=0。

经历循环过后,p指针指向i的前驱。

q=p->next; 语句完成q指针指向i结点的操作,此时q->next的地址为i结点后继的地址。

p->next=q->next; 语句完成i的前驱与i的后继的链接,i结点被隔离。

delete q; 完成删除操作。

5.遍历列表

C 复制代码
//遍历 
void  Display(LinkList *h)
{
	LinkList *p=h->next;
	while(p)
	{
		printf("%d->",p->data);
		p=p->next;
	}
	printf("\n");

代码测试

C 复制代码
int main()
{
	LinkList *h;
	ElemType e;
	InitLink(h);
	CreateLink(h);
	Display(h);
	GetElem(h,3,e);printf("%d\n",e);
	printf("%d\n",Find(h,4));
	Insert(h,4,10);
	Display(h);
	Del(h,3);
	Display(h);
	return 0; 
}
相关推荐
水木流年追梦2 小时前
大模型入门-大模型分布式训练2
开发语言·分布式·python·算法·正则表达式·prompt
sali-tec2 小时前
C# 基于OpenCv的视觉工作流-章78-KRT测量
图像处理·人工智能·数码相机·opencv·算法·计算机视觉
菜菜的顾清寒2 小时前
力扣HOT100(32)二叉树的中序遍历
数据结构·算法·leetcode
DolphinDB2 小时前
基于 DolphinDB 搭建微服务的 SpringBoot 项目
后端·算法
珊瑚里的鱼3 小时前
【动态规划】第N个泰波那契数
算法·动态规划
ComputerInBook3 小时前
C++ 23 相比 C++ 20 新增之特征
开发语言·算法·c++23
吴可可1233 小时前
样条曲线转多段线技巧
算法·c#
csdn_aspnet3 小时前
java 算法 LeetCode 编号 70 - 爬楼梯
java·开发语言·算法·leetcode
pengyi8710153 小时前
动态 IP 池调度算法核心原理:从随机分配到智能调度演进
网络·tcp/ip·算法