算法设计的一般方法
随机法依赖于随机数的统计特性。一个应用随机法的例子是快速排序。
分治法
动态规划
贪心法
近似法——旅行商问题
指针
在C语言中,通常声明一个void指针来表示泛型指针。
对于泛型指针来说类型转换非常重要,因为只有告诉泛型指针通过何种类型来访问地址时,泛型指针才能正确取到值。这是由于泛型指针不会告诉编译器它指向的是何种类型数据,因此编译器不知道多少个字节要被访问。也不知道应该如何解析字节。
函数指针是指向可执行代码段或调用可执行代码段的信息块的指针。
例如,在下面一段代码中,match被声明为一个函数指针,它接受两个void指针类型的参数,同时返回一个整型。
int(*match)(void*key1,void*key2);
假设有一个match_int函数,它的两个void 指针参数指向整型并回返1。可以这样赋值:
match = match_int;
调用:
retval = match(&x,&y);
递归
当递归调用是整个函数体中最后执行的语句且它的返回值不属于表达式的一部分,这个递归调用就是尾递归。
当编译器检测到一个函数调用是尾递归的时候,它就覆盖当前的活跃记录而不是在栈中去创建一个新的。
示例:以尾递归的形式计算阶乘的一个函数实现
//facttail.c
#include"facttail.h"
int facttail(int n,int a)
{
if(n <0)
return0;
elseif(0== n)
return1;
elseif(1== n)
return a;
else
return facttail( n -1, n * a);
}
计算n!:
facttail(n,1);
哈希表
示例:一个适用于处理字符串的哈希函数
//hashpjw.c
#include"hashpjw.h"
//hashpjw
unsignedint hashpjw(constvoid*key)
{
constchar*ptr;
unsignedint val;
//hash the key by performing a number of bit operations on it
val =0;
ptr = key;
while(*ptr !=‘\0‘)
{
unsignedint tmp;
val =(val <<4)+(*ptr);
if( tmp =(val &0xf0000000))
{
val = val ^(tmp >>24);
val = val ^ tmp;
}
ptr++;
}
//In practice, replace PRIME_TBLSIZ with the actual table size.
return val % PRIME_TBLSIZ;
}
树的平衡
树的平衡是指对于给定数量的结点,保证树的高度尽可能短的过程。这意味着在结点加入下一层之前必须保证本层结点满额。正式的说法是,如果满足树的所有叶子结点都在同一层上,或者所有 叶子结点都在最后两层上,且倒数第二层是满的,则这棵树是平衡的。
如果一棵平衡树最后一层的所有叶子结点都在最靠左边的位置上,则称这棵树是左平衡的。
表达式树
针对表达式(((74-10)/32×(23+17))的表达式树
使用先序、中序、后序遍历树,并把操作数与相关的操作符用括号括起来,得到前缀、中缀、后缀表达式:
前缀表达式
(×(/(-74 10)32)(+ 23 17)) = 80
中缀表达式
(((74 - 10)/32×(23+17)) = 80
后缀表达式(逆波兰表达式)
(((74 10 - )32/)(23 17+)× ) = 80
采用抽象栈状态机(abstract stack machine)来处理后缀表达式(逆波兰表达式)
二叉搜索树
为了更进一步理解保持一棵二叉搜索树平衡的重要性,不妨考虑一下当一棵二叉搜索树变得越来越不平衡的状态。如果是这样的话,查找一个结点的复杂度就变成了O(n),这就相当于从头到尾检索所有的结点。
NP完全问题
没有已知的求解多项式时间的算法,但也无法证明此多项式不存在,这类问题被称为NP完全问题
LRU页面置换算法
第二次机会置换法是实现LRU页面置换算法的一种方式。
它的工作方式是:维护一个当前存在于物理内存中的页面的循环链表。为了简化说明,假设链表中的每个元素只存储一个页码和一个引用值,引用值要么为1要么为0。所有的页面初始引用值都设置为0。每当系统访问页面时(比如,某个进程开始读或写某个页面),该页面的引用值就设置为1.
当需要某个页帧时,操作系统就使用它维护的循环链表以及引用值来判断哪些页面应该释放其页帧。为了确定这一点,开始遍历链表直到找到一个引用值为0的元素。当遍历每一个页面时,操作系统将页面的引用值从1重设回0。一旦它遇到引用值为0的元素,它就找到了一个自从上次遍历链表以来都没有被 系统访问过的页面,因此这个页面就是最近最少使用的页面。
集合覆盖
归并排序
归并排序需要额外的存储空间来运行,这也是它的一个缺点。然而,归并排序对于海量数据处理还是非常有价值的,因为它能够按预期将数据集分开。这使得我们能够将数据集分割为更加可管理的数据,接着用归并排序来处理数据,然后不断地合并数据,在这个过程中并不需要一次存储所有的数据。
多项式插值
求函数近似值的一种方法。其中函数值仅在几个点上已知。该算法的基础是建立级数小于等于n的一个插值多项式pn(z),其中n+1是已知函数值的个数。
最小二乘法
给定函数y(x)=b1x+b0,用来确定b1和b0的估计器方法,使得y(x)是n个点(x0,y0),...,(xn-1,yn-1)的集合的最佳拟合线。最佳拟合线使用最小二乘估计法来最小化每个点(xi,yi),i=0,...,n-1与沿y(x)的相应点(xi,y(xi))之间的垂直距离的平方和。
方程求解
使用牛顿迭代法
LZ77
相关数据结构(其中一种实现)
12位可以让滑动窗口容纳4KB,在一个32字节的缓冲区中,重复的字节不会超过32个字节,即长度为5位,下一个字节保存在【未匹配的符号】字段内。
第一位用1和0来表示在有重复项和没有重复项时数据表示的不同。
旅行商问题
在一幅图中,访问每个顶点一次,并最终返回起始顶点,这个访问的轨迹称为哈密顿圈。
要解决旅行商问题,需要用图G=(V,E)作为 模型,寻找图中最短的哈密顿圈。
一种近似地算旅行商路线的方法就是使用最近邻法。其运算方法如下,从一条仅包含起始点的路线开始,将此顶点涂黑。其他顶点为白色,在其它顶点加入此路线中后,再将相应顶点涂黑。接着,对于每个不在路线中的顶点v,要为最后加入路线的顶点u与v之间的边计算权值。回想一下,在旅行商问题中u与v之间边的权值就是u到v之间的距离。这个距离可以用每个顶点的坐标计算得到。
得到的近似解
实际上的最优化效果为: