算法珠玑算法总结(转)

寒假抽空看了下编程珠玑,最大的感受便是编程也是一门艺术,期间也引导我思考了一些问题,回学校后打算做一个总结,主要是根据编程珠玑后面的Algorithm附录总结了一下这本书里面的经典算法。

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次方

思想:充分利用了已经计算出来的数据防止重复计算来减少了算法运行时间

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的多项式,

思想:迭代利用前面计算的结果计算后面

y = a[n];

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小的数字

改进快速排序算法

clip_image001

clip_image002

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本书的目标读者是准备去硅谷找工作的码农,也适用于在国内找工作的码农,以及刚接触ACM算法竞赛的新手。 市场上讲解算法的书已经汗牛充栋,为什么还要写这本书呢?主要原因是我对目前市场上的大部分算法书都不太满意。 本书有如下特色: 背后有强大的AlgoHub支持。 本书的所有题目,都可以在 www.algohub.org(即将上线) 上在线判断代码。这样的一大好处是,读者可以边看书,边实现自己的代码,然后提交到网站上验证自己的想法是否正确。AlgoHub的使命是成为最好的算法学习和交流平台。AlgoHub囊括了 POJ, ZOJ, leetcode, HackerRank 等网站的经典题目(一些质量不高的题目则忽略),且 AlgoHub有非常简单的加题系统,用户不需要写一行代码即可自己添加题目,所以AlgoHub的题库还在飞速增长中。 每道题都有完整的代码。 市场上的大部分书,都会讲思路,但给出的代码都是片段,不是完整可编译的代码。本书每题都有完整的代码,且每个代码经过千锤百炼,保证可读性的前提下尽可能简短,方面读者在面试中能快速写出来。 每道题都有多种解法。 本书的宗旨是,用尽可能少的题目,覆盖尽可能多的算法。本书中的的每道题都有多种解法,每种解法不是简单的小改进,而是完全不同的思路,力求举一反三,让读者触类旁通。 本书支持多种主流编程语言。 目前支持 Java, C++, C#, Python, Ruby, JavaScript, Swift, Scala, Clojure, 将来还会支持更多编程语言。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值