数据结构:线性表中链表的建立和基本操作(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; 
}
相关推荐
江畔柳前堤7 小时前
大语言模型分布式训练:从并行策略到万卡工程的系统梳理
人工智能·分布式·深度学习·算法·目标检测·机器学习·语言模型
Forever Nore8 小时前
LeetCode 4 寻找两个正序数组的中位数 - 二分
算法·leetcode
罗西的思考9 小时前
【OpenClaw具身硬件】MiniClaw 阅读笔记---(1)基础
人工智能·算法·机器学习
蛋先生DX10 小时前
大模型参数存储格式揭秘:BF不是男朋友
深度学习·算法·llm
爱跳舞的烤冷面10 小时前
自学嵌入式第22天(数据结构——哈希)
数据结构·算法·哈希算法
猎嘤一号11 小时前
博弈论(Game Theory)的理论、算法与工程
人工智能·算法·安全·博弈论
月光船幽幽11 小时前
影子模式下保护 logits 不被修改
人工智能·python·算法
马拉AI11 小时前
腾讯开源 Agent 记忆系统,AI“换对话就忘”的问题有了新解法(附安装使用教程)
人工智能·算法·开源·科研
地平线开发者13 小时前
征程6工具链模型X86推理方式说明
算法
地平线开发者13 小时前
【征程6】校准量化中HistogramObserver解析
算法