线性表:关于链表(主要以单链表为例)的相关理解和应用

news2024/9/17 9:25:48

多清澈这天空 晴雨相拥 同心逐梦!

坚守我信心  一路出众!!

首先,按照惯例,欢迎大家边听歌边观看本博客

▶ 紫荆花盛开 (163.com)(建议复制链接,浏览器打开,csdn打开太慢了)

2022香港回归祖国25周年主题歌曲,好听!!!

一.单链表

首先 大家肯定知道链表的表达方式,如下

typedef struct LNode
{
    ElemType data;
    struct LNode *next;

}LinkNode;

定义一个LNode,node节点,结点,L是link,表示链接,所以叫做LNode链接结点构成链表

第一个存放元素的信息,链表的特点就是多存了一个节点(指针域)指向后继节点。

0.1稍微浅浅的给大家补充一下结构体的知识点:

1.一个正常的结构体代码编写

struct
{
int num;
char name[20];
char sex;
float score;
}boy1,boy2;

你可以直接在boy2后面打个{ }初始化,在使用boy的时候我们可以具体到一个成员如boy1.num 即为boy1结构体的num成员

然后接下来其实更加常见的定义是这样子的

struct stu
{
int num;
char name[20];
char sex;
float score;
}*boy1,boy2; //boy1是结构体指针,boy2是结构体

这时候我们对指向结构体的指针标表示的成员方式有所不同,可表示为(*boy1).num或者boy1->num(熟悉吧,这里算是补充一下知识点)都可以。一般配合malloc(与free)或者c++中的new(delete)函数使用,主要是结构体指针自创立开始指向的空间就不存在,所以我们得开辟一个空间给他们. 

1.1理解一些易混概念

        本来吧,其实我真想按课本讲一遍,但是感觉有点浪费时间,而且课本那些加深不了我的理解,所以干脆我就直接把我的理解说了,如果有错,希望各位大佬指正!

温馨提示:接下来的话可能有点绕口令!!

第一:节(结)点

我们知道一个节点包括了他的数据域和他的指针域

数据域存放他的data,指针域存放下一个节点的地址,其中在第一个的叫做头结点,头结点链接的第一个节点叫做首节点,最后一个节点叫做尾结点。经常会出现R指针,我管他叫标记指针,用于记住我们要操作的节点的下一个地址,免得链表丢失,至少现在我知道的就是这点,其中^表示NULL。

ok,接下来,来解决几个知识,保证我们看得到接下来的代码,懂了应该就会写了吧

1.对指针赋值相当于就是让指针指向哪里

1.1:比如Lode *r=L;让r指向头结点L。

1.2:pre=p;(pre和p都是指针),让pre指向p(指向的地址)

1.3:p=p->next p指向下一位(详细一点:p->next在等号后面表示解引用,指的是p指向的结构体的指针域的地址(表示这个结构体的下一个地址))

1.4:r->next=p 这个指的是让r对应的结构体指向p(而非指针r指向p,此时的r相当于标记指针)

1.5:s->next=p->next;p->next=s;像这种我们就推荐画图理解

1.6:p=L->next->next表示p指向L的下下指针域而L->next->next=NULL则指的是L的下一个节点的指针域为空,如下图所示

 

 2.链表的基本功能

相信大家已经对链表有了初步的了解,现在我们来一个一个实现他们

2.1链表创建与初始化

typedef char ch
typedef struct LNode
{
    ch data;
    struct LNode* next;
}Linknode;
初始化
void InitLnode(LinkNode *&L){
L=new LinkNode;
L->next=NULL;
}

 2.2插入

这里就不得不讲讲插入的两种方法

2.2.1头插法

思路示意图如下:

LinkList Headinster(LinkList &L,int n){
    LNode *s;
    int x=1;
	L= (LinkList)malloc(sizeof(LNode));
    L->data=x++;
    L->next=NULL;
    while(x!=n){
        s=(LNode*) malloc(sizeof(LNode));
        s->data=x;
        s->next=L;
        L=s;
        x++;
    }
    return L;
}

 

核心代码

s->next=L->next;		①
L->next=s;				②

 作用效果就是从头结点和首节点插入新的元素,从而导致先插进来的反而在后面

这里借用一下大佬的动图(侵权删)

2.2.2尾插法

顾名思义就是从尾部开始插入由于新的节点插入后成为新的尾部,所以我们需要用一个指针R去更新尾部节点(始终指向尾部)

大概像这样子

LinkList TailInster(LinkList &L,int n){
	int x=1;
	L= (LinkList)malloc(sizeof(LNode));
	LNode *s,*r=L;
	while(x!=n){
   		s=(LNode*) malloc(sizeof(LNode));
   		s->data=x;
  	 	r->next=s;
   		r=s;
   		x++;
	}
	r->next=NULL;
	return L;
}

核心代码

r->next=s;			//①r的指针域指向S(让新结点插入到链表)
r=s;				//②r指针指向s(保持r指针一直在链表尾端,方便插入新的结点)

 那么 现在我们继续写这个尾插法

//插入(使用尾插法)
void InsertLnode(LinkNode *&L,ch a[],int n)
{
    LinkNode *s,*r;
    r=L;
    for(int i=0;i<n;i++)
    {
        s=new LinkNode;
        s->next=a[i];
        r->next=s;
        r=s;
    }    
    r->next=NULL;
}

2.3链表的展示

void DisplayLinkNode(LinkNode *L)
{
    LinkNode *p=L->next;
    while(p!=NULL)
    {
        cout<<p->data<<"   ";
        p=p->next;
    }
    cout<<endl<<endl;
}

 2.4链表的长度

int LinkNodeLength(LinkNode *L)
{
    LinkNode *p=L;
    int n=0;    
    while(p->next!=NULL)//注意头结点不算我们的链表长度
    {
        n++;
        p=p->next;
    }
    return n; 
}

2.5链表中取值

bool Getlink(LinkNode *L,int n,ch &e)
{
    if(n<=0)
        return false;
    LinkNode *p=L;
    int j=0;
    while(j<n&&p!=NULL)
    {
        j++;
        p=p->next;
    }
    if(p==NULL)
        return false;
    else
    {
        e=p->data;
        return true;
    }
}

记忆方法:创建指针,遍历一下,判断是否为空,否则为可取之值 

 2.6链表中删除

bool DeleteNode(LinkNode *&L,int n,ch &e)
{
   if(n<=0) 
        return false;
    LinkNode *p=L,q;
    int j=0;
    while(j<n-1&&p!=NULL)//注意删除代码停在删除元素的前面
    {
        j++;
        p=p->next;
    }
    if(p==NULL)
        return false;
     q=p->next;
     if(q==NULL)
        return false
     e=q->data;
     p->next=q->next;
     delete q;
     return true;  

}

记忆方法 建立两个指针,遍历到删除元素的前一个,用p指向删除元素,然后进行交换

2.7新的插入

bool insertnode(LinkNode *&L,int i,ch e)
{
     if(i<=0)
        return false;
    LinkNode *p=L,*s;
    int j=0;
    while(j<n&&p!=NULL)
    {
        j++;
        p=p->next;
    }
    if(p==NULL||p->next==NULL)
        return false;
    s=new LinkNode;
    s->data=e;
    s->next=p->next;
    p->next=s;
    return true;
}

记忆方法:创建一个新节点,类似头插法的方式插进去 

小作业 

#include <iostream>
using namespace std;
typedef char ch;
typedef struct LNode {
	ch data;
	struct LNode*next;//指针域:存放下一个节点的地址
} LinkNode;
//初始化
void InitList(LinkNode *&L) {
	L = new LinkNode;
	L->next = NULL;
}
//插入(尾插法)
void Insertlist(LinkNode *&L, ch a[], int n) { //传入想插入的数组(1,2,3,4),则此法插完后亦是(1,2,3,4)
	LinkNode *s, *r; //创建两个指针
	r = L; //r指向首节点L
	for (int i = 0; i < n; i++) {
		s = new LinkNode; //创建空间
		s->data = a[i]; //数据存储(值得注意的是s是指针)
		r->next = s; //r->next表示L的next指针域,其实就是赋值,将s的地址(s本身就是地址)赋给L的next域,相当于是L--s
		r = s; //然后将r指向s
	}
	r->next = NULL; //最后r指向末节,指针域为NULL
}
//输出
void Displaylist(LinkNode *L) {
	LinkNode *p = L->next; //这里注意一下,其实首节点是没有数据的,直接指向下一个节点打印
	while (p != NULL) {
		cout << p->data << "  ";
		p = p->next;
	}
	cout << endl << endl;
}
//输出长度
int Listlength(LinkNode *L) {
	int n = 0;
	LinkNode *p = L;
	while (p->next != NULL) {
		n++;
		p = p->next;
	}
	return n;
}
//判断是否为空
bool emptyelem(LinkNode *L) {
	if (L->next == NULL)
		return false;
	else
		return true;
}
//取值
bool Getelem(LinkNode *L, int n, ch &e) {
	if (n <= 0)
		return false;
	LinkNode *p = L;
	int j = 0;
	while (j < n && p != NULL) {
		j++;
		p = p->next;
	}
	if (p == NULL)
		return false;
	e = p->data;
	return true;
}
//输出位置
int Locelem(LinkNode *L, ch e) {
	LinkNode *p = L->next;
	int j = 1;
	while (p != NULL && p->data != e) {
		p = p->next;
		j++;
	}
	if (p == NULL) {
		return 0;
	}

	return j;
}
//指定位置插入元素
bool insertlist(LinkNode *&L, int n, ch q) {
	if (n <= 0)
		return false;
	int j = 0;
	LinkNode *p = L, *s;
	while (j < n - 1 && p != NULL) {
		j++;
		p = p->next;
	}
	if (p==NULL||p->next == NULL)
		return false;
	else {
		s = new LinkNode;
		s->data = q;
		s->next = p->next;
		p->next = s;
		return true;
	}
}
//删除指定元素
bool Delem(LinkNode *&L, int n, ch &e) {
	if (n <= 0)
		return false;
	int j = 0;
	LinkNode *p = L, *q;
	while (j < n - 1 && p != NULL) {
		j++;
		p = p->next;
	}
	if (p == NULL)
		return false;
	q = p->next;
	if (q == NULL)
		return false;
	e = q->data;
	p->next = q->next;
	delete q;
	return true;
}
//释放
void Destroylist(LinkNode *&L) {
	delete L;
}

int main() {
	LinkNode *L1;
	InitList(L1);
	cout << "1.初始化单链表成功!!!" << endl << endl;
	//插入
	ch a[10] = {'a', 'b', 'c', 'd', 'e'};
	cout << "2.依次插入abcde." << "尾插法" << "    ";
	Insertlist(L1, a, 5);
	cout << "插入成功!!!" << endl << endl;
	//打印
	cout << "3.当前的单链表为:  ";
	Displaylist(L1);
	//输出长度
	int n = Listlength(L1);
	cout << "4.当前单链表的长度为:" << n << endl << endl;
	//判断链表是否为空
	cout << "5.当前链表";
	if (emptyelem(L1))
		cout << "不为空表" << endl << endl;
	else
		cout << "为空表" << endl << endl;
	//取值(输出元素)
	cout << "6.取值操作:";	int l;ch e;
	cout << "请输入您要取哪个位置的值:";cin >> l;
	if (Getelem(L1, l, e))
		cout << "取值成功! " << "单链表第" << l << "位的元素是:" << e << endl << endl;
	else
		cout << "取值失败,您输入的位置" << l << "越界!!!" << endl << endl;
	//查找
	cout << "7.查找操作:";ch find;
	cout << "请输入您要查找的元素:  ";
	cin >> find;
	if (Locelem(L1, find) == 0)
		cout << "对不起,当前单链表中没有您查找的元素!!" ;
	else
		cout << "查找成功,您所查找的元素" << find << "在当前单链表的第" << Locelem(L1, find) << "位" ;
	cout<<endl<<endl;
	cout << "8.插入操作:";int k;ch q;
	cout << "请您输入一个数字和一个字符,代表在第几位插入一个字符:";cin >> k;cin >> q;
	if (!insertlist(L1, k, q))
		cout << "Warning:输入序号越界,插入失败!!!" << endl;
	else
		cout << "插入成功!!!" << endl << endl;
	cout << "9.当前单链表的元素有:";
	Displaylist(L1);
	cout << "10.删除操作:";int y;ch o;
	cout << "请您输入要删除的元素的序号:";cin >> y;
	if (!Delem(L1, y, o))
		cout << "对不起,您的输入的序号有误(越界),删除失败!" << endl << endl;
	else
		cout << "删除成功!成功删除第" << y << "个元素" << o << endl << endl;
	cout << "11.当前单链表的元素有:";Displaylist(L1);
	//释放
	cout << "12.销毁单链表:";Destroylist(L1);
	cout << "销毁成功!!!";
	return 0;
}

 全部失败的样例:9,p,8p,7

全部成功的样例:4,c,2p,6

所以建议大家自己打吧,看完上面的写这个代码就很轻松 

感谢您今天的捧场,敬请期待下次演出。 See you next  illusion.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1535823.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mysql数据类型和常用函数

目录 1.整型 1.1参数signed和unsigned 1.2参数zerofill 1.3参数auto_increment 2.数字类型 2.1floor()向下取整 2.2随机函数rand() 2.3重复函数repeat() 3.字符串类型 3.1length()查看字节长度&#xff0c;char_length()查看字符长度 3.2字符集 3.2.1查看默认字符…

工程信号的去噪和(分类、回归和时序)预测

&#x1f680;【信号去噪及预测论文代码指导】&#x1f680; 还为小论文没有思路烦恼么&#xff1f;本人专注于最前沿的信号处理与预测技术——基于信号模态分解的去噪算法和深度学习的信号&#xff08;回归、时序和分类&#xff09;预测算法&#xff0c;致力于为您提供最精确、…

MySql实战--深入浅出索引(下)

在开始这篇文章之前&#xff0c;我们先来看一下这个问题&#xff1a; 在下面这个表T中&#xff0c;如果我执行 select * from T where k between 3 and 5&#xff0c;需要执行几次树的搜索操作&#xff0c;会扫描多少行&#xff1f; 下面是这个表的初始化语句。 图1 InnoDB的索…

第 6 章 ROS-xacro练习(自学二刷笔记)

重要参考&#xff1a; 课程链接:https://www.bilibili.com/video/BV1Ci4y1L7ZZ 讲义链接:Introduction Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程 6.4.3 Xacro_完整使用流程示例 需求描述: 使用 Xacro 优化 URDF 版的小车底盘模型实现 结果演示: 1.编写 X…

ChatGPT已经掌控了全局:不仅写论文的在用ChatGPT,同行评审也在用ChatGPT!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

分库分表场景下多维查询解决方案(用户+商户)

在采用分库分表设计时&#xff0c;通过一个PartitionKey根据散列策略将数据分散到不同的库表中&#xff0c;从而有效降低海量数据下C端访问数据库的压力。这种方式可以缓解单一数据库的压力&#xff0c;提升了吞吐量&#xff0c;但同时也带来了新的问题。对于B端商户而言&#…

【Python爬虫】网络爬虫:信息获取与合规应用

这里写目录标题 前言网络爬虫的工作原理网络爬虫的应用领域网络爬虫的技术挑战网络爬虫的伦理问题结语福利 前言 网络爬虫&#xff0c;又称网络爬虫、网络蜘蛛、网络机器人等&#xff0c;是一种按照一定的规则自动地获取万维网信息的程序或者脚本。它可以根据一定的策略自动地浏…

linux查看usb是3.0还是2.0

1 作为device cat /sys/devices/platform/10320000.usb30drd/10320000.dwc3/udc/10320000.dwc3/current_speed 如下 high-speed usb2.0 super-speed usb3.0 2 作为host linux下使用以下命令查看 &#xff0c;如果显示 速率为5G, 则为USB 3.0&#xff0c; USB2.0通常显示速率…

Day17|二叉树part04:110.平衡二叉树、257.二叉树的所有路径、404.左叶子之和、543: 二叉树的直径、124: 二叉树的最大路径和

之前的blog链接&#xff1a;https://blog.csdn.net/weixin_43303286/article/details/131982632?spm1001.2014.3001.5501 110.平衡二叉树 本题中&#xff0c;一棵高度平衡二叉树定义为&#xff1a;一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。思路&#xff…

Matlab之已知2点绘制长度可定义的射线

目的&#xff1a;在笛卡尔坐标系中&#xff0c;已知两个点的位置&#xff0c;绘制过这两点的射线。同时射线的长度可以自定义。 一、函数的参数说明 输入参数&#xff1a; PointA&#xff1a;射线的起点&#xff1b; PointB&#xff1a;射线过的零一点&#xff1b; Length&…

AI PPT生成工具 V1.0.0

AI PPT是一款高效快速的PPT生成工具&#xff0c;能够一键生成符合相关主题的PPT文件&#xff0c;大大提高工作效率。生成的PPT内容专业、细致、实用。 软件特点 免费无广告&#xff0c;简单易用&#xff0c;快速高效&#xff0c;提高工作效率 一键生成相关主题的标题、大纲、…

【链表】Leetcode 138. 随机链表的复制【中等】

随机链表的复制 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成&#xff0c;其中每个新节点的值都设为其对应的原节点…

Linux - 应用层HTTPS、传输层TCP/IP模型中典型协议解析

目录 应用层&#xff1a;自定制协议实例 HTTP协议首行头部空行正文http服务器的搭建 HTTPS协议 传输层UDP协议TCP协议 应用层&#xff1a; 应用层负责应用程序之间的沟通—程序员自己定义数据的组织格式 应用层协议&#xff1a;如何将多个数据对象组织成为一个二进制数据串进行…

代码签名证书被吊销的原因及其后果是什么?

代码签名证书是确保软件代码完整性和可信度的关键工具&#xff0c;然而&#xff0c;在某些情况下&#xff0c;此类证书可能会被撤销。这意味着证书颁发机构&#xff08;CA&#xff09;不再认可该证书的有效性&#xff0c;并宣布其失效。本文将解析导致代码签名证书撤销的原因、…

Bytebase 2.14.1 - 分支 (Branching) 功能支持 Oracle

&#x1f680; 新功能 分支 (Branching) 功能支持 Oracle。为 SQL 编辑器添加了项目选择器。 新增 SQL 审核规范&#xff1a; 禁止混合 DDL、DML 语句。禁止对同一张表进行不同类型的 DML 变更 (UPDATE,INSERT,DELETE)。 &#x1f514; 重大变更 工作空间设置中的「数据访问…

puppeteer使用示例云顶之弈官网

自己从0到1开发的&#xff0c;微信小程序【云顶宝藏】求求点个5星好评吧&#xff01; 需求&#xff1a;拿到所有英雄的信息 思路&#xff1a;点击每个英雄&#xff0c;进入英雄详情页&#xff0c;拿信息&#xff0c;并返回&#xff0c;继续下一个英雄** 最终效果 本地环境 win…

【链表】Leetcode 148. 排序链表【中等】

排序链表 给你链表的头结点 head &#xff0c;请将其按 升序 排列并返回 排序后的链表 。 示例 1&#xff1a; 输入&#xff1a;head [4,2,1,3] 输出&#xff1a;[1,2,3,4] 解题思路 对链表进行升序排序&#xff0c;可以使用归并排序&#xff08;Merge Sort&#xff09;的…

vben admin上线后接口调用成功,控制台报promise错误

vben admin上线后接口调用成功&#xff0c;控制台报promise错误 问题原因 vben admin框架对返回的数据格式做了统一处理&#xff0c;不满足格式要求的接口返回直接抛异常。 解决方案 将返回参数校验关闭&#xff0c;重新打包部署。

windowsVMware虚拟机中扩展linux磁盘空间

1.虚拟磁盘扩容 VM中&#xff0c;关闭linux虚拟机&#xff0c;直接编辑虚拟机-硬盘-扩展磁盘容量 2.通过Gparted工具进行LINUX系统磁盘分区 未分区挂载前可以看到/挂载点下空间为20G&#xff1a; 通过虚拟机-快照-拍摄快照&#xff0c;操作前可拍摄快照&#xff08;便于恢复之前…

Elasticsearch数据写入、检索流程及底层原理全方位解析

码到三十五 &#xff1a; 个人主页 心中有诗画&#xff0c;指尖舞代码&#xff0c;目光览世界&#xff0c;步履越千山&#xff0c;人间尽值得 ! 目录 ✍&#x1f3fb;序言✍&#x1f3fb;1️⃣✍&#x1f3fb;es的架构简介1. 分布式架构2. 索引与搜索3. 数据写入与持久化4. 缓…