啊哈 算法读书笔记 第 2 章 栈、队列、链表

news/2024/4/26 3:54:42/文章来源:https://blog.csdn.net/shaozheng0503/article/details/129192485

第 2 章 栈、队列、链表

目录

第 2 章 栈、队列、链表

队列:

解密回文——栈

纸牌游戏:

链表

模拟链表 

队列:

首先将第 1 个数删除,紧接着将第 2 个数放到这串数的末尾,再将第 3 个数删除并将第 4 个数放到这串数的末尾,再将第 5 个数删除……直到剩下最后一个数,将最后一个数也删除。按照刚才删除的顺序,把这些删除的数连在一起就是小哈的 号码啦。现在你来帮帮小哼吧。小哈给小哼加密过的一串数是“6 3 1 7 5 8 9 2 4”。

要去用程序来解决的话: 

#include <stdio.h> 
int main() 
{ int q[102]={0,6,3,1,7,5,8,9,2,4},head,tail; int i; //初始化队列head=1; tail=10; //队列中已经有9个元素了,tail指向队尾的后一个位置 while(head<tail) //当队列不为空的时候执行循环{ //打印队首并将队首出队printf("%d ",q[head]); head++; //先将新队首的数添加到队尾q[tail]=q[head]; tail++; //再将队首出队head++; } getchar();getchar(); return 0; 
}

队列的概念:

队列是一种特殊的线性结构,它只允许在队列的首部(head)进行删除操作,这称为“出队”,而在队列的尾部(tail)进行插入操作,这称为“入队”。当队列中没有元素时(即 head==tail),称为空队列。
队列将是我们今后学习广度优先搜索以及队列优化的 Bellman-Ford 最短路算法的核心
数据结构。所以现在将队列的三个基本元素(一个数组,两个变量)封装为一个结构体类型,
如下。
struct queue 
{ int data[100];//队列的主体,用来存储内容int head;//队首int tail;//队尾
};
可以这么理解:我们定义了一个新的数据类型,这个新类型非常强大,用这个新类型定义出的每一个变量可以同时存储一个整型数组和两个整数。

 使用结构体来实现的队列操作

#include <stdio.h> 
struct queue 
{ int data[100];//队列的主体,用来存储内容int head;//队首int tail;//队尾
}; 
int main() 
{ struct queue q; int i; //初始化队列q.head=1; q.tail=1; for(i=1;i<=9;i++) { //依次向队列插入9个数scanf("%d",&q.data[q.tail]); q.tail++; } while(q.head<q.tail) //当队列不为空的时候执行循环{ //打印队首并将队首出队printf("%d ",q.data[q.head]); q.head++; //先将新队首的数添加到队尾q.data[q.tail]=q.data[q.head]; q.tail++; //再将队首出队q.head++; } getchar();getchar(); return 0; 
}

解密回文——栈

 原书中对栈的说明:

栈究竟有哪些作用呢?我们来看一个例子。“xyzyx”是一个回文字符串,所谓回文字符
串就是指正读反读均相同的字符序列,如“aha”和“ahaha”均是回
文,但“ahah”不是回文。通过栈这个数据结构我们将很容易判断一个字符串是否为回文。
首先我们需要读取这行字符串,并求出这个字符串的长度。
char a[101];
int len;
gets(a);
len=strlen(a);
如果一个字符串是回文的话,那么它必须是中间对称的,我们需要求中点,即:
mid=len/2-1;
接下来就轮到栈出场了。
我们先将 mid 之前的字符全部入栈。因为这里的栈是用来存储字符的,所以这里用来实
现栈的数组类型是字符数组即 char s[101];,初始化栈很简单,top=0;就可以了。入栈的操作
top++; s[top]=x; (假设需要入栈的字符暂存在字符变量x中),其实可以简写为s[++top]=x;
现在我们就来将 mid 之前的字符依次全部入栈。
for(i=0;i<=mid;i++)
{
s[++top]=a[i];
}
接下来进入判断回文的关键步骤。将当前栈中的字符依次出栈,看看是否能与 mid 之后
的字符一一匹配,如果都能匹配则说明这个字符串是回文字符串,否则这个字符串就不是回
文字符串。
for(i=mid+1;i<=len-1;i++)
{
if (a[i]!=s[top])
{
break;
}
top--;
}
if(top==0)
printf("YES");
else
printf("NO");
最后如果 top 的值为 0,就说明栈内所有的字符都被一一匹配了,那么这个字符串就是
回文字符串。完整的代码如下。
#include <stdio.h>
#include <string.h>
int main()
{
char a[101],s[101];
int i,len,mid,next,top;gets(a); //读入一行字符串
len=strlen(a); //求字符串的长度
mid=len/2-1; //求字符串的中点top=0;//栈的初始化
//将mid前的字符依次入栈
for(i=0;i<=mid;i++)
s[++top]=a[i];//判断字符串的长度是奇数还是偶数,并找出需要进行字符匹配的起始下标
if(len%2==0)
next=mid+1;
else
next=mid+2;//开始匹配
for(i=next;i<=len-1;i++) 第 2 章 栈、队列、链表
{
if(a[i]!=s[top])
break;
top--;
}//如果top的值为0,则说明栈内所有的字符都被一一匹配了
if(top==0)
printf("YES");
else
printf("NO");
getchar();getchar();
return 0;
}

纸牌游戏:

  星期天小哼和小哈约在一起玩桌游,他们正在玩一个非常古怪的扑克游戏——“小猫钓
鱼”。游戏的规则是这样的:将一副扑克牌平均分成两份,每人拿一份。小哼先拿出手中的
第一张扑克牌放在桌上,然后小哈也拿出手中的第一张扑克牌,并放在小哼刚打出的扑克牌
的上面,就像这样两人交替出牌。出牌时,如果某人打出的牌与桌上某张牌的牌面相同,即
可将两张相同的牌及其中间所夹的牌全部取走,并依次放到自己手中牌的末尾。当任意一人
手中的牌全部出完时,游戏结束,对手获胜。
  假如游戏开始时,小哼手中有 6 张牌,顺序为 2 4 1 2 5 6,小哈手中也有 6 张牌,顺序
3 1 3 5 6 4,最终谁会获胜呢?现在你可以拿出纸牌来试一试。接下来请你写一个程序来
自动判断谁将获胜。这里我们做一个约定,小哼和小哈手中牌的牌面只有 1~9

 完整代码实现

#include <stdio.h> 
struct queue 
{ int data[1000]; int head; int tail; 
}; 
第 2 章 栈、队列、链表
struct stack 
{ int data[10]; int top; 
}; 
int main() 
{ struct queue q1,q2; struct stack s; int book[10]; int i,t; //初始化队列q1.head=1; q1.tail=1; q2.head=1; q2.tail=1; //初始化栈s.top=0; //初始化用来标记的数组,用来标记哪些牌已经在桌上for(i=1;i<=9;i++) book[i]=0; //依次向队列插入6个数//小哼手上的6张牌for(i=1;i<=6;i++) { scanf("%d",&q1.data[q1.tail]); q1.tail++; } //小哈手上的6张牌for(i=1;i<=6;i++) { scanf("%d",&q2.data[q2.tail]); q2.tail++; } while(q1.head<q1.tail && q2.head<q2.tail ) //当队列不为空的时候执行循环{ t=q1.data[q1.head];//小哼出一张牌//判断小哼当前打出的牌是否能赢牌if(book[t]==0) //表明桌上没有牌面为t的牌
41
混混藏书阁:http://book-life.blog.163.com
啊哈!算法
42 { //小哼此轮没有赢牌q1.head++; //小哼已经打出一张牌,所以要把打出的牌出队s.top++; s.data[s.top]=t; //再把打出的牌放到桌上,即入栈book[t]=1; //标记桌上现在已经有牌面为t的牌} else { //小哼此轮可以赢牌q1.head++;//小哼已经打出一张牌,所以要把打出的牌出队q1.data[q1.tail]=t;//紧接着把打出的牌放到手中牌的末尾q1.tail++; while(s.data[s.top]!=t) //把桌上可以赢得的牌依次放到手中牌的末尾{ book[s.data[s.top]]=0;//取消标记q1.data[q1.tail]=s.data[s.top];//依次放入队尾q1.tail++; s.top--; //栈中少了一张牌,所以栈顶要减1 } } t=q2.data[q2.head]; //小哈出一张牌//判断小哈当前打出的牌是否能赢牌if(book[t]==0) //表明桌上没有牌面为t的牌{ //小哈此轮没有赢牌q2.head++; //小哈已经打出一张牌,所以要把打出的牌出队s.top++; s.data[s.top]=t; //再把打出的牌放到桌上,即入栈book[t]=1; //标记桌上现在已经有牌面为t的牌 } else { //小哈此轮可以赢牌q2.head++;//小哈已经打出一张牌,所以要把打出的牌出队q2.data[q2.tail]=t;//紧接着把打出的牌放到手中牌的末尾q2.tail++; while(s.data[s.top]!=t) //把桌上可以赢得的牌依次放到手中牌的末尾{ book[s.data[s.top]]=0;//取消标记
第 2 章 栈、队列、链表q2.data[q2.tail]=s.data[s.top];//依次放入队尾q2.tail++; s.top--; } } } if(q2.head==q2.tail) { printf("小哼win\n"); printf("小哼当前手中的牌是"); for(i=q1.head;i<=q1.tail-1;i++) printf(" %d",q1.data[i]); if(s.top>0) //如果桌上有牌则依次输出桌上的牌{ printf("\n桌上的牌是"); for(i=1;i<=s.top;i++) printf(" %d",s.data[i]); } else printf("\n桌上已经没有牌了"); } else { printf("小哈win\n"); printf("小哈当前手中的牌是"); for(i=q2.head;i<=q2.tail-1;i++) printf(" %d",q2.data[i]); if(s.top>0) //如果桌上有牌则依次输出桌上的牌{ printf("\n桌上的牌是"); for(i=1;i<=s.top;i++) printf(" %d",s.data[i]); } else printf("\n桌上已经没有牌了"); } getchar();getchar(); return 0; 
}

链表

#include <stdio.h> 
#include <stdlib.h> 
int main() 
{ int *p; //定义一个指针p p=(int *)malloc(sizeof(int)); //指针p获取动态分配的内存空间地址*p=10; //向指针p所指向的内存空间中存入10 printf("%d",*p); //输出指针p所指向的内存中的值getchar();getchar(); return 0; 
}

书上关于链表的介绍: 

到这里你可能要问:为什么要用这么复杂的办法来存储数据呢?因为之前的方法,我们
必须预先准确地知道所需变量的个数,也就是说我们必须定义出所有的变量。比如我们定义
100 个整型变量,那么程序就只能存储 100 个整数,如果现在的实际情况是需要存储 101
个,那必须修改程序才可以。如果有一天你写的软件已经发布或者交付使用,却发现要存储
1000 个数才行,那就不得不再次修改程序,重新编译程序,发布一个新版本来代替原来的。
而有了 malloc 函数我们便可以在程序运行的过程中根据实际情况来申请空间。

例子: 

#include <stdio.h> 
#include <stdlib.h> 
//这里创建一个结构体用来表示链表的结点类型
struct node 
{ int data; struct node *next; 
}; 
int main() 
{ struct node *head,*p,*q,*t; int i,n,a; scanf("%d",&n); head = NULL;//头指针初始为空for(i=1;i<=n;i++)//循环读入n个数{ scanf("%d",&a); //动态申请一个空间,用来存放一个结点,并用临时指针p指向这个结点p=(struct node *)malloc(sizeof(struct node)); p->data=a;//将数据存储到当前结点的data域中p->next=NULL;//设置当前结点的后继指针指向空,也就是当前结点的下一个结点为空if(head==NULL) head=p;//如果这是第一个创建的结点,则将头指针指向这个结点else q->next=p;//如果不是第一个创建的结点,则将上一个结点的后继指针指向当前结点q=p;//指针q也指向当前结点} //输出链表中的所有数t=head; while(t!=NULL) { 
第 2 章 栈、队列、链表printf("%d ",t->data); t=t->next;//继续下一个结点} getchar();getchar(); return 0; 
}容易造成内存泄漏!
上面这段代码没有释放动态申请的空间,虽然没有错误,但是这样会很不安全,有兴趣的朋友可以去了解一下 free 命令。
#include <stdio.h> 
#include <stdlib.h> 
//这里创建一个结构体用来表示链表的结点类型
struct node 
{ int data; struct node *next; 
}; 
int main() 
{ struct node *head,*p,*q,*t; int i,n,a; scanf("%d",&n); head = NULL;//头指针初始为空for(i=1;i<=n;i++)//循环读入n个数{ scanf("%d",&a); //动态申请一个空间,用来存放一个结点,并用临时指针p指向这个结点p=(struct node *)malloc(sizeof(struct node)); p->data=a;//将数据存储到当前结点的data域中p->next=NULL;//设置当前结点的后继指针指向空,也就是当前结点的下一个结点为空if(head==NULL) head=p;//如果这是第一个创建的结点,则将头指针指向这个结点else q->next=p;//如果不是第一个创建的结点,则将上一个结点的后继指针指向当前结点
第 2 章 栈、队列、链表q=p;//指针q也指向当前结点} scanf("%d",&a);//读入待插入的数t=head;//从链表头部开始遍历while(t!=NULL)//当没有到达链表尾部的时候循环{ if(t->next->data > a)//如果当前结点下一个结点的值大于待插入数,将数插入到中间{ p=(struct node *)malloc(sizeof(struct node));//动态申请一个空间,
用来存放新增结点p->data=a; p->next=t->next;//新增结点的后继指针指向当前结点的后继指针所指向的结点t->next=p;//当前结点的后继指针指向新增结点break;//插入完毕退出循环} t=t->next;//继续下一个结点} //输出链表中的所有数t=head; while(t!=NULL) { printf("%d ",t->data); t=t->next;//继续下一个结点} getchar();getchar(); return 0; 
}

模拟链表 

链表可以用指针(上面的)和使用数组来实现的方式(叫做模拟链表)

#include <stdio.h> 
int main() 
{ int data[101],right[101]; int i,n,t,len; //读入已有的数 scanf("%d",&n); for(i=1;i<=n;i++) scanf("%d",&data[i]); len=n; //初始化数组right for(i=1;i<=n;i++) { if(i!=n) right[i]=i+1; else right[i]=0; } //直接在数组data的末尾增加一个数 len++; scanf("%d",&data[len]); //从链表的头部开始遍历 t=1; while(t!=0) { if(data[right[t]]>data[len])//如果当前结点下一个结点的值大于待插入数,将
数插入到中间 { right[len]=right[t];//新插入数的下一个结点标号等于当前结点的下一个结
点编号 right[t]=len;//当前结点的下一个结点编号就是新插入数的编号 break;//插入完成跳出循环 } t=right[t]; } //输出链表中所有的数 t=1; while(t!=0) { printf("%d ",data[t]); t=right[t]; } getchar(); getchar(); return 0; 
}

使用模拟链表也可以实现双向链表和循环链表 

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mysql 语句优化 (Explain)

Mysql 语句优化 &#xff08;Explain&#xff09; 1. 概述 ​ 在 select 语句之前增加 explain 关键字&#xff0c; mysql 会在查询上设置一个标记&#xff0c;返回查询执行计划信息&#xff0c;而不是执行这条sql 字段formatjson时的名称含义idselect_id该语句的唯一标识sel…

centos7安装

centos7安装制作U盘启动盘下载镜像下载 UltralISO制作启动盘使用U盘安装系统修改模式为 UEFI调整BOOT option保存重启进入安装界面安装图形界面安装搜狗输入法制作U盘启动盘 下载镜像 去官网下载镜像&#xff0c;找到 mirrors链接&#xff08;速度快&#xff09; 选择一个中…

创客匠人直播:构建公域到私域的用户增长模型

进入知识付费直播带货时代&#xff0c;很多拥有知识技能经验的老师和培训机构吃到了流量红利。通过知识付费直播&#xff0c;老师们可以轻松实现引流、变现&#xff0c;还可以突破时间、地域的限制&#xff0c;为全国各地的学员带来优质的教学服务&#xff0c;因此越来越受到教…

使用JavaScript+Selenium玩转Web应用自动化测试

自动化测试 在软件开发过程中, 测试是功能验收的必要过程, 这个过程往往有测试人员参与, 提前编写测试用例, 然后再手动对测试用例进行测试, 测试用例都通过之后则可以认为该功能通过验收. 但是软件中多个功能之间往往存在关联或依赖关系, 某一个功能的新增或修改可能或影响到…

CTFer成长之路之反序列化漏洞

反序列化漏洞CTF 1.访问url&#xff1a; http://91a5ef16-ff14-4e0d-a687-32bdb4f61ecf.node3.buuoj.cn/ 点击下载源码 本地搭建环境并访问url&#xff1a; http://127.0.0.1/www/public/ 构造payload&#xff1a; ?sindex/index/hello&ethanwhoamiPOST的参数&#…

Redis的持久化方式

Redis支持两种方式的持久化&#xff0c;一种是RDB方式、另一种是AOF&#xff08;append-only-file&#xff09;方式&#xff0c;两种持久化方式可以单独使用其中一种&#xff0c;也可以将这两种方式结合使用。 •RDB&#xff1a;根据指定的规则“定时”将内存中的数据存储在硬…

【Virtualization】Windows11安装VMware Workstation后异常处置

安装环境 Windows 11 专业版 22H2 build 22621.1265 VMware Workstation 17 Pro 17.0.0 build-20800274 存在问题 原因分析 1、BIOS未开启虚拟化。 2、操作系统启用的虚拟化与Workstation冲突。 3、操作系统启用内核隔离-内存完整性保护。 处置思路 1、打开“资源管理器”…

6-Java中新建一个文件、目录、路径

文章目录前言1-文件、目录、路径2-在当前路径下创建一个文件3-在当前路径下创建一个文件夹&#xff08;目录&#xff09;3.1 测试1-路径已经存在3.2 测试2-路径不存在3.2 创建不存在的路径并新建文件3.3 删除已存在的文件并新建4-总结前言 学习Java中如何新建文件、目录、路径…

Fiddler报文分析-断点应用、模拟网络限速-HTTPS的 拦截

目录 一、报文分析 Statistics 请求性能数据 检查器&#xff08;Inspectors&#xff09; 自定义响应&#xff08;AutoResponder&#xff09; Composer Composer的功能就是用来创建HTTP Request然后发送请求。 允许自定义请求发送到服务器&#xff0c;即可以手动创建一个新…

大学毕业后,送了2个月外卖,哭了一整晚

先简单介绍一下自己&#xff0c;我来自湛江&#xff0c;大学学的的物流管理专业&#xff0c;现在就职于一家互联网公司&#xff0c;从事软件测试工作。 我来自湛江的一个偏远农村&#xff0c;家里兄弟姐妹多&#xff0c;父母无力负担我的学费&#xff0c;很多时候学费都是靠姐…

CHATGPT是新的“搜索引擎终结者”吗?百度是否慌了

ChatGPT 以其非凡的自然语言处理 &#xff08;NLP&#xff09; 能力和清晰的响应风靡全球&#xff0c;有望带来一场重大的技术革命。在不知不觉中&#xff0c;叙事转向了ChatGPT与百度的对决&#xff0c;因为来自OpenAI的智能和健谈的聊天机器人已经慢慢获得了“潜在的百度终结…

JS 执行机制 详解(附图)

一、JS是单线程JS语言的一大特点就是单线程&#xff0c;也就是说&#xff0c;同一个时间只能做一件事。这是JS这门脚本语言诞生的使命所致——用来处理页面中用户的交互&#xff0c;以及操作DOM而诞生的。单线程就意味着&#xff0c;所有任务需要排队&#xff0c;前一个任务结束…

自动化测试整理 --- STAF/STAX Robot Framework

题记:上周花了点时间学习开源的自动化测试框架Robot Framework,结合自己之前的自动化经验&#xff0c;就想周末写篇文章整理下。 目前&#xff0c;所在项目的自动化测试框架是基于STAF/STAX的拓展&#xff0c;围绕STAX执行引擎&#xff0c;扩展了测试用例的创建、管理&#xf…

CMake调试器出炉:调试你的CMake脚本

Visual Studio 开发团队一直和 Kitware 紧密合作&#xff0c;致力于开发一个用于调试 CMake 脚本的调试器。 我们将继续这个工作&#xff0c;以便开发人员社区可以通过添加新功能和对其他 DAP 功能的支持来共同改进它。 我们很高兴地宣布&#xff0c;CMake 调试器的预览版现在…

验证功能覆盖率收集时per_instance=1可能导致覆盖率线性增长

验证覆盖率收集时&#xff0c;发现coverage database达到了惊人的256G&#xff0c;如下&#xff1a; 进入database中的testdata目录下的用例定位发现&#xff0c;问题出在这个文件&#xff1a; testbench.inst.xml其大小基本等同于验证用例覆盖率的大小。 这个文件时怎么产…

用数据讲故事:基于分析场景的17条Python使用小结

数据科学的编程需要非常灵活的语言&#xff0c;以最少的代码处理复杂的数据建模场景。作为一名数科小白&#xff0c;我对Python的第一认知是丰富的机器学习算法&#xff0c;但Python有超过12万个第三方库&#xff0c;覆盖从数据预处理、统计分析、数据挖掘及可视化等各种日常数…

Android Studio中创建java工程

1. 前言 电脑环境: Ubuntu 18.04 开发工具环境:Android Studio 4.1.3 版本 经常要使用验证Java API, 把配置环境步骤记录一下 2. 创建步骤 2.1 新建一个Android Studio App工程 New ---> New Project ---> 选择一个Activity主题---> Finish 就创建ok 2.2 …

【模拟集成电路】分频器(DIV_TSPC)设计

分频器&#xff08;DIV_TSPC&#xff09;设计前言一、DIV工作原理二、DIV电路设计&#xff08;1&#xff09;32分频原理图&#xff08;2&#xff09;D触发器原理图&#xff08;3&#xff09;D锁存器原理图&#xff08;4&#xff09;三输入与非门原理图三、DIV仿真测试32分频器测…

【模拟集成电路】宽摆幅压控振荡器(VCO)设计

鉴频鉴相器设计&#xff08;Phase Frequency Detector&#xff0c;PFD&#xff09;前言一、VCO工作原理二、VCO电路设计VCO原理图三、压控振荡器&#xff08;VCO&#xff09;测试VCO测试电路图瞬态测试&#xff08;1&#xff09;瞬态输出&#xff08;2&#xff09;局部放大图&a…

调试版获取安卓SHA1值

确保你的电脑上有JDK&#xff0c;配置好环境变量后执行我这些步骤。where keytool看看电脑找不找得到找得到就可以进行下一步了口令默认android或者为空