《啊哈算法》第二章--队列 栈 链表

news2024/11/24 14:25:27

在这里插入图片描述

文章目录

  • 前言
  • 一、数据结构基础知识(衔接知识)
  • 二、队列
  • 三、栈
  • 四、链表
  • 总结


前言

上一节我们学习了排序算法当中的快速排序 冒泡排序 桶排序 ,那么本节得主要学习内容是队列 栈 链表得相关数据结构得知识


一、数据结构基础知识(衔接知识)

基于学习这本书得都是一些算法小白甚至是小学生学习 所以我想补充一些 数据结构得基础知识给大家,以便于大家更好的理解本节得知识

1. 栈中的“先进后出,后进先出”什么意思

栈对于数据的管理主要有两种操作:

  1. 压栈:栈的插入操作叫做进栈 / 压栈 / 入栈,从栈顶进行压栈。
  2. 出栈:栈的删除操作叫做 出栈,从栈顶进行出栈。
    在这里插入图片描述

2.栈的定义
栈只允许在固定的一段进行插入和删除元素的操作。进行数据插入和删除操作的一端称为栈顶,不进行操作的一端称为栈底栈中的元素遵守 后进先出 (LIFO - Last In First Out) 的原则。也就是先进的后出,后进的先出

3,栈与队列的区别
形象得比喻就是栈就像子弹一样,最先进去的最后被射出来,而队列则是排队内样,先到先得 这就是先进先出,也叫后进后出

看完这些如果对队列 和 栈 想进一步了解的伙伴可以 看我下面两篇博客 进行学习
【数据结构】千字深入浅出讲解队列(附原码 | 超详解)

【数据结构】千字深入浅出讲解栈(附原码 | 超详解)

二、队列

队列的定义

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出

FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头

数据结构图如下:
在这里插入图片描述
队首(head)删除数据,称之为“出队”
队尾(tail)插入数据,称之为“入队”
当队列中没有元素时(head == tail),称为空队列

题目描述

新学期开始,小哼和小哈俩人坐同桌然后小哼问小哈QQ号码是多少,但是小哈不轻易给别人QQ号就给了一串神秘数字,但是数字是有规则的数字

规则是先将第1个数删除,然后把第2个数放到这串数末尾,再将第3个数删除,并把第4个数放到末尾…

直到剩下最后一个数,把最后一个数也删除。把删除的数连在一起就是小哈的QQ号了。小哈给他加密过的一串数是“920430714”,求小哈的QQ号是多少?

解题思路
在这里插入图片描述
在这里插入图片描述

队首删除一个数的操作就是head++
队尾增加一个数的操作就是q[tail]=x , tail++

现在有9个数,把9个数放入队列后,head == 1,tail == 10,此时head和tail之间的数就是目前队列中“有效”的数

代码如下

//方法一:
#include<iostream>
using namespace std;
int main()
{
	int q[102]={0,6,3,1,7,5,8,9,2,4},head,tail;
	//初始化队列
	head=1;//指向第一个
	tail=10;//指向最后一个的下一个
	while(head<tail)//队列不为空
	{
		//打印队首
		printf("%d ",q[head]);
		head++;//队首出队 
		
		//先将新队首得数添加到队尾
		q[tail]=q[head];
		tail++;
		//再将队首出队 
		head++; 
	}
	return 0;
}

样例实列

输入:6 3 1 7 5 8 9 2 4
输出:6 1 5 9 4 7 2 8 3

将队列三个基本元素(一个数组,两个变量)封装为一个结构体类型,其实在算法比赛当中不应该使用结构体 而是采用第一种方法。

//方法二

#include<iostream>
using namespace std;
struct queue
{
	int data[100];
	int head;
	int tail;
};
int main()
{
	struct queue q;
	int i;
	//初始化队列
	q.head=1;
	q.tail=1;
	for(int i=1;i<=9;++i)
	{
		//依次向队列插入9个数
		cin>>q.data[q.tail];
		q.tail++; 
	}
	while(q.head<q.tail)
	{
		//打印队首并将队首出队
		cout<<q.data[q.head]<<" ";
		q.head++;
		q.data[q.tail]=q.data[q.head];
		q.tail++;
		q.head++; 
	}
	return 0;
}

三、栈

栈后进先出,生活中有很多例子:

1. 浏览网页退回之前某个网页,需要一步步点击后退键
2. 给弹夹装子弹,最后装入的子弹是第一个被打出去的,或者说第一个被退出来的
3. 又比如一个小桶,小桶的直径只能放入一个球,依次放入2,1,3号小球,想取出2号球就得先将3号取出来,再将1号取出来,最后才能取最先放进去的2号小球

在这里插入图片描述
本节栈当中主要出现了一个问题 就是判断回文字符串 其实这个题目早在之前学习C语言的时候我就写过了,牛客上是有这个题目的

解题思路:
对于字符串进行一分为二,中点设置为mid,mid左边的字符串入栈进入到栈里面,然后入栈后让栈里面的字母和mid后面的字符进行匹配,设置Top,每一次匹配成功Top–,最后看Top是否为0,为0就代表是回文,否则不是回文字符

入栈的操作:s[++top] = a[i];
通过strlen(a)得到字符串长度len,mid = len / 2 - 1;

代码如下:

#include<iostream>
#include<cstring>
using namespace std;

char a[110],s[110];
int len,mid,next,top;

int main()
{
	cin.get(a,110);//读入字符串 
	len=strlen(a);//求字符串长度 
	mid=len/2-1;//求字符串的中点
	//入栈操作
	for(int i=0;i<=mid;++i)
	{
		s[++top]=a[i];
	}
	//判断字符串的长度是奇数还是偶数,并找出需要进行字符匹配的下标
	if(len%2==0) next=mid+1;
	else next=mid+2; 
	
	//开始匹配
	for(int i=next;i<=len-1;++i)
	{
		if(a[i]!=s[top]) break;
		top--;
	}
	
	if(top==0)	cout<<"Yes"<<endl;
	else cout<<"No"<<endl;
	return 0;
}

样例实列

输入:nihaooahin
输出:Yes

下面讲解一下不用栈的思路:

双指针算法,分别指向字符串的左侧和右侧进行相向而行,一个一个匹配,俩指针所指向的字符不一样的时候就输出NO,当两个指针相遇或者穿过彼此的时候,就输出Yes

代码如下:

#include<iostream>
#include<cstring>
using namespace std;
char a[110];
int is_huiwen(int left,int right)
{
	while(left<right)
	{
		if(a[left++]!=a[right--]) return 0;
		else continue;
	}
	return 1;
}
int main()
{
	cin.get(a,110);
	int right=strlen(a)-1;
	if(is_huiwen(0,right)) cout<<"Yes"<<endl;
	else cout<<"No"<<endl;
	return 0;
}

四、链表

链表的定义

链表是一种物理存储结构非连续非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针
接次序实现的 。

在这里插入图片描述

实现链表

使用指针和动态分配内存函数malloc来实现

1. 指针
详细看博客:C语言—指针初阶—总结

2. malloc函数
1malloc(sizeof(int))
等价于 malloc(4); 从内存中申请分配4个字节大小的内存空间
2现在已经成功从内存中申请了4个字节的空间来存放一个整数,如何对这个空间操作呢?
这就需要一个指针来指向这个空间,即存储这个空间的首地址
3(int *)malloc()函数返回类型使void *类型,表示未确定类型的指针,该类型可强制转换为任何其他类型指针,此处我们需要强转整型,所以是(int *)

int *p; //定义指针p
p = (int *)malloc(sizeof(int));

malloc()函数动态申请空间:

#include<iostream>
#include<cstdlib> //malloc()
using namespace std;
int main()
{
    int *p; //定义指针p
    //指针p获取动态分配的内存地址
    p = (int *)malloc(sizeof(int));
    *p = 10; //向p所指向的内存存入10
    cout<<*p<<endl; //输出p指向内存中的值
    return 0;
}

每一个节点由两部分组成,左边部分存放具体数值,右边部分存储下一个节点的地址(称为后继指针),这里我们定义一个结构体类型来存储这个节点
在这里插入图片描述
详细去看我的数据结构博客【数据结构】万字深入浅出讲解单链表(附原码 | 超详解)

问题:建立链表

#include<iostream>
#include<cstdlib> //malloc()
using namespace std;
struct node
{
    int data;
    struct node *next;
};
int main()
{
    struct node *head, *p, *q, *t;
    int i, n, a;
    cin>>n;
    head = NULL; //头指针初始为空
    for(i = 1; i <= n; ++i) {
        cin>>a;
        //动态申请一个空间存放节点,临时指针p指向这个节点
        p = (struct node *)malloc(sizeof(struct node));
        p->data = a; //数据存储到数据域
        p->next = NULL; //后继指针指向空
        if(head == NULL)
            head = p; //第一个节点让头指针指向
        else
            q->next = p; //否则上一节点后继指针指向当前节点
        q = p; //上一节点指针指向当前节点
    }
 
    //输出链表中所有数
    t = head;
    while(t != NULL) {
        cout<<t->data<<" ";
        t = t->next; //继续下一节点
    }
    return 0;
}

问题:插入数据

#include<iostream>
#include<cstdlib> //malloc()
using namespace std;
 
//创建一个结构体表示节点
struct node
{
    int data;
    struct node *next;
};
 
int main()
{
    struct node *head, *p, *q, *t; //p,q,t都是临时指针
    int i, n, a;
    cin>>n; //n个数
    head = NULL; //头指针初始为空
    for(i = 1; i <= n; ++i) {
        cin>>a;
        //动态申请空间存放一个节点,临时指针p指向该节点
        p = (struct node *)malloc(sizeof(struct node));
        p->data = a;
        p->next = NULL; //当前节点下一节点为空
        if(head == NULL)
            head = p; //若为第一个创建的,头指针指向该节点
        else
            q->next = p; //上一节点后继指针指向当前节点
        q = p; //指针q指向当前节点
    }
 
    cin>>a; //待插入的数
    t = head; //从链表头部开始遍历
    while(t != NULL) {
        if(t->next == NULL || 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) {
        cout<<t->data<<" ";
        t = t->next; //继续下一节点
    }
    return 0;
}

总结

先到这儿后面继续补充

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

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

相关文章

《英雄联盟》丢失d3dcompiler_47.dll怎么办,推荐这个修复方案

不知道大家有么有遇到过&#xff0c;在打开《英雄联盟》的时候&#xff0c;计算机提示丢失d3dcompiler_47.dll&#xff0c;无法继续执行此代码。d3dcompiler_47.dll是一个动态链接库文件&#xff0c;它是与Direct3D编译器相关的组件之一。像是photoshop等软件&#xff0c;英雄联…

IEEE754 标准是如何制定浮点数的存储的

1. IEEE754 标准简介 IEEE754 标准是一种用于浮点数表示和运算的标准&#xff0c;由国际电工委员会&#xff08;IEEE&#xff09;制定。它定义了浮点数的编码格式、舍入规则以及基本的算术运算规则&#xff0c;旨在提供一种可移植性和一致性的方式来表示和处理浮点数 IEEE754 …

c#使用ThreadPool

说到ThreadPool&#xff0c;都知道是线程池。在c#中&#xff0c;有很多方式可以实现线程。从时间上来排序&#xff0c;大概是这样的&#xff0c;Thread&#xff0c;backgroundworker&#xff0c;ThreadPool&#xff0c;Parallel&#xff0c;Task。其中后面2种是最新的&#xff…

第十八章:Auto-DeepLab:用于语义图像分割的层次化神经架构搜索

0.摘要 最近&#xff0c;神经架构搜索&#xff08;NAS&#xff09;已经成功地识别出在大规模图像分类任务上超越人工设计的神经网络架构。在本文中&#xff0c;我们研究了NAS在语义图像分割任务中的应用。现有的工作通常集中在搜索可重复的基本单元结构&#xff0c;而手动设计控…

一些有趣的Git学习资料

Git 可以说是程序员必备的技能之一了&#xff0c;基于 Github/Gitlab 以及相关工作流的使用&#xff0c;Git 已经融入到了我们的日常工作中&#xff0c;这里分享一些有趣的 Git 学习资料&#xff0c;希望可以帮助大家更好的理解 Git。 1. Welcome to Learn Git Branching 以动…

第二周笔记

public class Calc { //加法, 把和作为一个结果返回出去, 返回给调用者 public int add3(int num1, int num2){ if(num1 0 && num2 0){ return 0; //隐式包含一个if-else结构 } //使用return 关键字 return nu…

【Linux操作系统】多线程抢票逻辑——学习互斥量(锁)函数接口

文章目录 1.进程线程间的互斥相关背景概念2.联系代码学习同步互斥问题3.互斥量&#xff08;锁&#xff09;的函数接口3.1初始化互斥量3.2销毁互斥量3.3互斥量加锁和解锁3.4改进多线程抢票代码 1.进程线程间的互斥相关背景概念 临界资源&#xff1a;多线程执行流共享的资源就叫…

beego验证码(配置到redis存储)

我们定义一个全局变量用于存储redis连接 RedisDb *redis.Client 然后连接 redis 这一块我们将redis信息写到app.conf文件里了&#xff1a; redisDb 1 redisAddr "127.0.0.1:6379" redisPwd "" package initializeimport ("beego_learning/global&q…

【Java基础教程】(十四)面向对象篇 · 第八讲:多态性详解——向上及向下转型、关键字 final与 instanceof的作用~

Java基础教程之面向对象 第八讲 本节学习目标1️⃣ final 关键字1.1 final类1.2 final方法1.3 final属性 2️⃣ 多态性2.1 向上转型2.2 向下转型2.3 关键字 instanceof &#x1f33e; 总结 本节学习目标 掌握final 关键字的主要作用及使用&#xff1b;掌握对象多态性的概念以…

【CSDN新星计划】初阶牛C/C++赛道——顺序程序设计(C语句)

目录 3.1 C语句的作用和分类 &#x1f349;&#xff08;1&#xff09;控制语句 &#x1f349;&#xff08;2&#xff09;函数调用语句 &#x1f349;&#xff08;3&#xff09;表达式语句 &#x1f349;&#xff08;4&#xff09;空语句 &#x1f349;&#xff08;5&#…

【C++进阶之路】vector的基本使用和模拟实现

前言 作为STL的容器之一&#xff0c;vector的名字通常令人疑惑&#xff1f;在字面上&#xff0c;我们通常会翻译成向量&#xff0c;但感觉又解释不通&#xff0c;总觉得应该叫dynamic array翻译成动态数组/顺序表&#xff0c;更容易理解&#xff1f;那为啥呢&#xff1f; 我从…

【Java】微服务项目的部署

微服务项目的部署 准备Centos安装 Docker镜像加速检查加速器是否生效 下载docker-compose方法1 curl方法2 pip方法3 直接下载released 用docker-compose部署中间件导入项目安装jdk maven git设置idea内存减小jar启动占用内存增加idea可使用内存 本文参考 https://gitee.com/…

音乐怎么转换成wav格式?分享这五个方法给大家!

在音频编辑和处理过程中&#xff0c;将音乐文件转换为WAV格式是一种常见需求。WAV格式以其无损音质和广泛的兼容性而受到许多人的喜爱。下面介绍了五种常用的方法&#xff0c;帮助您将音乐文件转换为WAV格式&#xff0c;其中方法一使用记灵在线工具。 方法一&#xff1a;记灵在…

非常规自增自减

非常规自增自减 目录 一&#xff0e; 概述二&#xff0e; 例题 一&#xff0e; 概述 在C语言的单目操作符中有&#xff08;自增&#xff09;和–&#xff08;自减&#xff09;这两个运算符。假设有变量i&#xff0c;我想让变量i加上1&#xff0c;那么我们会写成ii1这样的形式。…

自旋锁与开关中断临界区的区别

自旋锁和开关中断临界区都是用于保护共享资源的机制&#xff0c;但它们的实现方式和使用场景有所不同。 自旋锁主要是用于多核CPU上的并发编程中&#xff0c;它通过不断地检查锁的状态来等待锁的释放&#xff0c;从而避免了线程的阻塞。当一个线程需要访问共享资源时&#xff…

VTK 三维模型 体绘制 关于环境光、漫反射、镜面反射

光源: 1):环境光:环境光是一种低强度的光,由光线经过周围环境表面多次反射后形成的,利用环境光可以描述一块区域的亮度,通常在场 景中,环境光的颜色是一个常量. 2):太阳光:即定向光源,特点是从无穷远出发射光线,光线是平行的,光线强度不会随着距离衰减. 3):点光源:在有限空间…

maven安装和换源

1. 安装(17条消息) maven历史版本下载和安装_beiback的博客-CSDN博客 安装 maven历史版本仓库 下载 Index of /dist/maven/maven-3 (apache.org)https://archive.apache.org/dist/maven/maven-3/ 选择对应版本-binaries/-zip 解压即可使用 2.换源 (17条消息) 手把手教你更改…

【读书笔记】从实模式到保护模式

计算机语言 x86汇编语言&#xff1a;从实模式到保护模式&#xff08;操作系统引导课&#xff09; 原书作者李忠 用电表示数据 寄存器的作用&#xff1a;具有记忆功能的器件。锁存可以通过下面的开关控制&#xff0c;平时开关为空&#xff0c;按下开关之后&#xff0c;将输入锁…

PLEX如何搭建个人局域网的视频网站

Plex是一款功能非常强大的影音媒体管理系统&#xff0c;最大的优势是多平台支持和界面优美&#xff0c;几乎可以在所有的平台上安装plex服务器和客户端&#xff0c;让你可以随时随地享受存储在家中的电影、照片、音乐&#xff0c;并且可以实现观看记录无缝衔接&#xff0c;手机…

VScode——NPM脚本窗口找不到

一、问题描述&#xff08;NPM终端在任务栏左侧找不到&#xff09; VScode&#xff08;Visual Studio Code&#xff09;版本&#xff1a;1.79.2 二、解决办法 第一步&#xff1a;通过设置/用户设置/扩展/MPM更改NPM默认配置&#xff0c;如下图所示&#xff1a; 第二步&#xff…