关于单链表

本文介绍了单链表的概念,包括链表和顺序表的比较。单链表每个结点包含数据域和指针域,用于指向后继结点。文章还阐述了单链表的存储密度特点,并提供了C语言实现的单链表操作,包括初始化、插入、删除、查找等操作的示例代码和运行结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于单链表

​ 线性表的链式存储结构称为链表

​ 其中每个存储结点不仅包含元素本身的信息(称为数据域),而且包含元素之间逻辑关系的信息,在C/C++语言中采用指针来实现,这称为指针域

​ 在每个结点中除包含有数据域以外只设置一个指针域,用于指向其后继结点,这样构成的链表称为单向链接表,称为单链表

​ 若一个结点中的某个指针域不需要指向其他任何结点,则将它的值置为空,用常量NULL表示。

​ 在线性表的链式存储中,通常每个链表带有一个头结点,并通过头结点的指针唯一表示该链表,称之为头指针,相应的指向首结点或者开始结点的指针称之为首指针,指向尾结点的指针称为尾指针

​ 从一个链表的头指针所指的头结点出发,沿着结点的链(即指针域的值)可以访问到每个结点。

单链表结构如下图:

在这里插入图片描述

​ 在单链表中,机假设每个结点的类型用LinkNode表示,它应包括存储元素的数据域,这里用data表示,其类型用通用类型标识符ElemType表示,还包括存储后继结点位置的指针域,这里用next表示。LinkNode类型的声明如下:

typedef struct LNode
{
	ElemType data;		//存放元素值 
	struct LNode *next;	//指向后继节点 
}LinkNode;				//单链表节点类型  



链表和顺序表的比较

​ ① 顺序表是线性表的直接映射,所以具有随机存取特性,即查找第i个元素对应的时间复杂度为O(1),而链表不具有随机存取特性。

​ ② 所谓存储密度是指结点中数据元素本身所占的存储量和整个结点占用的存储量之比,即

存储密度=结点数据元素所占的存储量/结点所占的存储量

​ 一般情况下,存储密度越大,存储空间的利用率越高。因为链表中有指针域,所以链表的存储密度小于1(而顺序表中没有指针域,每个顺序表元素存放一个线性表元素,所以顺序表的存储密度为1)。


作业要求:

1.初始化单链表h

2.依次采用尾插法插入a、b、c、d、e元素

3.输出单链表h

4.输出单链表h的长度

5.判断单链表h的第3个元素

6.输出单链表h的第3个元素

7.输出元素a的位置

8.在第4个元素位置上插入f元素

9.输出单链表h

10.删除单链表h的第3个元素

11.输出单链表h

12.释放单链表h

本次用C语言进行实现

以下为实现代码:

#include "stdio.h"    

typedef char ElemType;

typedef struct LNode
{
	ElemType data;		//存放元素值 
	struct LNode *next;	//指向后继节点 
}LinkNode;				//单链表节点类型  
关于函数参数中使用*&引用

​ 引用L是为了改变L的值,而L指向的是链表的头结点的地址,即要改变头结点的地址,
​ 但是一般改链都是对头结点之后的结点进行操作,所以头结点的地址一直没变,故去掉&后函数依旧可以正常执行。
之所以会加&,是以防没有链表是没有头结点的那种情况,因为可能会对第一个结点操作,那L的地址就会改变

void CreatListF(LinkNode *&L,ElemType a[],int n)	//头插法 
{
	LinkNode *s;
	L=(LinkNode *)malloc(sizeof(LinkNode));		
	L->next=NULL; 								//创建头结点,其next域置为NULL 
	for(int i=0;i<n;i++)						循环建立数据结点s  
	{
		s=(LinkNode*)malloc(sizeof(LinkNode));
		s->data=a[i];							//创建数据结点s
		s->next=L->next; 						//将结点s插入到原首结点之前、头结点之后 
		L->next=s;
	} 
}


void CreateListR(LinkNode *&L,ElemType a[],int n)		//尾插法 
{
	LinkNode *s,*r;
	L=(LinkNode *)malloc(sizeof(LinkNode));				//创建头结点 
	r=L;												//r始终指向尾结点,初始时指向头结点 
	for(int i=0;i<n;i++)								//循环建立数据结点 
	{
		s=(LinkNode *)malloc(sizeof(LinkNode));
		s->data=a[i];									//创建数据结点s 
		r->next=s;										//将结点s插入到结点r之后 
		r=s;
	}
	r->next=NULL;										//尾结点的next域置为NULL; 
}


//初始化线性表

void InitList(LinkNode *&L)
{
	L=(LinkNode *)malloc(sizeof(LinkNode));				//创建头结点,其next域置为NULL; 
	L->next=NULL;
}


//销毁线性表

void DestroyList(LinkNode *&L)
{
	LinkNode *pre=L,*p=L->next;			//pre指向结点p的前驱结点
	while(p!=NULL)						//扫描单链表L
	{
		free(pre);						//释放pre结点
		pre=p;							//pre、p同步后移一个结点
		p=pre->next; 
	} 
	 free(pre);							//循环结束时p为NULL,pre指向尾结点,释放它 
}


//判断线性表是否为空表

bool ListEmpty(LinkNode *L)
{
	return (L->next==NULL);
} 
 


//求线性表的长度

int ListLength(LinkNode *L)
{
	int n=0;
	LinkNode *p=L;			//p指向头结点,n置空为0(即头结点的序号为0)
	while(p->next!=NULL)
	{
		n++;
		p=p->next;
	} 
	return (n);				//循环结束时,p指向尾结点,其序号n为结点个数 
} 


//输出线性表

void DispList(LinkNode *L)
{
	LinkNode *p=L->next;		//p指向首结点
	while(p!=NULL)				//p不为NULL,输出p结点的data域 
	{
		printf("%c ",p->data);
		p=p->next;				//p移向下一个结点 
	} 
	printf("\n");
} 


//求线性表中某个数据元素值

bool GetElem(LinkNode *L,int i,ElemType &e)
{
	int j=0;
	LinkNode *p=L;						//p指向头结点,j置为0(即头结点的序号为0)
	if(i<=0)return false;				//i错误返回假
	while(j<i&&p!=NULL)					//找第i个结点p
	{
		j++;
		p=p->next;
	} 
	if(p==NULL)return false;			//不存在第i个数据结点,返回false
	else								//存在第i个结点,返回true 
	{
		e=p->data;
		return true;
	}	 
}


//按元素查找

int LocateElem(LinkNode *L,ElemType e)
{
	int i=1;
	LinkNode *p=L->next;				//p指向首结点,i置为1(即首结点的序号为1)
	while(p!=NULL&&p->data!=e) 			//查找data值为e的结点,其序号为i
	{
		p=p->next;
		i++;
	} 
	if(p==NULL)							//不存在值为e的结点,返回0 
		return 0;
	else								//存在值为e的结点,返回其逻辑序号i 
		return (i);
 } 


//插入数据元素

bool ListInsert(LinkNode *&L,int i,ElemType e)
{
	int j=0;
	LinkNode *p=L,*s;				//p指向头结点,j置为0(即头结点的序号为0)
	if(i<=0)return false;			//i错误返回false
	while(j<i-1&&p!=NULL)			//查找第i-1个结点p
	{
		j++;
		p=p->next;
	} 
	if(p==NULL)						//未找到第i-1个结点,返回false 
		return false;
	else							//找到第i-1个结点p,插入新结点并返回true
	{
		s=(LinkNode *)malloc(sizeof(LinkNode));
		s->data=e;					//创建新结点s,其data域置为e;
		s->next=p->next;			//将结点s插入到结点p之后 
		p->next=s;
		return true;
	} 
} 


//删除元素数据

bool ListDelete(LinkNode *&L,int i,ElemType &e)
{
	int j=0;
	LinkNode *p=L,*q;			//p指向头结点,j置为0(即头结点的序号为0)
	if(i<=0)return false;			//i错误返回false
	while(j<i-1&&p!=NULL)			//查找第i-1个结点p
	{
		j++;
		p=p->next;
	} 
	if(p==NULL)						//未找到第i-1个结点,返回false 
		return false;
	else							//找到第i-1个结点p
	{
		q=p->next;					//q指向第i个结点
		if(q==NULL)					//若不存在第i个结点,返回false
			return false;
		e=q->data;
		p->next=q->next;			//从单链表中删除q结点
		free(q);					//释放q结点
		return true;				//返回true表示成功删除第i个结点 
	}
} 



int main()
{
	LinkNode *L;
	ElemType e; 
	InitList(L);
	printf("初始化链表后:ListLength=%d\n",ListLength(L));
	char a[5]={'a','b','c','d','e'}; 
	CreateListR(L,a,5);
	printf("依次插入a~e后,链表为:");
	DispList(L);
	printf("链表的长度L.Length= %d\n",ListLength(L));
	printf("判断链表是否为空表(0为非空,1为空):   %d\n",ListEmpty(L));
	GetElem(L,3,e);
	printf("链表的第3个元素为:  %c\n",e);
	printf("元素a在链表中的位置为:%d\n",LocateElem(L,'a'));
	printf("在链表中的第4个位置插入元素f之后:");
	ListInsert(L,4,'f');
	DispList(L);
	printf("删除链表的第3个元素后:");
	ListDelete(L,3,e);
	DispList(L);
	DestroyList(L);
	return 0;
}

运行结果:

初始化链表后:ListLength=0
依次插入a~e后,链表为:a b c d e
链表的长度L.Length= 5
判断链表是否为空表(0为非空,1为空): 0
链表的第3个元素为: c
元素a在链表中的位置为:1
在链表中的第4个位置插入元素f之后:a b c f d e
删除链表的第3个元素后:a b f d e

下面是一个简单的单链表的C语言程序: ```c #include <stdio.h> #include <stdlib.h> // 定义链表节点结构体 typedef struct node { int data; // 数据域 struct node *next; // 指针域 } Node; // 初始化单链表 Node* initList() { Node *head = (Node*)malloc(sizeof(Node)); // 创建头结点 head->next = NULL; // 初始为空链表 return head; } // 在链表末尾添加新节点 void addNode(Node *head, int data) { Node *p = head; while (p->next != NULL) { // 找到链表最后一个节点 p = p->next; } Node *newNode = (Node*)malloc(sizeof(Node)); // 创建新节点 newNode->data = data; // 设置新节点的数据域 newNode->next = NULL; // 新节点的指针域指向空 p->next = newNode; // 将新节点接到链表最后 } // 删除链表中的指定节点 void deleteNode(Node *head, int data) { Node *p = head->next; Node *pre = head; while (p != NULL && p->data != data) { // 找到要删除的节点 pre = p; p = p->next; } if (p != NULL) { // 找到了要删除的节点 pre->next = p->next; // 将前一个节点的指针域指向要删除节点的下一个节点 free(p); // 释放要删除节点的内存 } } // 遍历链表并输出每个节点的数据域 void traverseList(Node *head) { Node *p = head->next; while (p != NULL) { printf("%d ", p->data); p = p->next; } printf("\n"); } int main() { Node *head = initList(); // 初始化链表 addNode(head, 1); // 在链表末尾添加新节点 addNode(head, 2); addNode(head, 3); addNode(head, 4); traverseList(head); // 遍历链表并输出每个节点的数据域 deleteNode(head, 3); // 删除节点 traverseList(head); // 再次遍历链表并输出每个节点的数据域 return 0; } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值