文章目录
- 前言
- 一、数据结构基础知识(衔接知识)
- 二、队列
- 三、栈
- 四、链表
- 总结
前言
上一节我们学习了排序算法当中的快速排序 冒泡排序 桶排序 ,那么本节得主要学习内容是队列 栈 链表得相关数据结构得知识
一、数据结构基础知识(衔接知识)
基于学习这本书得都是一些算法小白甚至是小学生学习 所以我想补充一些 数据结构得基础知识给大家,以便于大家更好的理解本节得知识
1. 栈中的“先进后出,后进先出”什么意思
栈对于数据的管理主要有两种操作:
- 压栈:栈的插入操作叫做进栈 / 压栈 / 入栈,从栈顶进行压栈。
- 出栈:栈的删除操作叫做 出栈,从栈顶进行出栈。
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;
}
总结
先到这儿后面继续补充