【数据结构】线性表的链式存储(单链表)及基本运算——C语言

1. 单链表基本概念

链式存储是最常用的动态存储方法,通常将采用链式存储结构的线性表称为线性链表。从链接方式看,链表可分为单链表、循环链表和双链表。从实现角度看,链表可分为动态链表和静态链表。
链表用一组任意的存储单元来存放线性表的结点,链表中结点的逻辑顺序和物理顺序不一定相同,为了正确表示结点间的逻辑关系,必须在存储线性表的每个数据元素值的同时,存储指示其后继结点的地址(或位置)。这两部分信息组成的存储映像称为结点(Node)。
在这里插入图片描述

结点包括两个域:数据域用来存储结点的值,指针域用来存储数据元素的直接后继的地址(或位置)。线性表正是通过每个结点的指针域将线性表的n个结点按其逻辑顺序链接在一起的。由于此线性链表的每个结点只有一个next指针域,故将这种链表称为单链表
单链表中每个结点的存储地址存放在其前驱结点的指针域中,由于线性表中的第一个结点无前驱,所以应设一个头指针H指向第一个结点。由于线性表的最后一个结点没有直接后继,则指定单链表的最后一个结点的指针域为“空”(NULL)。

单链表的基本运算包括初始化、建表(头插法/尾插法)、输出、结点查找(按值/按序号)、求表长、逆置、插入、删除、升序排序和升序合并等。

2. 完整代码

/*
线性表的链式存储结构(单链表)及其基本运算
包括单链表初始化,建表(头插法、尾插法),输出,查找结点(按值、按序号),求表长,逆置,插入,删除,升序排序,升序合并两个单链表等
*/

# include<stdio.h>
# include<malloc.h>
# define ERROR 0
# define OK 1

typedef int ElemType;

/*单链表的存储结构*/
/*L是单链表的头指针,它指向表中第一个结点(对于带头结点的单链表,则指向单链表的头结点),若L==NULL(对于带头结点的单链表为L->next==NULL)表达式为真,则表示单链表为一个空表,其长度为0。
若是非空表,则可以通过头指针L访问表中结点,从而找到要访问的所有结点的数据信息。
例如,对于带头结点的单链表L,令p=L->next,则p指向表中的第一个元素结点(也称首元结点),通过p->data就可以访问到表中第一个元素的数据值。*/
typedef struct Node {
	ElemType data;
	struct Node* next;
}Node, * LinkList;

LinkList L, LB;

/*初始化单链表*/
InitList(LinkList* L) {
	*L = (LinkList)malloc(sizeof(Node));	//建立头结点
	(*L)->next = NULL;						//建立空的单链表L
}

/*头插法建表*/
/*从一个空表开始,每次读入数据,生成新结点,将读入数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头结点之后,直到读入结束标志为止。
采用头插法得到的单链表的逻辑顺序与输入元素顺序相反,亦称头插法建表为逆序建表法*/
void CreateFromHead(LinkList L) {
	Node* s;
	int c, flag = 1;
	while (flag) {
		scanf("%d", &c);
		if (c != 0) {
			s = (Node*)malloc(sizeof(Node));//建立新结点
			s->data = c;
			s->next = L->next;				//将s结点插入表头
			L->next = s;
		}
		else
			flag = 0;
	}
}

/*尾插法建表(建立单链表与输入顺序相同)
将新结点插入到当前单链表的表尾上,为此需增加一个尾指针r,使之指向当前单链表的表尾*/
void CreateFromTail(LinkList L) {
	Node* r, * s;
	r = L;									//r指针动态指向链表的当前表尾,以便于做尾插入,其初值指向头结点
	int c, flag = 1;
	while (flag) {
		scanf("%d", &c);
		if (c != 0) {
			s = (Node*)malloc(sizeof(Node));//建立新结点
			s->data = c;
			r->next = s;
			r = s;
		}
		else {
			flag = 0;
			r->next = NULL;					//将最后一个结点的next链域置为空,表示链表的结束
		}
	}
}

/*单链表输出*/
void OutputList(LinkList L) {
	if (L->next == NULL) {
		printf("空表!");
	}
	else {
		Node* p;
		p = L->next;
		while (p) {
			printf("%d ", p->data);
			p = p->next;
		}
	}
}

/*按序号查找第i个结点值*/
/*在带头结点的单链表L中查找第i个结点,若找到(1<=i<=n),则返回该结点的存储位置;否则返回NULL*/
int Get(LinkList L, int n) {
	int i = 0;
	Node* p;
	if (n <= 0)
		return NULL;
	p = L;
	while (p->next != NULL && i < n) {
		p = p->next;
		i++;
	}
	if (i == n)
		return (p->data);
	else
		return NULL;
}

/*按值查找结点序号*/
/*在带头结点的单链表L中查找其结点值等于n的第一个结点,若找到则返回该结点的位置p,否则返回NULL*/
int Locate(LinkList L, int n) {
	int i = 0;
	Node* p;
	p = L->next;
	while (p != NULL) {
		if (p->data != n) {
			p = p->next;
			i++;
		}
		else
			break;
	}
	if (p != NULL)
		return i + 1;
	else
		return NULL;
}

/*求表长*/
int GetLength(LinkList L) {
	int i = 0;
	Node* p;
	p = L->next;
	while (p != NULL) {
		p = p->next;
		i++;
	}
	return i;
}

/*逆置*/
void ReverseList(LinkList L) {
	Node* p, * temp;
	if (L->next == NULL || L->next->next == NULL)
		return NULL;
	p = L->next->next;
	L->next->next = NULL;					//原本第一个结点逆置后变为最后一个结点,next链域置为空,表示链表的结束
	while (p != NULL) {
		temp = p->next;						//从第二个结点起依次移至头结点之后实现逆置
		p->next = L->next;
		L->next = p;
		p = temp;
	}
}

/*插入*/
/*插入的过程分为三步:
(1)查找:在单链表中找到第i-1个结点并由指针pre指示
(2)申请:申请新结点s,将其数据域的值置为e
(3)插入挂链:通过修改指针域将新结点s挂入单链表L */
void InsList(LinkList L, int i, int e) {
/*在带头结点的单链表L中第i个位置插入值为e的新结点*/
	Node* pre, * s;
	pre = L;
	if (i < 1) {
		printf("插入位置不合法!\n");
		return ERROR;
	}
	int k = 0;
	while (pre != NULL && k < i - 1) {		//查找第i-1个结点
		pre = pre->next;
		k++;
	}
	if (pre == NULL) {
		printf("插入位置不合法!\n");
		return ERROR;
	}
	s = (Node*)malloc(sizeof(Node));		//申请一个新结点
	s->data = e;
	s->next = pre->next;					//修改指针完成插入操作
	pre->next = s;
	printf("插入成功!\n");
	return OK;

}

/*删除*/
/*删除过程分为两步:
(1)查找:通过计数方式找到第i-1个结点并由指针pre指示
(2)删除第i个结点并释放结点空间 */
int DelList(LinkList L, int i) {
/*在带头结点的单链表L中删除第i个元素*/
	Node* pre, * r;
	pre = L;
	if (i < 1) {
		printf("删除位置不合法!\n");
		return ERROR;
	}
	int k = 0, e;
	while (pre->next != NULL && k < i - 1) {//查找第i-1个结点
		pre = pre->next;
		k++;
	}
	if (pre->next == NULL) {
		printf("删除位置不合法!\n");
		return ERROR;
	}
	r = pre->next;
	pre->next = r->next;					//修改指针,删除结点r
	e = r->data;
	free(r);
	printf("删除成功!删除的结点值为%d\n", e);
	return OK;
}

/*升序排序*/
void AscSort(LinkList L) {
	Node* s, * pre, * p, * temp;
	if (L->next == NULL)
		return ERROR;
	s = L->next->next;
	L->next->next = NULL;
	while (s != NULL) {
		pre = L;
		p = L->next;
		while (p && s->data > p->data) {
			pre = p;
			p = p->next;
		}
		temp = s->next;
		pre->next = s;
		s->next = p;
		s = temp;
	}
}

/*升序合并*/
void MergeList(LinkList L, LinkList LB) {
	Node* pa, * pb, * pre;
	pre = L; 
	pa = L->next; 
	pb = LB->next;
	while (pa && pb) {
		if (pa->data < pb->data)
		{
			pre->next = pa; pre = pa; pa = pa->next;
		}
		else
		{
			pre->next = pb; pre = pb; pb = pb->next;
		}
	}
	pre->next = pa ? pa : pb;
	free(LB);
}

/*选择菜单函数用于实现功能的选择*/
void Menu(int select) {
	int n, e;
	switch (select) {
	case 1:InitList(&L);						//初始化
		printf("初始化成功!\n"); break;
	case 2:printf("输出结果为:");				//输出
		OutputList(L);
		printf("\n"); break;
	case 3:InitList(&L);						//头插法建表
		printf("请输入单链表元素(以空格隔开,以0结束):");
		CreateFromHead(L);
		printf("头插法建表成功!\n"); 
		printf("输出结果为:");
		OutputList(L);
		printf("\n"); break;
	case 4:InitList(&L);						//尾插法建表
		printf("请输入单链表元素(以空格隔开,以0结束):");
		CreateFromTail(L);
		printf("尾插法建表成功!\n"); 
		printf("输出结果为:");
		OutputList(L);
		printf("\n"); break;
	case 5:printf("请输入要查找的结点序号:");		//按序号查找结点值
		scanf("%d", &n);
		if (Get(L, n) == NULL)
			printf("输入错误,未查到该结点!\n");
		else
			printf("结点%d的值为:%d\n", n, Get(L, n)); break;
	case 6:printf("请输入要查找的结点序号:");		//按值查找结点序号
		scanf("%d", &n);
		if (Locate(L, n) == NULL)
			printf("未查到该结点!\n");
		else
			printf("值为%d的结点序号为:%d\n", n, Locate(L, n)); break;
	case 7:printf("长度为:%d\n", GetLength(L)); break;//求表长
	case 8:ReverseList(L);						//逆置
		printf("逆置成功!输出结果为:");
		OutputList(L);
		printf("\n"); break;
	case 9:printf("请输入插入位置及结点值:");		//插入
		scanf("%d%d", &n, &e);
		InsList(L, n, e);
		printf("输出结果为:");
		OutputList(L);
		printf("\n"); break;
	case 10:printf("请输入删除位置:");			//删除
		scanf("%d", &n);
		DelList(L, n);
		printf("输出结果为:");
		OutputList(L);
		printf("\n"); break;
	case 11:AscSort(L);							//升序排序
		printf("升序排序结果为:");
		OutputList(L);
		printf("\n"); break;
	case 12:InitList(&LB);						//升序合并
		printf("请输入要合并的新单链表元素(以空格隔开,以0结束):");
		CreateFromTail(LB);
		printf("尾插法建表成功!\n");
		printf("输出结果为:");
		OutputList(LB);
		AscSort(L);
		AscSort(LB);
		MergeList(L, LB);
		printf("\n合并成功!输出结果为:");
		OutputList(L);
		printf("\n"); break;
	default:printf("输入错误!\n"); break;
	}
}

int main() {
	InitList(&L);

	printf("----------单链表基本运算----------\n");
	printf("1.初始化\t2.输出\n");
	printf("3.头插法建表\t4.尾插法建表\n");
	printf("5.按序号查找\t6.按值查找\n");
	printf("7.求表长\t8.逆置\n");
	printf("9.插入\t\t10.删除\n");
	printf("11.升序排序\t12.升序合并\n");
	printf("0.退出程序\n");
	printf("----------------------------------\n");
	int select;
	printf("请选择操作:");
	scanf("%d", &select);
	while (select) {
		Menu(select);
		printf("----------------------------------\n");
		printf("请选择下一操作:");
		scanf("%d", &select);
	}
	printf("退出成功!\n");
	return 0;
}

3. 运行结果

在这里插入图片描述
参考:耿国华《数据结构——用C语言描述(第二版)》

  • 10
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值