紧接着上一章的阅读,现在我来自习室开始了第二章(今天下雨了天气很凉爽,太适合学习啦)。那么就一起开始吧~
第二章----栈、队列、链表
第一节----解密qq号----队列
这本书的引入是很有趣的,就算有时有点烦躁,但看这本书我不会觉得这本书看不下去,真的是很适合我这种懒孩子。那我就直接总结重要知识点,应该会很枯燥的hhhh
…
我们要进行的解密操作是:
将序列的第一个数删除,将第二个数移到序列末尾;
将序列的第三个数删除,将第四个数移到序列末尾;
将序列的第五个数删除,将第六个数移到序列末尾;
…
直到删除完所有数再按照删除的序列整理所得,就是解密后的序列。
…
我们用数组来存储数据序列,那么数组是怎么删除的呢?想想,是通过移动后续元素向前,覆盖掉当前要删除的元素来达到删除的效果的。这样的移动操作是非常消耗时间的。所以引入俩个整形变量(这里注意和指针的区别),head和tail。
head记录序列第一位的下标,tail记录序列最后一位元素的下一位的下标。
注意哈,这里的tail不是标记序列的最后一个元素。是为了避免当头标记和尾标记相遇在一个元素上时产生误会。因为我们规定俩标记相遇时就代表队列为空。
有个两个标记之后,一切就变得很明了了。
删除头标记所标记的元素,只需:
head++;
往队列尾部添加元素就只需:
q[tail] = x;
tail++;
…
分析结束了,实现代码也就很好写了。
//当头标记小于尾标记时
while(head<tail){
printf("%d,",q[head]); //打印被删除出列的数据
head++; //然后进行真正的删除操作
q[tail] = q[head]; //尾标记当前标记的位置的值换成头标记当前标记的值
tail++; //尾标记后移
head++; //头标记移到下一个待处理的数据
}
…
经过一个小demo我们其实已经在用队列的概念了。
队列:是线性结构的一种。
但比较特殊的是:
只允许在队首进行删除操作,也就是“出队”;
只允许在队尾进行添加操作,也就是“入队”。
当head=tail时,规定为空队列。(此处呼应tail为什么不标记末元,而是末元的下一个位置)。
第二节------解密回文-----栈
回忆一下,队列是一个先进先出的数据结构,我们用队列实现了一个解密qq号的demo。现在我们继续来看一种先进后出的线性结构----栈。
…
我们要思考的问题是:如何判断一个字符串是不是回文字符串?
回文:abba abcba
分析:
1.输入一个字符串
2.计算字符串的长度len
3.计算字符串的mid,注意mid并不是长度的一半,只是被我们用来方便判断回文的一个特殊位置
4.把字符串的前部分入栈
5.之后再出栈,边出栈边和字符串对应的值进行对比
代码实现:
//判断是否回文---用栈来实现
#include<stdio.h>
#include<string.h>
int main(){
char str[20],s[20];
int i,len,mid,top,next;
//1.输入一个字符串
printf("请输入一个字符串:");
scanf("%s",&str);
//2.计算字符串的长度len
len = strlen(str);
//3.计算字符串的mid,注意mid并不是长度的一半,只是被我们用来方便判断回文的一个特殊位置
mid = len/2 -1;
//4.把字符串的前部分入栈
top=0;
for(i=0;i<=mid;i++){
top++;
s[top]=str[i];
}
//为之后的对比操作做准备,计算出需要进行字符匹配的起始下标
if(len%2==0){
next = mid + 1;
}else{
next = mid + 2;
}
//5.之后再出栈,边出栈边和字符串对应的值进行对比
for(i = next;i<len;i++){
if(str[i]!=s[top]){
break;
}
top--;
}
//6.输出结果
if(top==0){
printf("yes");
}else{
printf("no");
}
}
第三节 扑克游戏
接下来,我们要去试着用栈和队列的思想做一个综合的小demo.
demo是一个小游戏:甲和乙一起玩一个简单的扑克游戏。游戏开始,俩人手中各持有1~9牌号中的6张牌,甲先出牌,按照牌的到手顺序依次轮流将牌放在桌上,
过程中,若有刚出的牌与桌上已有的另一张牌值相同,则出牌的人赢走俩张牌之间的所有牌(包括这俩张牌)。谁先把对方手中的牌玩得一个不剩就算赢。
分析:
1.拿在手里的牌是按顺序出的,所以我们用两个队列来存储。
2.桌上的牌需要判断与刚出的牌是否一致,考虑到循环遍历判断太麻烦,正好本质就是“除重”,我们参照桶排序的方法来实现除重。
3.桌上所有的牌信息用栈来存。
代码实现:(本垃圾敲了好一下午,debug滴得我慌慌)
#include<stdio.h>
//1.定义队列来存储手中的牌
struct queue{
int data[9];
int head;
int tail;
};
//2.定义栈来存储桌上的牌
struct stack{
int data[10];
int top; //指向栈顶的标记
};
int main(){
struct queue q1,q2;
struct stack poker;
int i,j,t,book[10];
//3.初始化俩人手中的牌和桌上的牌
q1.head=1;q1.tail=1;
q2.head=1;q2.tail=1;
poker.top=0;
//4.分别给俩人发6张牌
printf("请给甲发牌:");
for(i = 1; i <= 6;i++){
scanf("%d",&q1.data[q1.tail]);
q1.tail++;
}
printf("请给乙发牌:");
for(j = 1; j <= 6;j++){
scanf("%d",&q2.data[q2.tail]);
q2.tail++;
}
//5.初始化标记桌面上的牌的桶
for(i = 1;i<10;i++){
book[i]=0;
}
//6.当俩人队列都不为空的时候进行出牌循环
while(q1.head<q1.tail && q2.head<q2.tail){
//甲出牌
t = q1.data[q1.head];
//判断甲是否赢牌
if(book[t] == 0){
//甲不赢牌,要进行牌出甲队列、入桌子栈、桶赋值三个操作
q1.head++;
poker.top++;
poker.data[poker.top]=t;
book[t]=1;
} else{
//甲赢牌:刚出的牌出甲队列进入甲队列队尾、倒序遍历桌子的牌,依次进甲队列、取消桶中的标记
q1.head++;
q1.data[q1.tail] = t;
q1.tail++;
while(poker.data[poker.top]!=t){
book[poker.data[poker.top]] = 0;
q1.data[q1.tail] = poker.data[poker.top];
poker.top--;
q1.tail++;
}
//收回桌子上另外一张t
book[poker.data[poker.top]] = 0;
q1.data[q1.tail] = poker.data[poker.top];
q1.tail++;
poker.top--;
}
//7.若甲手中的牌已打完
if(q1.head==q1.tail){
break;
}
//8.乙出牌
t = q2.data[q2.head];
if(book[t] == 0){
q2.head++;
poker.top++;
poker.data[poker.top] = t;
book[t] = 1;
} else{
q2.head++;
q2.data[q2.tail] = t;
q2.tail++;
while(poker.data[poker.top]!=t){
//每个拿回的牌的book都要置零
book[poker.data[poker.top]] = 0;
q2.data[q2.tail] = poker.data[poker.top];
poker.top--;
q2.tail++;
}
book[poker.data[poker.top]]=0;
q2.data[q2.tail] = poker.data[poker.top];
q2.tail++;
poker.top--;
}
}
//9.当俩人中有一个的牌被赢完时
if(q1.head == q1.tail){
//乙赢了
printf("乙赢得了这场扑克游戏");
//输出乙现在手里的牌
printf("乙现在手中的牌是:");
for(i = q2.head;i<q2.tail;i++){
printf("%d ",q2.data[i]);
}
//输出桌上的牌(如果有的话)
if(poker.top>0){
printf("桌子上的牌:");
for(i = 1;i <= poker.top;i++ ){
printf("%d ",poker.data[i]);
}
}else{
printf("桌子上没牌了。");
}
}
if(q2.head == q2.tail){
printf("甲赢得了这场扑克游戏");
//输出甲现在手里的牌
printf("甲现在手中的牌是:");
/*wihle(q1.tail!=q1.head){
printf("%d ",q1.data[--tail]);
}*/
for(i = q1.head;i<q1.tail;i++){
printf("%d ",q1.data[i]);
}
//输出桌上的牌(如果有的话)
if(poker.top>0){
printf("桌子上的牌:");
for(i = 1;i <= poker.top;i++ ){
printf("%d ",poker.data[i]);
}
}else{
printf("桌子上没牌了。");
}
}
getchar();
getchar();
return 0;
}
第五节 链表
之前我们使用数组来存储数据,明显它适合查找和修改,但是在增加和删除这俩个功能模块,数组可就墨迹得多了。主要还是看实际需求来选择存储结构了。链表呢,它的查询和修改和数组比起来是有点浪费时间,但是链表的优点在于,删除和增加的时候,不用像数组那样移动很多个元素,只需要修改一下指针就好了。
在开始之前,我们先来复习三个符号和一个函数:
& :取地址符
*:乘号
:声明一个指针变量
:间接访问运算符
->:结构体指针运算符,用来访问结构体内部成员
malloc->从内存中申请分配指定大小的内存空间。
malloc的使用:
1.直接括号内写要申请分配的字节数。eg: malloc(4)
2.若不清楚类型的字节大小,可间接表示。eg: malloc(sizeof(int))
3.由于malloc函数的返回值是:void * 类型,表示未确定类型的指针。在c和c++中void *类型可以强制转换为各种类型。一般都需要进行类型转换。eg:p= (int *)malloc(sizeof(int))
下面我们直接来做一个例子:
直接上代码啦:
//LinkedList Insert
#include<stdio.h>
struct node{
int data;
struct node *next;
};
int main(){
int n,i,x,index,sum;
struct node *p,*q,*head,*t,*ne;
printf("输入初始化链表长度:");
scanf("%d",&n);
head=NULL;
printf("请输入链表数据:");
//------------初始化链表数据------------------
for(i=0;i<n;i++){
p = (struct node *)malloc(sizeof(struct node));
scanf("%d",&p->data);
p->next=NULL;
if(head==NULL){
head=p;
}else{
q->next=p;
}
q=p;
}
//------------输出原始链表-----------------------
t=head;
printf("初始化结束的链表为:");
printf("\n");
while(t!=NULL){
printf("%d ",t->data);
t=t->next;
}
//-------------插入数据到链表指定位置--------------
printf("请输入要插入的数,以及它的位置索引:例如(6 4)");
printf("\n");
sum=2; //sum=1的话,插入的位置会后推一个。这里还没想透彻。估计是头节点造成的。
scanf("%d %d",&x,&index);
t=head;
while(t!=NULL){
if(sum==index){
ne = (struct node *)malloc(sizeof(struct node));
ne->data=x;
ne->next=t->next;
t->next=ne;
break;
}
sum++;
t=t->next;
}
//--------------------输出链表数据--------------
t=head;
printf("插入结束的链表为:");
printf("\n");
while(t!=NULL){
printf("%d ",t->data);
t=t->next;
}
getchar();
getchar();
return 0;
}
好啦,那么第二章就练习到这里~
第三章《枚举》见~