数据结构 第三章 线性表(二)

🚀 写在最前:在上一篇文章,学习了线性表的顺序存储——顺序表,首先定义了线性表的逻辑结构以及该数据结构的相关操作,并使用顺序存储实现了相关操作,这篇文章将使用链式存储实现线性表的相关操作,以单链表为主。

🚀:求个关注😀,让我们一起探索计算机的奥秘!

目录

一、线性表的链式存储 

1)🔊简单理解线性表的链式存储

2)🔊线性表链式存储的分类

3)🔊单链表的定义

二、单链表的相关操作

文件结构

①初始化单链表

②插入节点操作

特定位序插入(后插)

指定节点前插操作

③打印链表

④按值查找节点

⑥删除节点操作

⑦求单链表表长

三、链式存储和顺序存储对比


一、线性表的链式存储 

1)🔊简单理解线性表的链式存储

还是以绪论部分的图为例,对于一个线性表来说,它实际在计算中存储位置随机,他们的逻辑结构通过指针一一链接,就是线性表的链式存储。

 

2)🔊线性表链式存储的分类

对于链式存储来说,其通过指针链接的方式不同可分为单链表(就如上图所示)、双链表循环单链表循环双链表静态链表

这里同样的留个印象就行,后面都会具体学习这几种链表。

3)🔊单链表的定义

线性表的链式存储又称单链表,它指通过一组任意的存储春单元来存储线性表中的数据元素。对于链表中的每一个节点来说,它有一个数据域用于存储数据,一个指针域用于链接数据即存放后继节点的地址。

二、单链表的相关操作

文件结构

以下操作,都以带头节点的链表为例 。

①初始化单链表

要实现的功能,得到一个空空链表且含有一个头指针。

LinkList_one.h中的内容,在这里实际定义数据结构以及数据结构的操作。

#pragma once
#include<stdio.h>
#include<stdlib.h>

//数据元素重定义为Elemtype
typedef int Elemtype;

//定义节点类型
typedef struct LNode {
	Elemtype data;      //数据域  数据类型Elemtype
	struct LNode* next; //指针域  指向LNode类型的指针 
}LNode,*LinkList;

//定义数据结构的操作

//初始化单链表
bool InitList(LinkList &L);

//插入节点操作——特定位序插入

//指定节点前插操作

//打印链表

//按值查找节点

//删除节点操作

//求单链表表长

LinkList_one.c中的内容,在这里实际实现数据结构的操作。

bool InitList(LinkList &L) {
	//申请一个头节点
	L = (LNode*)malloc(sizeof(LNode));
	if (L == NULL) {
		printf("malloc空间申请失败");
		return false;
	}else {
		L->next = NULL;    //将头节点的next置空
	}
	return true;
}

test.c中的内容,测试实现的数据结构操作是否好用。

#include"LinkList_one.h"

int main() {
	LinkList L = NULL;  //定义一个链表指针

	//初始化链表
	if (InitList(L)) {
		printf("初始化成功!\n");
	}

	return 0;
}

运行结果:

初始化成功!

D:\C_WorkSpace\LinkList\x64\Debug\LinkList.exe (进程 2708)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .

②插入节点操作

特定位序插入(后插)

LinkList_one.h中的内容

//插入节点操作——特定位序插入
bool ListInsert(LinkList L, int i, Elemtype e);

LinkList_one.c中的内容

//插入节点操作——特定位序插入
bool ListInsert(LinkList L, int i, Elemtype e) {
	LNode* p = L;  //指针p指向当前节点
	int j = 0;     //记录指针指向第几个节点,头节点为第0个节点

	//找到第i-1个节点,在i-1个节点后面插入Elemtype e
	while (p!= NULL && j < i - 1) {
		p = p->next;
		j++;
	}
	if (p == NULL) {
		printf("插入位置不合法!");
		return false;
	}
	LNode* tmp = (LNode*)malloc(sizeof(LNode));
	if (tmp == NULL) {
		printf("空间开辟失败导致插入失败。");
	}else{
		tmp->data = e; //将数据放入新节点的数据域
		tmp->next = p->next;
		p->next = tmp;
	}
}

test.c中的内容

#include"LinkList_one.h"

int main() {
	LinkList L1 = NULL;  //定义一个链表指针

	//初始化链表
	if (InitList(L1)) {
		printf("初始化成功!\n");
	}

	ListInsert(L1, 1, 4);
	ListInsert(L1, 1, 8);
	ListInsert(L1, 3, 10);
	printf("位置1元素值是%d\n", L1->next->data);
	printf("位置2元素值是%d\n", L1->next->next->data);
	printf("位置3元素值是%d\n", L1->next->next->next->data);
	ListInsert(L1, 5, 10);
	return 0;
}

运行结果:

初始化成功!
位置1元素值是8
位置2元素值是4
位置3元素值是10
插入位置不合法!
D:\C_WorkSpace\LinkList\x64\Debug\LinkList.exe (进程 21936)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .

指定节点前插操作

LinkList_one.h中的内容

//指定节点前插操作
bool ListPriorInsert(LNode* node, Elemtype e);

LinkList_one.c中的内容

//指定节点前插操作
bool ListPriorInsert(LNode* node, Elemtype e) {
	assert(node);
	LNode* tmp = (LNode*)malloc(sizeof(LNode));
	if (tmp == NULL) {
		printf("空间申请失败导致节点插入失败\n");
		return false;
	}
	else {
		//先将节点进行尾插
		tmp->next = node->next;
		node->next = tmp;

		//节点的存储数据调换位置
		tmp->data = node->data;
		node->data = e;
		return true;
	}
}

test.c中的内容

#include"LinkList_one.h"

int main() {
	LinkList L1 = NULL;  //定义一个链表指针

	//初始化链表
	if (InitList(L1)) {
		printf("初始化成功!\n");
	}

	ListInsert(L1, 1, 4);
	ListInsert(L1, 1, 8);
	ListInsert(L1, 3, 10);
	printf("位置1元素值是%d\n", L1->next->data);
	printf("位置2元素值是%d\n", L1->next->next->data);
	printf("位置3元素值是%d\n", L1->next->next->next->data);
	ListPriorInsert(L1->next, 1000); //在第一个节点的
	printf("位置1元素值是%d\n", L1->next->data);
	return 0;
}

运行结果:

初始化成功!
位置1元素值是8
位置2元素值是4
位置3元素值是10
位置1元素值是1000

D:\C_WorkSpace\LinkList\x64\Debug\LinkList.exe (进程 17496)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .

③打印链表

LinkList_one.h中的内容

//打印链表
void ListPrint(LinkList L);

LinkList_one.c中的内容

//打印链表
void ListPrint(LinkList L) {
	if (L->next == NULL) {
		printf("此链表为空\n");
	}
	else {
		LNode* p = L->next;
		while (p!=NULL){
			printf("%d-->", p->data);
			p = p->next;
		}
        printf("\n");
	}
}

test.c中的内容

#include"LinkList_one.h"

int main() {
	LinkList L1 = NULL;  //定义一个链表指针

	//初始化链表
	if (InitList(L1)) {
		printf("初始化成功!\n");
	}

	ListInsert(L1, 1, 4);
	ListInsert(L1, 1, 8);
	ListInsert(L1, 3, 10);
	printf("位置1元素值是%d\n", L1->next->data);
	printf("位置2元素值是%d\n", L1->next->next->data);
	printf("位置3元素值是%d\n", L1->next->next->next->data);
	ListPriorInsert(L1->next, 1000); //在第一个节点的
	printf("位置1元素值是%d\n", L1->next->data);
	ListPrint(L1);
	return 0;
}

运行结果:

初始化成功!
位置1元素值是8
位置2元素值是4
位置3元素值是10
位置1元素值是1000
1000-->8-->4-->10-->
D:\C_WorkSpace\LinkList\x64\Debug\LinkList.exe (进程 16680)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .

④按值查找节点

LinkList_one.h中的内容。

//按值查找节点
LNode* LocateElem(LinkList L, Elemtype value);

LinkList_one.c中的内容。

//按值查找节点
LNode* LocateElem(LinkList L, Elemtype value) {
	LNode* p = L->next;
	while (p != NULL) {
		if (p->data == value) {
			return p;
		}
		p = p->next;
	}
	return NULL;
}

test.c中的内容。

#include"LinkList_one.h"

int main() {
	LinkList L1 = NULL;  //定义一个链表指针

	//初始化链表
	if (InitList(L1)) {
		printf("初始化成功!\n");
	}

	ListInsert(L1, 1, 4);
	ListInsert(L1, 1, 8);
	ListInsert(L1, 3, 10);
	printf("位置1元素值是%d\n", L1->next->data);
	printf("位置2元素值是%d\n", L1->next->next->data);
	printf("位置3元素值是%d\n", L1->next->next->next->data);
	ListPriorInsert(L1->next, 1000); //在第一个节点的
	printf("位置1元素值是%d\n", L1->next->data);
	ListPrint(L1);

	LNode* tmp = LocateElem(L1, 10);
	printf("tmp指向的数据为%d\n", tmp->data);
	return 0;
}

运行结果:

初始化成功!
位置1元素值是8
位置2元素值是4
位置3元素值是10
位置1元素值是1000
1000-->8-->4-->10-->
tmp指向的数据为10

D:\C_WorkSpace\LinkList\x64\Debug\LinkList.exe (进程 8984)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .

⑥删除节点操作

LinkList_one.h中的内容。

//删除节点操作
Elemtype ListDelete(LinkList L, int i);

LinkList_one.c中的内容。

//删除节点操作
Elemtype ListDelete(LinkList L, int i) {
	LNode* p = L; //p指向第一个节点
	int j = 0;    //定位指针p指向第几个节点

	//找到第i-1的节点
	while (p!= NULL && j < i - 1) {
		p = p->next;
		j++;
	}

	if (p == NULL || p->next == NULL) {
		printf("删除位置不合法");
		return false;
	}

	LNode* tmp = p->next;
	Elemtype element = tmp->data;
	p->next = tmp->next;
	free(tmp);
	return element;
}

test.c中的内容。

#include"LinkList_one.h"

int main() {
	LinkList L1 = NULL;  //定义一个链表指针

	//初始化链表
	if (InitList(L1)) {
		printf("初始化成功!\n");
	}

	ListInsert(L1, 1, 4);
	ListInsert(L1, 1, 8);
	ListInsert(L1, 3, 10);
	printf("位置1元素值是%d\n", L1->next->data);
	printf("位置2元素值是%d\n", L1->next->next->data);
	printf("位置3元素值是%d\n", L1->next->next->next->data);
	ListPriorInsert(L1->next, 1000); //在第一个节点的
	printf("位置1元素值是%d\n", L1->next->data);
	ListPrint(L1);

	LNode* tmp = LocateElem(L1, 10);
	printf("tmp指向的数据为%d\n", tmp->data);
	printf("删除之前:\n");
	ListPrint(L1);
	ListDelete(L1, 1);
	printf("删除之后:\n");
	ListPrint(L1);
	return 0;
}

运行结果:

初始化成功!
位置1元素值是8
位置2元素值是4
位置3元素值是10
位置1元素值是1000
1000-->8-->4-->10-->
tmp指向的数据为10
删除之前:
1000-->8-->4-->10-->
删除之后:
8-->4-->10-->

D:\C_WorkSpace\LinkList\x64\Debug\LinkList.exe (进程 25416)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .

⑦求单链表表长

LinkList_one.h中的内容。

//求单链表表长
int Length(LinkList L);

LinkList_one.c中的内容。

//求单链表表长
int Length(LinkList L) {
	int count = 0;
	LNode* p = L->next;
	while (p != NULL) {
		count++;
		p = p->next;
	}
	return count;
}

test.c中的内容

#include"LinkList_one.h"

int main() {
	LinkList L1 = NULL;  //定义一个链表指针

	//初始化链表
	if (InitList(L1)) {
		printf("初始化成功!\n");
	}

	ListInsert(L1, 1, 4);
	printf("表长为%d\n", Length(L1));
	ListInsert(L1, 1, 8);
	printf("表长为%d\n", Length(L1));
	ListInsert(L1, 3, 10);
	printf("位置1元素值是%d\n", L1->next->data);
	printf("位置2元素值是%d\n", L1->next->next->data);
	printf("位置3元素值是%d\n", L1->next->next->next->data);
	ListPriorInsert(L1->next, 1000); //在第一个节点的
	printf("位置1元素值是%d\n", L1->next->data);
	ListPrint(L1);
	printf("表长为%d\n", Length(L1));

	LNode* tmp = LocateElem(L1, 10);
	printf("tmp指向的数据为%d\n", tmp->data);
	printf("删除之前:\n");
	ListPrint(L1);
	printf("表长为%d\n", Length(L1));
	ListDelete(L1, 1);
	printf("删除之后:\n");
	ListPrint(L1);

	printf("表长为%d\n", Length(L1));
	return 0;
}

运行结果:

初始化成功!
表长为1
表长为2
位置1元素值是8
位置2元素值是4
位置3元素值是10
位置1元素值是1000
1000-->8-->4-->10-->
表长为4
tmp指向的数据为10
删除之前:
1000-->8-->4-->10-->
表长为4
删除之后:
8-->4-->10-->
表长为3

D:\C_WorkSpace\LinkList\x64\Debug\LinkList.exe (进程 24904)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .

三、链式存储和顺序存储对比

通过实现线性表顺序存储(顺序表)的操作和线性表链式存储(链表)的操作对比,可以总结出以下各自的优缺点。

对于顺序表:

  • 优点:随机存取(直接下标取数据,时间复杂度为O(1)),存储密度高,所有空间都用来存放数据。
  • 缺点:要求有大片的连续空间,改变存储容量不方便。

对于链表:

  • 不要求有很大的连续空间,改变存储容量方便。
  • 不可以随机存取,同时要消耗一定的空间来存储指针。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值