单链表输入和正序输出c语言,数据结构与算法——单链表及第三次实验题解

数据结构与算法——单链表及第三次实验题解

数据结构与算法——单链表及第三次实验题解

数据结构与算法——单链表及第三次实验题解

文章目录

数据结构与算法——单链表及第三次实验题解

学习思路

单链表的基本结构

单链表的基本操作

定义单链表——C++版

创建单链表

插入元素

在头部插入元素

在尾部插入元素

头指针与尾指针的区别

删除元素

遍历元素

求单链表的长度

单链表的进阶操作

输出单链表

销毁单链表

以已有数组为基础创建单链表

倒序创建——头插法

正序创建——尾插法

删除第i个元素

在第i个元素后插入值为value的结点

找到值为value的元素,并返回它的位置

题解

1:整数单链表的基本运算-1

描述

输入

输出

样例输入

样例输出

题解1

2:整数单链表的基本运算-2

描述

输入

输出

样例输入

样例输出

题解2

3:单链表的插入和显示操作

描述

输入

输出

样例输入

样例输出

题解3

这块涉及很多指针,所以对很多同学来说都会有难度。我觉的应该先抓一些基本操作,然后再去看大的算法设计是由哪些基本操作构成的。基本操作大概包括:

创建单链表;

插入元素;

删除元素;

遍历单链表;

书上的结构体、动态内存分配都是用C语言的结构体实现的,但我觉得大家可能对C++的new和delete更熟悉一点,而且C++的结构体形式也更加简单,所以后续的代码都将用C++给出

单链表是由一个个的结点组成,每个节点都是指针类型的变量,都包含数据域和指针域。其中数据域用来存储数据,指针域用来连接下一个结点:

ca31ba14856dae13ff14abf15d616823.png

我们用来存储数据的单链表是由一个个上面这样的结点组成的,对于每个节点,你可以通过它找到它下一个结点,但你不可以通过它找到它上一个结点,这就决定了要对单链表进行操作,必须找到目标结点的上一个结点

160b9b3022fa6201203363170ec626af.png

为了便于操作,我们经常给单链表添加头指针和尾指针,分别指向首结点和尾结点

定义单链表——C++版

struct node{

int data; //数据域

node *next; //指针域

};

创建单链表

与创建数组不同,最初创建单链表,其实就是创建头指针,后续的“扩大单链表”,其实就是给头指针上挂上其他的链条。

c664938a3cf5e0ccddbfcbf543c9e0a4.png

所以我建议大家直接将“创建单链表”理解为“创建头指针”,然后将其他的赋值、完善等操作理解为“给头指针挂上新的链条”。

创建头指针用到如下代码

node *head;

head=new node();

head->next=NULL;

方便起见,我们把后两句存放在一个初始化函数里:

void initList(node *&head){ //初始化函数

head=new node();

head->next=NULL;

}

这样以后创建头指针的时候,只要写:

node *head;

initList(head);

就可以创建好了。

插入元素

插入元素可以很形象地理解为在一条链子上插入新的一环。比如将结点p插入到两个结点之间,也就是插入到结点before之后,其实是这样的过程:

339a401c429b07159b33bbcfa67e68d1.png

p->next=before->next; //p的指针域连接last后面的环

before->next=p; //last的指针域连接p

在头部插入元素

如果我要在最头上插入一个新结点,那我找不到before,怎么办?

这就体现出头指针head的作用了,头指针head的存在保证了就算你想在最开始的位置插入结点,你还是能找到一个before,来完成你的操作。

p->next=head->next; //p的指针域指向原来的首结点

head->next=p; //头指针指向p,p成为新的首结点

6f726c12285b12b8351f0ed8a3e6168f.png

在尾部插入元素

在尾部插入元素时,最后的元素相当于before,但没有before->next。我们在最后加一个尾指针tail,这样插入元素时,情况又变成了在两个“结点”之间插入一个新的结点。

但值得注意的是,尾指针指向最后一个元素,而不是最后一个元素指向尾指针,所以插入操作与前面略有不同:

0d039f16861819cf5221f73719fb79b1.png

tail->next=p; //tail->next表示尾结点的指针域

tail=p; //尾指针指向新的尾结点p

头指针与尾指针的区别

需要注意的是,头指针其实是头结点的指针域,也就是说,头指针本身是一个结点,但尾指针并不是结点,它只是一个单纯的指针。

0b64b3be7465e2287a243d7cad1221dc.png

头指针通过指针域来发挥“指针”的作用,也就是说,首结点是head->next。在实际应用中,我们要输出首结点的数据,其实是这样输出的:

cout<next->data;

而尾指针就是一个纯粹的指针,尾结点就是tail,所以如果要输出尾结点的数据,是这样的:

cout<data;

在初始化单链表,也就是创建头指针的过程中,如果需要添加尾指针,应该是这样的:

void initList_tail(node *&head){

head=new node();

head->next=NULL;

node *tail=head;

}

因为刚创建头指针的过程中,头指针本身就是最后一个元素,所以按照尾指针的定义,应该让尾指针指向最后一个元素,也就是指向头指针。

删除元素

删除元素也可以类比取下链条中的某一环,并把这一环销毁。思路应该是创建一个临时指针,用来代表这个即将被销毁的结点,然后重新连接链条,并销毁待删除的结点。

54ff0c7283bd674247ae2be393838bc0.png

node *q=before->next;

before->next=q->next;

delete q;

遍历元素

单链表的遍历特别有顺藤摸瓜捣毁黑帮的感觉,就像你手里掌握着黑帮老大——头指针,然后通过他,你能找到他的小弟,然后找到小弟的小弟…最后找到的那个人,他是黑帮最底层的人,他没有小弟——NULL。

需要注意的有两点:

黑帮成员之间有鲜明的等级观念,只有上级能联系下属,下属无法联系上级,也就是说单链表的遍历是单向的

黑帮老大是个重要人物,不能出事,因为我们一旦失去了黑帮老大,就再也控制不住整个黑帮了。所以每次遍历的时候,我们需要额外找个跑腿的——工作指针p,用它来联系小弟们,以保证老大head不被改变。

综上,遍历的操作应该是:

void findBro(node *head){

node *p=head; //工作指针

//或:node *p=head->next;

while(p!=NULL){ //判断当前的人是不是最底层小弟

//操作

p=p->next; //找他的小弟

}

}

求单链表的长度

掌握了遍历的方法后,我们可以在遍历过程中夹带一点私货,比如记录一下这个黑帮一共有多少人——求单链表长度:

int getLength(node *head){ //求长度

int count=0;

node *p=head->next; //工作指针

while(p!=NULL){

p=p->next;

count++;

}

return count;

}

有了count的引入,就为我们的操作带来了更多的可能,比如判断if(count==i),以实现找到第i个元素等,在后面的部分会给出相应的代码

输出单链表

void output(node *head){ //其实就是在遍历过程中加一个输出

node *p=head->next;

while(p!=NULL){

cout<data<

p=p->next;

}

cout<

}

销毁单链表

拆分为基本操作:遍历+单个元素删除

void destoryList(node *&head){ //销毁

node *pre=head,*p=head->next; //从头结点开始删除

while(p!=NULL){

delete pre;

pre=p;

p=p->next;

}

}

以已有数组为基础创建单链表

倒序创建——头插法

拆分为基本操作:不断地往表头插入新结点

void createList(node *&head,int a[],int len){ //已有数组a,数组长度len

for(int i=0;i

node *s=new node(); //创建一个游离的新结点

s->data=a[i]; //给新结点赋值

s->next=head->next; //把新结点插入链表的前端

head->next=s;

}

}

正序创建——尾插法

拆分为基本操作:不断地往表尾插入新的结点

void createList_tail(node *head,int a[],int len){

node *tail=head;

for(int i=0;i

node *s=new node(); //创建一个游离的新结点

s->data=a[i]; //给新结点赋值

tail->next=s; //把新结点插入链表的尾端

tail=s;

}

}

删除第i个元素

拆分为基本操作:遍历到第i-1个元素的位置,删除它后面的元素

void deleElem(node *&head,int i){ //删除第i个元素

node *p=head; //工作指针

int count=1;

while(p!=NULL&&count!=i){

p=p->next;

count++;

}

node *q=p->next; //基本操作——删除

p->next=q->next;

delete q;

}

在第i个元素后插入值为value的结点

拆分为基本操作:遍历到第i个元素的位置,在它后面插入新的元素

void insElem(node *&head,int value,int i){ //在第i个元素后插入值为value的结点

node *p=head->next; //工作指针

int count=1;

while(p!=NULL&&count!=i){

p=p->next;

count++;

}

node *q=new node(); //创建

q->data=value;

q->next=p->next;

p->next=q;

}

ps. i=0时在头结点后直接插入结点

找到值为value的元素,并返回它的位置

拆分为基本操作:遍历单链表直到找到value,然后输出此时的count

int findValue(node *head,int value){

node *p=head->next;

int count=1;

while(p!=NULL&&p->data!=value)

{

p=p->next;

count++;

}

return count;

}

1:整数单链表的基本运算-1

题目链接:传送门

描述

设计整数单链表的基本运算程序,并用相关数据进行测试

输入

顺序输入单链表A的各个元素

输出

第一行:创建单链表A后,输出所有元素

第二行:删除第一个元素,输出删除后的所有元素

第三行:输出删除元素后表的长度

第四行:在第二元素处插入一个新的元素100

第五行:输出第一个元素100所在位置

样例输入

1 2 3 4 0 9

样例输出

1 2 3 4 0 9

2 3 4 0 9

5

2 100 3 4 0 9

2

题解1

#include

using namespace std;

struct node{ //定义链表

int data;

node *next;

};

//头结点用head表示

void initList(node *&head){ //初始化函数

head=new node();

head->next=NULL;

}

void destoryList(node *&head){ //销毁链表

node *pre=head,*p=head->next; //从头结点开始删除

while(p!=NULL){

delete pre;

pre=p;

p=p->next;

}

}

int getLength(node *head){ //求长度

int count=0;

node *p=head->next;

while(p!=NULL){

p=p->next;

count++;

}

return count;

}

int findValue(node *head,int value){ //找到值为value的结点

node *p=head->next;

int count=1;

while(p!=NULL&&p->data!=value)

{

p=p->next;

count++;

}

return count;

}

void insElem(node *&head,int value,int i){ //在第i个元素后插入值为value的结点

node *p=head->next;

int count=1;

while(p!=NULL&&count!=i){

p=p->next;

count++;

}

node *q=new node();

q->data=value;

q->next=p->next;

p->next=q;

}

void deleElem(node *&head,int i){ //删除第i个元素

node *p=head;

int count=1;

while(p!=NULL&&count!=i){

p=p->next;

count++;

}

node *q=p->next;

p->next=q->next;

delete q;

}

void output(node *head){ //输出

node *p=head->next;

while(p!=NULL){

cout<data<

p=p->next;

}

cout<

}

void createList_tail(node *head,int a[],int len){ //尾插法创建链表

node *tc=head;

for(int i=0;i

node *s=new node();

s->data=a[i];

tc->next=s;

tc=s;

}

}

int main()

{

node *head;

initList(head);

int a[10000];

for(int i=0;i<6;i++)cin>>a[i];

createList_tail(head,a,6);

output(head);

deleElem(head,1);

output(head);

cout<

insElem(head,100,1);

output(head);

cout<

destoryList(head);

return 0;

}

2:整数单链表的基本运算-2

题目链接:传送门

描述

设计有序整数单链表的插入运算程序,并用相关数据进行测试

输入

按升序顺序输入单链表A的各个元素和待插入元素

输出

第一行:创建单链表A后,输出所有元素

第二行:输出按照升序插入后的所有元素

样例输入

0 1 2 3 4 9

7

样例输出

0 1 2 3 4 9

0 1 2 3 4 7 9

题解2

这个题需要稍微拐一个弯,就是需要找到插入位之前的元素,所以在比较大小的过程中,需要把手伸的长一点,即如果下一个元素的数值大于待插入元素,那么工作指针p停留在当前元素:

#include

using namespace std;

struct node{

int data;

node *next;

};

//头结点用head表示

void initList(node *&head){ //无值初始化

head=new node();

head->next=NULL;

}

void destoryList(node *&head){ //销毁

node *pre=head,*p=head->next; //从头结点开始删除

while(p!=NULL){

delete pre;

pre=p;

p=p->next;

}

}

void output(node *head){

node *p=head->next;

while(p!=NULL){

cout<data<

p=p->next;

}

cout<

}

void createList_tail(node *head,int a[],int len){

node *tc=head;

for(int i=0;i

node *s=new node();

s->data=a[i];

tc->next=s;

tc=s;

}

}

int main()

{

node *head;

initList(head);

int a[1000],value;

for(int i=0;i<6;i++)cin>>a[i];

cin>>value;

createList_tail(head,a,6);

output(head);

node *p=head->next;

while(p!=NULL&&p->next->data<=value)

p=p->next;

node *s=new node();

s->data=value;

s->next=p->next;

p->next=s;

output(head);

destoryList(head);

return 0;

}

3:单链表的插入和显示操作

题目链接:传送门

描述

使用尾插法创建链表,查找链表值最大结点(假定当前链表最大值唯一),在最大值结点后插入一个比最大值大10的结点。

#include

#include

using namespace std;

typedef int ElemType;

#define MAX_SIZE 100

typedef struct node

{

ElemType data;//数据域

struct node *next;//指针域

} SLinkNode;//单链表结点类型

void CreateListR(SLinkNode *&L, ElemType a[], int n)//尾插法建表

{

SLinkNode *s, *tc; int i;

L = (SLinkNode *)malloc(sizeof(SLinkNode));//创建头结点

tc = L;//tc为L的尾结点指针

for (i = 0; i < n; i++)

{

s = (SLinkNode *)malloc(sizeof(SLinkNode));

s->data = a[i];//创建存放a[i]元素的新结点s

tc->next = s;//将s结点插入tc结点之后

tc = s;

}

tc->next = NULL;//尾结点next域置为NULL

}

int InsElemSpe(SLinkNode *&L)//插入结点

{

// 在此处补充你的代码

return 1;//插入运算成功,返回1

}

void DispList(SLinkNode *L)//输出单链表

{

SLinkNode *p = L->next;

while (p != NULL)

{

cout<data<

p = p->next;

}

cout<

}

void DestroyList(SLinkNode *&L)//销毁单链表L

{

SLinkNode *pre = L, *p = pre->next;

while (p != NULL)

{

free(pre);

pre = p; p = p->next;//pre、p同步后移

}

free(pre);

}

int main()

{

ElemType a[MAX_SIZE];

SLinkNode *L;

int nlength;

cin >> nlength;

for (int i = 0; i < nlength; i++)

cin >> a[i];

CreateListR(L, a, nlength);

InsElemSpe(L);

DispList(L);

DestroyList(L);

return 0;

}

输入

输入分两行数据,第一行是尾插法需要插入的数据的个数,第二行是具体插入的数值。

输出

按程序要求输出

样例输入

4

40 50 70 65

样例输出

40 50 70 80 65

题解3

只给出自己写的那部分函数:

int InsElemSpe(SLinkNode *&L)

{

// 在此处补充你的代码

SLinkNode *p=L->next;

int max_value=0;

while (p!=NULL){

max_value=max(max_value,p->data);

p=p->next;

}

SLinkNode *pp=L->next;

while(pp!=NULL&&pp->data!=max_value)

pp=pp->next;

SLinkNode *q=(SLinkNode*)malloc(sizeof(SLinkNode));

q->data=max_value+10;

q->next=pp->next;

pp->next=q;

return 1;//插入运算成功,返回1

}

数据结构与算法——单链表及第三次实验题解相关教程

数据结构详解系列第一章 绪论

数据结构详解系列:第一章 绪论 本章主要需要了解数据结构中的一些基本概念,区别逻辑结构和物理结构。 文章目录 前言 总览: ①基本概念和术语 总览: ①基本概念和术语 数据: ? 能够描述客观事物属性,且能够输入到计算机中 被识别和处理的符号集合 。 数

高阶数据结构之AVL树

高阶数据结构之AVL树 AVL树的插入实现 AVL树是二叉平衡树,即在AVL树中任何节点的两个子树的高度最大差别为1,所以它也被称为高度平衡树。 二叉搜索树 特征: 任取树中的结点,要求结点的左子树中的所有key全部小于结点的key 结点的右子树中的所有key全部大于

数据结构详解系列:第二章 线性表

数据结构详解系列:第二章 线性表 前言 本章主要详解了线性表(逻辑结构)对应的顺序表和链表(存储结构)的实现方式,其中包含增删改查的操作的多种实现方法及代码,最后还介绍了一些特殊链表的引入,以解决现有的存储结构存在的一些问题缺陷。 文章目录 前

最短路径算法

最短路径算法 为什么80%的码农都做不了架构师? 问题描述:给你一个顶点做源点,你想要知道,如何从源点到达其他所有点的最短路径。 OK,这个问题看起来没什么用。我们一般想知道的是A点到B点的最短路径,这个单源最短路径问题告诉我们A点到所有点的最短路径

【算法系列 四】 String

【算法系列 四】 String 为什么80%的码农都做不了架构师? 1. 字符串循环左移(九度OJ1362),要求时间复杂度O(N),空间复杂度O(1) 这是一道基本的题目,简单说来就是三次翻转 比如:abcdef 左移两位 cdefab 过程: ab 翻转 ba cdef 翻转 fedc 将上面两个翻转后

从代码出发:多层全连接神经网络反向传播算法代码剖析

从代码出发:多层全连接神经网络反向传播算法代码剖析 BP反向传播算法剖析 代码 运行过程 最后结果可视化 数据分布 最后结果 ‘’’ 写在前面: 复习了一边BP算法,照着书上代码研究了一遍,加了些注释。代码看起来还能接受,自己照着写出来的就出现各种问题

数据结构详解系列:第三章 栈和队列

数据结构详解系列:第三章 栈和队列 前言 本章主要详解了栈和队列以及特殊矩阵的压缩知识,其中包括栈的顺序存储和链式存储,队列的顺序存储以及链式存储,循环队列的顺序实现以及优化改进-双端队列,最后介绍了三种特殊矩阵的存储结构压缩算法。 文章目录 前

优秀数据结构学习 - 共享内存无锁队列的实现(二)

优秀数据结构学习 - 共享内存无锁队列的实现(二) 1 关键技术 操作系统提供的进程间通信机制有文件、socket、消息队列、管道、共享内存等。其中,共享内存是最快的IPC机制[6]。 共享内存映射到进程空间后,数据可以直接从共享内存进行读写,不需要执行系统调

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
算法与数据结构它们分别涵盖了以下主要内容: 数据结构(Data Structures): 逻辑结构:描述数据元素之间的逻辑关系,如线性结构(如数组、链表)、树形结构(如二叉树、堆、B树)、图结构(有向图、无向图等)以及集合和队列等抽象数据类型。 存储结构(物理结构):描述数据在计算机中如何具体存储。例如,数组的连续存储,链表的动态分配节点,树和图的邻接矩阵或邻接表表示等。 基本操作:针对每种数据结构,定义了一系列基本的操作,包括但不限于插入、删除、查找、更新、遍历等,并分析这些操作的时间复杂度和空间复杂度。 算法: 算法设计:研究如何将解决问题的步骤形式化为一系列指令,使得计算机可以执行以求解问题。 算法特性:包括输入输出、有穷性、确定性和可行性。即一个有效的算法必须能在有限步骤内结束,并且对于给定的输入产生唯一的确定输出。 算法分类:排序算法(如冒泡排序、快速排序、归并排序),查找算法(如顺序查找、二分查找、哈希查找),图论算法(如Dijkstra最短路径算法、Floyd-Warshall算法、Prim最小生成树算法),动态规划,贪心算法,回溯法,分支限界法等。 算法分析:通过数学方法分析算法的时间复杂度(运行时间随数据规模增长的速度)和空间复杂度(所需内存大小)来评估其效率。 学习算法与数据结构不仅有助于理解程序的内部工作原理,更能帮助开发人员编写出高效、稳定和易于维护的软件系统。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值