《啊哈!算法》阅读笔记-----第二章《栈、队列、链表》

紧接着上一章的阅读,现在我来自习室开始了第二章(今天下雨了天气很凉爽,太适合学习啦)。那么就一起开始吧~

第二章----栈、队列、链表


第一节----解密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;
} 

好啦,那么第二章就练习到这里~
第三章《枚举》见~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值