码迷,mamicode.com
首页 > 编程语言 > 详细

《啊哈算法》读后总结(上)

时间:2019-11-16 19:28:11      阅读:76      评论:0      收藏:0      [点我收藏+]

标签:语言   回文字符串   can   mda   就是   交换   名称   一个   开始   

阅读心得

这本书早有耳闻,但是一直没有落实去看,最近在给自己充电,于是把这本书看了一遍。总体来说,这本书写得很生动有趣,比较适合零基础的人入门,对于我来说内容有些简单(因为我本科已经接触过一些算法,里面的有些内容我之前已经掌握)。但是,这本书除了算法之外,带给我最大的帮助就是更加熟悉了一点C,因为我的C语言不太好,一直都是学习java,比较逃避C,但是在学习这本书的时候,我把里面出现的所有代码都自己消化并手写了一遍,虽然里面的代码十分浅显,但是一本书写下来,我已经对C没有那么恐惧了。所以,我从心里很喜欢这本书。希望想要入门的小伙伴也能把这本书好好看一看。

阅读总结

【这本书一共有九章,第九章是一个思路引领,前八章是妥妥的干货。在这里我对这本书的内容,结合自己的理解做一些记录,方便日后能够复习】

第一章:排序(有多重要大家心里都知道,不会排序的人生是不完整的人生~)

1.桶排序

说实话,我是在这本书里第一次接触桶排序,之前学的排序算法上来都是直接选择、插入、快速、合并,看了这本书才知道还有桶排序这个神奇宝贝哈哈哈。桶排序堪称最快最简单的排序,它的原理是定义一个数组book[]来标记数字是否出现。比如我们现在要对从1到99之间的若干数字进行排序,那么就定义一个数组book[],每出现一个数字 i,就让对应的book[i]的值加1,输入所有的数字之后,我们按照顺序输出即可。

??让我们来看一下核心代码:

//排序
for
(i=1;i<=n;i++) { scanf("%d",&t); book[t]++; } //输出 for(i=1;i<=100;i++){ for(j=1;j<=book[i];j++){ printf("%d", i); }
}

2.冒泡排序

排序界的鼻祖没人反对吧?反正它是我学的第一种排序嘿嘿嘿。简单来说,就是每次都比较两个相邻的元素,如果它们的顺序错误就把它们交换过来。比如我们想对n个数进行从大到小的排序,我们就需要进行n-1趟排序(最后一个数不用排),每次排序都找出最小的一个数放在最后。冒泡排序的时间复杂度为O(N^2)。

??让我们来看一下核心代码:

//排序
for(i=1;i<=n-1;i++){
    for(j=1;j<=n-i;j++){
        if(a[j]<a[j+1]){
           t=a[j];
           a[j]=a[j+1];
           a[j+1]=t;
         }
     }
}
//输出
for(i=1;i<=n;i++)
      printf("%d",a[i]);

3.快速排序

号称最常用的排序,桶排序浪费空间,冒泡排序浪费时间,于是快速排序前来报到~快速排序的原理是这样的:假设我们现在要对一行数字进行从小到大排序,我们在这一行数中随便找一个数作为基准数(用来参照的数),然后让一个哨兵从这行数的最右边开始,一步一步移动,找比基准数还小的数,另外一个哨兵从最左边找比基准数最大的数,(前提是不能相撞哦),到之后,将这两个数进行交换,然后继续找下去,直到碰头为止,最后,我们再把基准数与哨兵的位置交换,一次排序就完成啦!此次排序将基准数放在了最终位置上,我们接着对基准数左边和右边分别重复刚刚的过程,就可以完成所有数字的排序啦。快速排序的平均时间复杂度为O(NlogN)。

??让我们来看一下核心代码:

void quicksort(int left,int right){
int i,j,t,temp;
if(left>right)  return;
temp=a[left];
i=left;
j=right;
while(i!=j){
  while(a[j]>=temp&&i<j)   j--;
  while(a[i]<=temp&&i<j)   i++;
  if(i<j){
  t=a[i];
  a[i]=a[j];
  a[j]=t;
}
} 
a[left]=a[i];
a[i]=temp;
quicksort(left,i-1);
quicksort(i+1,right);
}

 第二章:栈、队列、链表

1.解密QQ号——队列

 问题是这样的:小哼想要小哈的QQ号,小哈给了小哼一串数字,并告诉小哼,这串数字已经加密,解密方法是:先删除第一个数字,再将第二个数字放在末尾,然后删除第三个数字,再将第四个数字放在末尾。。。以此类推,最后得到所有的删除数字就是正确的QQ号。

这是一个非常典型的队列问题,队列的特点就是先入先出(先进去的排在前面,出来的时候也是前面的先出来,可以把它想象成一个水平放置的水管),删除最前面的数字就像队列中把队首元素出列,将第二个数字放在末尾,就像把队首的元素放在队尾一样,所以实现起来就比较清晰了。

这里还有一个问题,就是小哈给的QQ 号是固定的,如果我们不知道QQ号,需要这个队列具有一定灵活性怎么办呢?于是我们引出了另一个概念——结构体。

什么是结构体呢?其实就是把一组相关联的数据放在一起变成一个整体,这个整体就是结构体。下面我们来定义一个结构体:

struct queue{
 int data[100];//队列的主体,用数组来存储内容
 int head;//队首
 int tail;//队尾
};
//千万注意定义完结构体之后的;不能丢掉

定义好这个结构体之后,就相当于定义了一个“数据类型”,当我们想要创建这样的结构体时,可以直接声明struct queue q;这个q就是结构体的名称。

回到刚才的问题:如何让队列中的元素可变呢?很简单,因为队列的data[]数组存放的是队列中的数据,我们给data[]数组元素赋值即可,需要注意,队列是在队尾插入元素的。

for(i=1;i<=9;i++){
  //依次向队列插入9个数
  scanf("%d",&q.data[q.tail]);
  q.tail++;
}

??让我们来看一下完整代码:

#include <stdio.h>
struct queue{
  int head;
  int tail;
  int data[100];
};
int main(){
  struct queue q;
  int i;
  //初始化队列
  q.head=1;
  q.tail=1;
  for(i=1;i<=9;i++){
    scanf("%d",&q.data[q.tail]);
    q.tail++;
   }
  while(q.head<q.tail){
     printf("%d",q.data[q.head]);
     q.head++;
     q.data[q.tail]=q.data[q.head];
     q.tail++;
     q.head++;
}
getchar();getchar();
return 0;
}

2.解密回文——栈

问题:如何判断一个字符串是否为回文(正读反读均相同的字符串)

我们知道,如果一个字符串是回文的,那么它一定是中间对称的,我们找到中点mid之后,将mid之前的字符全部入栈,然后将当前栈中的字符依次出栈(栈的特点是先入后出,即后入的在上面,所以弹出来时也先弹出来,可以把它想象成一个桶),并与mid之后的字符进行匹配,如果都能匹配则说明当前这个字符串是回文字符串。

既然队列有队尾和队首,那么栈也一定有特殊位置——栈顶top,注意top指向栈顶元素,每次入栈之前都要先top+1,栈顶元素表示为s[top]。

??我们来看一下完整代码:

#include <stdio.h>
#include <string.h>
int main(){
  char a[101],s[101];
  int i,len,mid,next,top;
  gets(a);//读入一行字符串
  len=strelen(a);//字符串长度
  mid=len/2-1;//字符串中点
  
  top=0;//栈的初始化
  for(i=0;i<=mid;i++);
  s[++top]=a[i];
  
  //判断字符串长度是奇数还是偶数,偶数从mid+1开始比,奇数从mid+2开始比,中间的那个不用管
  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--;
   }

  //如果全都匹配完,top为0
  if(top==0)  printf("YES");
  else  printf("NO");
  
  getchar();  getchar();
  return  0;
}
    

3.纸牌游戏——小猫钓鱼

游戏规则是这样的:现在有从1到9的纸牌若干,将其平均分成两份给小哼和小哈,小哼先拿出第一张牌在桌子上,然后小哈也拿出一张牌在桌子上,并放在小哼出的牌的上面,就这样两人交替出牌。出牌时,如果某人打出的牌与桌子上某张牌相同,即可将两张相同的牌以及中间夹着的牌全部取走,并依次放到自己手中牌的末尾。当任意一人手中的牌全部出完时,游戏结束,对手获胜。请你写一个程序来自动判断谁将获胜。

我们来分析一下思路:首先小哼和小哈都有两种操作,也就是出牌和赢牌,出牌的动作很像出队,赢牌的动作很像入队(赢的牌又被放到自己手中牌的末尾),而桌子就像是一个栈,每打出一张牌就是入栈一次(后出的牌在先出的牌的上面),当有人赢牌的时候,就把上面一部分的牌拿走,这个过程就像出栈。综上,我们需要两个队列和一个栈来模拟本次的游戏。

首先,用一个结构体来实现队列:

struct queue{
  int data[1000];
  int head;
  int tail;
};

接着,用一个结构体来实现栈:

struct stack{
  //因为桌子上最多有9张牌,所以data的大小设置为10即可。
  int data[10];
  int top;
}

然后,我们定义两个队列q1,q2来模拟小哼和小哈的牌,用一个栈来模拟桌子上的牌。

struct queue q1,q2;
struct stack s;

接下来初始化队列和栈:

q1.head=1;
q1.tail=1;
q2.head=1;
q2.tail=1;
s.top=0;

然后分别读入小哼和小哈手中的牌,我们假设游戏开始时,小哼和小哈手中各有6张牌。

//先读入6张牌,放到小哼手上
for(i=1;i<=6;i++){
  scanf("%d",&q1.data[q1.tail]);
  q1.tail++;
}
//再读入6张牌,放到小哈手上
for(i=1;i<=6;i++){
  scanf("%d",&q2.data[q2.tail]);
  q2.tail++;
}

现在准备工作基本做好,游戏正式开始,小哼先出牌。

//将小哼出的牌赋值给临时变量t
t=q1.data[q1.head];

那么我们如何判断小哼出的牌是赢是输呢?很简单,我们只要把 t 与桌子上的牌(也就是栈中的元素)一个一个进行比较,如果有相同的牌,标志位flag就为1,跳出比较。

//标志位初始时为0
flag=0;
for (i=1;i<=s.top;i++){
   if(t==s.data[i]{
       flag=1;
       break;
    }
}

接下来分别对 flag=0和 flag=1的情况进行分析:

//如果标志位为0,则q1出列,s入栈
if(flag==0){
  q1.head++;
  s.top++  s.data[s.top]=t;
}
//如果标志位为1,需要将赢得的牌入队
if(flag==1){
  //先把正在出的这张牌入队
   q1.head++;
   q1.data[q1.tail]=t;
   q1.tail++;
  //再把桌子上的牌入队
  while(s.data[s.top]!=t){
     q1.data[q1.tail]=s.data[s.top];
     q1.tail++;
     s.top--;
   }
}

小哼出牌基本模拟完了,小哈出牌也是一样的,接下俩我们要判断游戏如何结束。只要两个人有一个人没有牌游戏就会结束。

//当队列不为空时才能继续游戏,这个while循环应该加在两人出牌的外面
while(q1.head<q1.tail&&q2.head<q2.tail)

最后一步,输出谁最终赢得了游戏,以及游戏结束后,获胜者手中的牌和桌子上的牌。

if(q2.head==q2.tail){
printf("小哼win\n");
ptintf("小哼当前手中的牌是");
for(i=q1.head;i<=q1.tail-1;i++)
     printf(" %d",q1.data[i]);
//如果桌子上有牌则一次输入桌子上的牌
if(s.top>0){
   printf("\n桌子上的牌是);
   for(i=1;i<=s.top;i++){
         printf(" %d",s.data[i]);
     }
}
else   printf("桌子上已经没有牌了");
}

小哈是否赢牌也跟上面一样的思路。

但是,上面的代码其实有一个可以优化的地方,还记得我们是怎么判断有没有赢牌的嘛?没错,我们是通过枚举桌子上每一张牌来判断的,也就是用了一个for循环,其实可以用一个更好的方法,就是用一个数组来记录当前桌子上有哪些牌。

int book[10];
//初始化
//一张牌也没有出现
for(i=1;i<=9,i++)
     book[i]=0;

//接下来,如果出的牌桌子上没有,就入栈并且将book[t]=1;而且要注意的是,在桌子上的牌出栈的时候,每出一张牌,就要将其对应的book[s.data[s.top]]=0;

//出牌时
t=q1.data[q1.head];
if(book[t]==0)
{
  q1.head++;
  s.top++;
  s.data[s.top]=t;
  book[t]=1;
}
//出栈时别忘了标志为0

以上就是该游戏的算法,由于代码较多,这里就不再给出完整代码啦!

 

 

 

《啊哈算法》读后总结(上)

标签:语言   回文字符串   can   mda   就是   交换   名称   一个   开始   

原文地址:https://www.cnblogs.com/iceywu/p/11827932.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!