編程珠璣I算法總結

主要是根據編程珠璣後面的Algorithm附錄總結了一下這本書裏面的經典算法。html

1 展轉相減求最大公約數算法

思想:最大公約數能整除i和j,則其必定也能整除i-j(if i>j)編程

int gcd(int i, int j) 
{
 while(i != j) 
 { 
 if(i > j) i-=j; 
 else j-=i; 
 } 
 return i; 
}

 

2 快速求取x的n次方數組

思想:充分利用了已經計算出來的數據防止重複計算來減小了算法運行時間app

function exp(x,n) 
//pre n>=0  //post result = x^n 
{ 
if n=0 return 1; 
else if even(n) 
return square(exp(x,n/2)); 
else return x*exp(x,n-1); 
}

 

3 計算 y的多項式,函數

思想:迭代利用前面計算的結果計算後面oop

y = a[n];post

for i in [n-1,0]性能

y = y*x + a[i];優化

4 從[l,u]隨機取數,數的範圍可到30bit。

32位機上RAND_MAX是0x7fff

int bigrand() { return RAND_MAX*rand() + rand(); } 
int randint(int l, int u) { return l + bigrand()%(u-l+1); }

 

5 從n中隨機地取m個元素,並按序輸出

洗牌算法:

void genshuf(int m, int n) 

  int *x = new int[n]; 
  for(int i=0; i < n; i++) 
   x[i] = i; 
  for(int i=0; i < m; i++) 
  { 
   j = randint(i,n-1); //洗牌的精髓 
   swap(x[i],x[j]); 
  } 
  sort(x,x+m);//保證按序輸出 
  for(int i=0; i < m; i++) 
   cout<<x[i]<<endl; 
}

來算算機率,開始整個數組以固定方式初始化。接着每一個元素以rand方式和全部元素交換,這保證了隨機,時間複雜度O(nlogn)

Knuth算法,來自《計算機程序設計藝術》:

////////////////////////////////////////////////////////////////////////// 
//pre: 0<=m<=n , n is 0 to n-1 
//post: print m numbers selected from n 
void GenKnuth(int m, int n) 

  for(int i=0; i < n; i++) 
  { 
   if((rand()%(n-i)) < m)//此時選取機率爲toSelected/Remaining 
   { 
     std::cout<<i<<" "; 
     m--; 
    } 
  } 
}

時間複雜度O(n)。

集合算法:

// 
Initialize set S to empty 
  size = 0 
  while size < m do 
   t = bigrand() % n 
  if t is not in S 
   insert t into S 
  size++ 
  print the elements of S in sorted order

這個算法的缺點在於若是m和n接近的時候,比較失敗(已在集合的機率)會很大從而下降性能,時間複雜度也爲O(nlogn)

改進集合算法:

// 
void genfloyd(int m, int n) 

  set<int> S; 
  set<int>::iterator i; 
  for(int j=n-m; j < n; j++) 
  { 
    int t = bigrand()%(j+1); 
    if(S.find(t) == S.end()) 
    { 
      S.insert(t); // t not in S 
    } 
    else 
    { 
      S.insert(j); // t in S 
    } 
   } 
   輸出全部數字 
}

6 Problem: Rotate a one-dimensional vector of n elements left by i positions.

■常規解法:1 copy the first i elements of x to a temporary array, movint the remaining n - i elements left i places, and then copying the first i from temporary array back to the last positions in x.這個算法須要額外的內存。2 we could define a function to rotate x left one position and call it i times,這個算法很耗時。

■Juggling act,雜耍算法: move x[0] to the temporary t, then move x[i] to x[0], x[2i] to x[i], and so on (taking all indices into x modulo n),until we come back to taking an element from x[0], at which point we instead take the element from t and stop the process. 這個算法結合了常規兩個算法的優勢其中最關鍵的在於對須要執行次數的證實即移動多少回回到原來起點才能覆蓋全部數組答案是gcd(i,n)證實請點這裏

■翻轉算法,這個算法起源於這樣一個思想:Rotating the vector x is really just swapping the two segments of the vector ab to be the vector ba, where a represents the first i elements of x.由這引起了以下算法, 定義reverse(i,j),指把vector中i到j的元素翻轉,那麼這個代碼以下:

reverse(0,i-1)

reverse(i,n-1)

reverse(0,n-1)

 

7 Problem: 轉置矩陣

在每一個矩陣數據前面加一個行號列號的數據,對這些數據先按列號進行排序再按行號排,去除前置數據。

 

8 Problem: maxmum sum of any contiguous subvector of a vector.

四種解法見#bookmark=id.v94teif5zuqe

9 查找字典中的迴文

1 對字典中每一個單詞中的字母按字典序排序

2 對字典中的每一個單詞按字典序排序

3 迴文的單詞最後都被排在了一塊兒。

 

10 求文本中的最長重複子串

利用後綴數組進行求解

char* FindLCS(const char* str, int len) 

  //構建後綴數組 
  const char** suffix = new const char*[len]; 
  int i=0; 
  for(; i < len; i++) 
  { 
    suffix[i] = &str[i]; 
  } 
  qsort(suffix,i,sizeof(char*),pstrcmp);//對後綴數組排序 
//經過查找相鄰字符串,求最長重複子串 
  int maxlen = 0; 
  int maxi = -1; 
  for(int l=0; l < i-1; l++) 
  { 
    int temp = 0; 
    if((temp = comlen(suffix[l],suffix[l+1])) > maxlen) 
    { 
      maxlen = temp; 
      maxi = l; 
    } 
  } 
  char* result = new char[len]; 
  if(maxi != -1) 
  { 
    strncpy_s(result,len,suffix[maxi],maxlen); 
  } 
  delete [] suffix; 
  return result; 

其中用到以下函數: 
//給qsort用的比較函數 

//pre: p1,p2指向字符串,並以'\0'結尾 
//post: 若是函數的第一個參數小於第二個參數,返回負值;若是等於返回零值;若是大於返回正值 
int pstrcmp(const void* p1, const void* p2) 

  return strcmp( *(char* *)p1, *(char* *)p2); 


//求出p和q的最長公共字符數 
//pre: p爲母串,q爲查找串,p比q長,p最後以'\0'結尾 
//post: 返回所求數字 
int comlen(const char* p,const char* q) 

  int i=0; 
  while(*p && *p++ == *q++) 
    i++; 
  return i; 
}

11 尋找第k小的數字

改進快速排序算法

12 堆排序的操做 siftdown siftup(比建立堆,調整堆更原子點的操做,感受概括的 挺好)

見書

 

13 查找問題

通常解法:

順序搜索, 可用哨兵優化

二分搜索,僞代碼:

l = 0; u = n-1; 
loop 
  if(l>u) 
    p = -1;break
  m = l + (u-l)/2; 
  case 
    x[m]<t: l = m+1; 
    x[m]>t: u = m-1; 
    x[m] ==t: p=m; break;

用二分求第一個出現的數據(有重複數據的時候)

l = -1; u = n; 
while l+1 != u 

  m = (l + u) / 2; 
  if(x[m] < t) 
    l = m; 
  else 
    u = m; 

p = u; 
if(p >= n || x[p] != t) 
  p = –1

14 排序問題

通常解法:

插入排序,O(n^2), 部分有序時很是快速能在O(n)時間內解決,故快速排序再分治到小規模數據時用插入排序可以提升效率。穩定的排序算法。

for i = [1,n) t = x[i]; for(j=i; j>0 && x[j-1]>t; j--) x[j] = x[j-1]; x[j] = t;

(5行代碼搞定)

快速排序,平均狀況O(nlogn),最壞狀況(O(n^2),空間O(n)(遞歸堆棧)

兩種代碼,前者爲算法導論採用,後者是前者的優化版,比前者快常數因 子。

MIT’s(11行代碼):

//pre: x[l..u] 
//post:x[l..u] in increasing order 
void qsort(l,u) 

  if(l >= u) 
    return

  //partition 
  i = l; 
  for j = [l+1,u] 
  /*invariant: x[l+1..i]<x[l] && x[i+1..j-1]>=x[l]*/ 
    if(x[j] < x[l]) 
      swap(x[++i],x[j]); 
  swap(x[i],x[l]); 

  qsort(l,i-1); 
  qsort(i+1,u); 
}

Better Version(15行代碼):

void qsort(l,u) 

  if(l >=u) 
    return
  
  t = x[l]; i = l; j = u+1; 
  loop 
    do i++ while i<=u && x[i]<t 
    do j -- while x[j]>t 
    if(i>j) 
      break
    swap(x[i],x[j]); 
  swap(x[l],x[j]); 

  qsort(l,j-1); 
  qsort(j+1,u); 
}

Heap Sort:時間一直是O(nlogn),是漸進最優的排序算法。

for i = [2,n) 
  siftup(i); 
for(i=n; i>=2; i--) 
  swap(1,i); 
  siftdown(i-1);

(5行代碼搞定)

Bitmap Sort

通常需知足很數的範圍在必定區間以及不重複(可不知足)等限制

for i = [0,n) 
  bit[i] = 0 
  for each i in the input file 
    bit[i] = 1 
  for i = [0,n) 
    if bit[i] == 1 
      write i on the output file

轉自:http://www.cnblogs.com/HappyAngel/archive/2011/03/15/1985261.html

相關文章
相關標籤/搜索