《啊哈算法》讀后總結(上)


閱讀心得

這本書早有耳聞,但是一直沒有落實去看,最近在給自己充電,於是把這本書看了一遍。總體來說,這本書寫得很生動有趣,比較適合零基礎的人入門,對於我來說內容有些簡單(因為我本科已經接觸過一些算法,里面的有些內容我之前已經掌握)。但是,這本書除了算法之外,帶給我最大的幫助就是更加熟悉了一點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

以上就是該游戲的算法,由於代碼較多,這里就不再給出完整代碼啦!

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM