2013年1月25日 星期五

[C Programming] 最長平台問題-延伸

針對最長平台問題,達成新的要求:
1. 請改寫程式,指出最長平台的位置。
2. 請寫一個程式,接收一個已經自小到大排好的陣列,
    把所有平台與它的位置都找出來。
3. 如果給定的陣列沒有依序排好,
    請寫一個程式可以找出所有平台與它們的位置,
    但給定的陣列卻不一定要排列好。
    (程式還可以只比n次就找到答案嗎?)
4.寫成遞迴的模式。

個人解法:
1. 我們插入一個變數來記錄一個平台的結束點,
    按照原方法,當找到一個更長的平台時,
    就把結束點再次變更到該索引值。

Code:
// Use *index to remember the end point.
int longest_pl(int x[], int n, int* index, int* len)
{
 *len = 1;
 int i;
 for(i = 1; i < n; i++)
  if( x[i] == x[i-*len] )
  {
   *len = *len + 1; 
   *index = i;
  }
 return *len;
}

2&3. 改寫大概就不能用原來那套的方法了,
因為必定要加入變數來記錄位置,所以就乾脆換方法吧?

Code:
// Complexity is O(n)
void all_pldata(int x[], int n, int rec[][3])
{
 int i;
 int tmp = 0;
 int cnt = 0;
 for(i = 1; i < n; i++) 
 {
  if( x[i] != x[tmp] )
  {
   rec[cnt][0] = x[tmp]; rec[cnt][1] = tmp; rec[cnt][2] = i-tmp;
   // printf("%d : Index %d ~ %d\n", x[tmp], tmp, i-1);
   tmp = i; cnt++;
  }
  else if( i + 1 == n ) // Last index
  {
   rec[cnt][0] = x[tmp]; rec[cnt][1] = tmp; rec[cnt][2] = i-tmp+1;
   // printf("%d : Index %d ~ %d\n", x[tmp], tmp, i);
   cnt++; rec[cnt][0] = -1;
  }
 }
 for(i=0; i<cnt; i++)
 {
  printf("%d : Index %d ~ %d\n", 
    rec[i][0], rec[i][1], rec[i][1]+rec[i][2]-1); 
 } 
}

int rec[][3]的一維長度設為n。每一組rec[]中存的依序為:
平台的數字、平台起始點、平台長度。
當中注意要處理最後一組的狀況,會有一點點差異。
如果不是全部平台長度為1,則rec不會被用完,在尾端補上-1,
可以方便外界想使用的時候,能有迴圈的終止條件。(否則會一路跑下去)
當然如果覺得多花了記憶體空間的話,也可以先找出共有幾組平台,
再進行宣告並把資料丟進去,只是就會多出先跑一次的執行時間。

比較的次數依舊為n次,因為開頭從i=1開始,
但結束的時候可能會跑過if以及else if兩個判斷式。

其實tricky一點來說,還要考慮n==1的狀況XD

// 把它放在函式的開頭!
if(n==1)
{
 printf("%d : Index %d ~ %d\n", x[0], 0, 0); 
 return;
}

4.其實跟原本的樣子差不多,寫成遞迴不會比較快就是了@@"

Code:
// 丟出來的時候不要忘了用*來取值。
int* recur_pl(int x[], int n, int *i, int* len)
{
 // Let *len = 1, i = 1 when first time calling this function.
 if(*i == n) return len;
 if( x[*i] == x[*i-*len] ) *len = *len + 1; 
 *i = *i + 1;
 return recur_pl(x, n, i, len);
}

2 則留言:

  1. Hi, 請問你覺得書上特別提到遞迴作法有什麼原因嗎?我看起來覺得它只是把 for-loop 變成遞迴,反而秏在 function call 上面。謝謝~ :p

    回覆刪除
    回覆
    1. 您好: )
      一般來說用遞迴取代for-loop大約有幾點好處:
      1.容易思考。
      這其實很不負責任XD
      因為完全不計較效率的狀況,用遞迴基本上只要會把大的問題拆成小的,
      然後再給出最小單位的問題的答案即可。
      2.比較不用在意迴圈的執行次數。
      很多發生bug情況大多在於差一個1少執行一次,
      或多執行一次,遞迴同樣不用考慮這個東西。
      3.有些拆分方法會使得遞迴能具有效率且易讀。
      可以的話請參照後面的快速Fibonacci算法一文,
      在解法中我們使用每次拆成一半的方式,
      但碰到奇數次則是拆成n+n+1的型式,
      這種拆法可以令程式加快,但如果相同方式要硬寫成迴圈,
      可讀性則會大幅降低,每次所要考慮的迴圈條件也較麻煩。
      在多個判斷式的條件下,相對於耗在function call上來說,
      可能就較不理想了。

      謝謝您的回應~
      最近因為即將上班的關係,較沒有空來繼續往下解,
      之後安定下來以後會繼續的XD 也請持續支持~

      刪除