无中生有之突击NOIP(2)--栈,队列,链表
来源:互联网 发布:学编程入门 编辑:程序博客网 时间:2024/05/06 11:21
1、队列
定义:形如我们排队买票,第一个站队的人第一个买票一样,一个可以控制变量先进先出的结构体里,我们称之为队列。
理解:我们可以想象出一排东西整齐存放于一行里,我们要做的是用一个可以压缩的框框,通过从前到后移动,来决定我们框住的值到底为多少。
实现代码如下:
#include<stdio.h>#include<algorithm>int main(){ int q[102]={0,6,3,1,7,5,8,9,2,4},head,tail; head=1; tail=10;//队列初始化 while(head<tail) { cout<<q[head]; head++; q[tail]=q[head]; tail++; head++; } getchar();getchar(); return 0;}
总结:队列属于一种特殊的线性结构,它只允许在队列首部删除,队列尾部插入,称为入队。当队列中没有元素的时候,我们称之为空队列。队列将作为我们今后广度优先搜索(BFS)和队列优化的Bellman-Ford最短路算法的核心数据。
P.S.我们可以将队列定义为一个结构体,即一共两个变量一个数组的结构。
e.g.`
struct queue
{
int data[100];//队列的主体,
int head;//队首
int tail;//队尾
};
//上面是结构体的定义,下面定义一个名为q的结构体
struct queue q;
q.head=1;
q.tail=1;
cin>>q.data[q.tail];
2、栈
定义:栈(stack)又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
理解:例如汉诺塔,我们把123号塔依次放入,若要移出1号塔,首先要移出3号塔,然后是2号。说白了就是一个后进先出的数据结构,我们只能在一端进行操作。可以判断回文,数据如下:
#include<stdio.h>;#include<string.h>;#include<algorithm>;int main(){ char a[101],s[101]; int i,len,mid,next,top; gets(a);//读入一串字符串 len=strlen(a);//求字符串的长度 mid=len/2-1;//寻找中点 top=0;//栈初始化 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++) { if(a[i]!=s[top]) break; top--; } if(top==0)//如果top的值为0;则说明栈内的字符都被匹配过了 cout<<"yes"; else cout<<"NO"; getchar():getchar(); return 0; }
总结使用:大家可以尝试做做此题:
问题描述:
小哼和小哈在玩一个比较奇怪的扑克游戏——小猫钓鱼。游戏规则:将一副扑克牌平均分成两份,没人拿一分。小哼先拿出手中第一张扑克牌放在桌上,然后小哈也拿出手中第一张扑克牌,并放在小哼刚才打出的扑克牌的上面,两人交替出牌。出牌时,如果某人打出的牌与桌上某张牌的牌面相同,可将两张相同的牌及其中间所夹的牌全部拿走,并依次放到自己手中牌的末尾。当任意一人手中当牌出完时,游戏结束,对方获胜。
分析:
1、这个游戏一共有两种操作:出牌和赢牌。
出牌即出队,赢牌即入队。
2、桌面相当于一个栈,出牌即入栈,赢牌即出栈。
3、通过枚举了解桌面有哪些牌。
思路:
1、定义两个队列q1、q2表示小哼、小哈手中的牌。
2、定义一个栈s表示桌面的牌
3、定义数组book记录桌面上已经出现的牌面
4、游戏开始时q1的队首出队,判断当前时是出牌还是赢牌
4.1如果是出牌,则入栈 4.2如果是赢牌,则q1的队首到队尾,再将赢得的牌依次入队
5、q2 的队首出队,操作同4
6、第4、5步重复进行直至任意人手中没有牌
7、输出赢的人手中的牌,输出此时桌面上的牌
代码实现如下:
#include <stdio.h> struct queue{ int data[1000]; int head; int hail; }; struct stack{ int data[10]; int top; }; int main(int argc, const char * argv[]) { struct queue q1,q2; struct stack s; int book[10]={0}; //用来标记哪些牌已经在桌子上 int i,t; //初始化队列 q1.head=1; q1.hail= 1; q2.head=1; q2.hail=1; //初始化栈 s.top=0; //依次向队列输入6个数 for(i=1;i<=6;i++) { scanf("%d",&q1.data[q1.hail]); q1.hail++; } for(i=1;i<=6;i++) { scanf("%d",&q2.data[q2.hail]); q2.hail++; } while(q1.head<q1.hail&&q2.head<q2.hail) { t=q1.data[q1.head]; //q1打出第一张牌 //判断q1当前打出的牌是否能赢牌 if(book[t]==0)// 表面桌上没有牌面为t的牌 { //q1此轮不能赢牌 q1.head++;//q1打出一张牌,打出的那张牌出队 s.top++; s.data[s.top]=t;//q1打出的牌入栈 book[t]=1;//标记已打出的牌t } else {//q1此轮可以赢牌 q1.head++;//q1打出第一张牌 q1.data[q1.hail]=t;//q1打出的牌放到队尾 q1.hail++; while(s.data[s.top]!=t)//将赢得的牌依次入队 { q1.data[q1.hail]=s.data[s.top]; q1.hail++; s.top--; book[s.data[s.top]]=0;//将从桌面入队的牌取消标记 } } t=q2.data[q2.head];//q2出牌 if(book[t]==0) //q2此轮不能赢牌 { q2.head++; s.top++; s.data[s.top]=q2.data[q2.head]; book[t]=1; } else { //q2此轮能赢牌 q2.data[q2.hail]=q2.head; q2.head++; q2.hail++; while(s.data[s.top]!=t) { q2.data[q2.hail]=s.data[s.top]; q2.hail++; book[s.data[s.top]]=0; s.top--; } } } if(q2.head==q2.hail) { printf("q1 win!\n"); printf("q1当前手中的牌是:\n"); for(i=q1.head;i<q1.hail;i++) { printf("%d ",q1.data[i]); } if(s.top>0)// { printf("\n桌上的牌是:\n"); for(i=1;i<=s.top;i++) { printf("%d ",s.data[i]); } } else printf("\n桌上已经没有牌!\n"); } else if(q1.head==q1.hail) { printf("q2 win!\n"); printf("q2当前手中的牌是:\n"); for(i=q2.head;i<q2.hail;i++) { printf("%d ",q2.data[i]); } if(s.top>0)// { printf("\n桌上的牌是:\n"); for(i=1;i<=s.top;i++) { printf("%d ",s.data[i]); } } else printf("\n桌上已经没有牌!\n"); } getchar(); getchar(); return 0; }
总结:是不是发现栈和队列不是那么简单呢??大家可以考虑一下,其实无论是栈还是队列,都是一种自己定义的结构体,完全可以在理解struct之后,自行定义。
3.链表
在储存数的时候,我们通常用数组,但实际上,数组不够灵活而且特别呆板,比如说我们要让一组排好序的数字{2 3 5 8 9 10 18 26 32 }中插入一个6,我们其实只需要插入在5到8之间,但这样我们却需要从8向后所有的数字都依次向后移动一位,而实际上我们只需要用链表就可以完成操作,链表考指针和动态分配内存函数malloc来实现。我们先来回顾一下指针。
1.定义
int a; int*p;
第一行我们很熟悉了,就是定义一个整型变量 a。第二行你会发现在 p前面多了一个* 号,这就表示定义了一个整型指针变量 p。即定义一个指针,只需在变量前面加一个*号就OK啦。而至于浮点数的指针,则只需要把int换为double就好。 2.存储 p=&a; &这个符号很熟悉吧, 就是经常在 scanf函数中用到的&。&叫取地址符。这样整型指针p就获得了(存储了)整型变量 a的地址。3.使用用*p来使用,即指针所指向的内存中的值。直接可以理解为*p就是a。4.存储2我们想在程序中存储一个整数10, 除了使用 int a;这种方式在内存中申请一块区域来存储,还有另外一种动态存储方法。malloc(4) ;malloc函数的作用就是从内存中申请分配指定字节大小的内存空间。 上面这行代码就申请了4个字节。如果你不知道 int类型是4个字节的, 还可以使用 sizeof(int)获取 int类型所占用的字节数,如:malloc(sizeof(int) ) ;现在你已经成功地从内存中申请了 4个字节的空间来准备存放一个整数,可是如何来对这个空间进行操作呢?这里我们就需要用一个指针来指向这个空间,即存储这个空间的首地址。int*p;p=(int *)malloc(sizeof(int) ) ;完整代码如下, 注意当在程序中使用 malloc函数时需要用到 stdlib.h头文件。
#include<stdio.h>#include<stdlib.h>int main(){int*p; //定义一个指针pp=(int*)malloc(sizeof(int) ) ; //指针p获取动态分配的内存空间地址*p=10; / /向指针p所指向的内存空间中存入10printf( "%d" , *p) ; //输出指针p所指向的内存中的值getchar() ;getchar() ; return 0;}
而如果我们想要完成之前的问题,则要放弃前面所讲的,毕竟我们无法确定我们要有多少个整数,即我们需要申请多少的内存。所以在这里我们要申请一个结构体,
struct node { int data; struct node*next; };
上面代码中,我们定义了一个叫做 node的结构体类型,这个结构体类型有两个成员。第一个成员是整型 data,用来存储具体的数值;第二个成员是一个指针,用来存储下一个结点的地址。 因为下一个结点的类型也是 struct node, 所以这个指针的类型也必须是 struct node 类型的指针。
然后我们可以建立一个头指针用来储存下一节点的值,当链表没有建立时指针为空,然后创造第一个结点,用指针p指向这个点。在然后分别设置新创建的这个结点的左半部分和右半部分
struct node*head;head= NULL;//头指针初始为空struct node*p;/ /动态申请一个空间, 用来存放一个结点, 并用临时指针p指向这个结点p=(struct node*)malloc(sizeof(struct node) ) ;scanf("%d" ,&a) ;p- >data=a; / /将数据存储到当前结点的data域中p->next=NULL; //设置当前结点的后继指针指向空, 也就是当前结点的下一个结点为空
上面我们发现有一个新的符号->,这叫做结构体指针运算符,也是用来访问结构体内部成员的。因为此处的p是一个指针,所以不能用”.”,只能用->,下面用*next指向空。头指针的作用是方便以后从头便利整个链表
if(head==NULL)head=p; / /如果这是第一个创建的结点, 则将头指针指向这个结点elseq- >next=p; / /如果不是第一个创建的结点, 则将上一个结点的后继指针指向当前结点
完整代码如下:
#include<stdio.h>#include<stdlob.h>#include<algorithm>;//创造一个结构体用来表示链表的节点类型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;//上一个结点指向这个结点,因为p会指向下一个结点 } //输出所有链表内的数。 t=head; while(t!=NULL) { cout<<t->data; t=t->next;//继续下一个结点。 } getchar();getchar(); return 0;}
如果要插入一个值怎么办呢,很简单啊,你只要能找到一个可以比他大的值或者所有都比他小,第一种插入,第二种就直接放在队列尾部喽。
实现代码如下:
#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- >nex t=p; / /如果不是第一个创建的结点, 则将上一个结点的后继指针指向当前结点 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;}
4.模拟链表
分析:如果我用尽洪荒之力也看不懂指针,那怎么办呢,简单,看看这个,模拟链表,通过数组来实现链表的套路。
如图data是用来存放数据,right则是存放该数据右侧的数据在data中的位置,若right为0,那么右边就没有元素。比如说right[9].
代码实现如下:
#include<stdio.h>#include<algorithm>int main(){ int data[101],right[101]; int i,n,t,len; cin>>n; for(i=1;i<=n;i++) cin>>data[i]; len=n; for(i=1;i<=n;i++) { if(i!=n) right[i]=i+1; else right[i]=0; } len++; cin>>data[len]; t=1; while(t!=0) { if(data[right[t]]>data[len]) { right[len]=right[t];//将right[len]值指向第一个比他大的值 right[t]=len;//将此值指向插入的值 break; } t=right[t]; } //输出 t=1; while(t!=0) { cout<<data[t]; t=right[t]; } getchar(); getchar(); return 0;}
P.S.模拟链表也可以实现双向链表和循环链表,大家自行尝试哈
编者口述:有谁知道该如何下冰箱里的馄饨啊,先拿出来化着还是直接扔冰的进去啊求解。
- 无中生有之突击NOIP(2)--栈,队列,链表
- 无中生有之突击NOIP(1)--排序
- 无中生有之突击NOIP(4)--搜索
- 无中生有之突击NOIP(7)--Tree
- 无中生有之突击NOIP(特刊)突击学习总结1
- 无中生有之突击NOIP(3)--暴力枚举
- 无中生有之突击NOIP(5)--图的遍历
- 无中生有之突击NOIP(6)--最短路径
- 无中生有之突击NOIP(8)——更多算法(一)
- 【无中生有】---7---分布式消息队列
- 【NOIp复习】数据结构之栈、队列和二叉树
- 算法入门之突击战(贪心)
- 无中生有
- 英语突击 之断句
- 【无中生有】---2---数据库设计-1
- 【无中生有】---3---数据库设计-2
- 【NOIP数据结构专项】单调队列单调栈
- [NOIP]单调队列(更新中)
- Java:各种进制的转换、整型和String之间的转换
- 让自己的项目或库文件支持cocoapods
- android JNI 开发
- 泛型E,T,K,V等与?区别
- maven 使用多个配置文件
- 无中生有之突击NOIP(2)--栈,队列,链表
- 使用经验6
- 用友nc65开发中在删除时报错:实体[xxx]没有实现制定的业务接口:nc.itf.uap.pf.metadata.IHeadBodyQueryItf或者是nc.uap.pf.metadata.Hea
- LoadView调用顺序
- 配置Sublime Text 3以支持搜狗输入
- J2EE开发时的命名规范
- Bundle,Intent传递复杂的--Parcelable
- 组合数、逆元,数学
- JavaScript中清空数组的三种方式