有一段时间未读什么书了,周末两天于是拿起程序设计实践大概翻了一遍,以下便是个人总结的第二章的内容。
第二章:算法与数据结构
对大部分程序员而言,所需要的是知道有哪些合适的、可用的算法和数据结构,知道如何在各种可以互相替代的东西之中做出选择。
2.1检索
顺序检索非常简单,但是他的工作量与被检索数据的数目成正比。如果要找的数据并不存在,数据量加倍也会是使检索的工作量加倍。这是一种线性关系--运行时间是数据规模的线性函数,因此这种检索也被称为线性检索。
二分检索方法效率更高一些。以下是二分检索的一个实例:
typedef struct Nameval Nameval;
struct Nameval{
char *name;
int value;
};
/*Value are Unicode/ISO106446 encoding.*/
Nameval htmlchars[]={
"AElig",0x00c6,
"Acute",0x00c1,
"Acirc",0x00c2,
/* ..... */
"zeta",0x03b6,
};
/* lookup:binary search for name in tab:reurn index */
int lookup(char *name,Nameval tab[],int ntab)
{
int low,high,mid,cmp;
low = 0;
high = ntab -1;
while(low<=high){
mid = (low+high)/2;
cmp = strcmp(name,tab[mid].name);
if(cmp<0)
high = mid -1;
else if(cmp>0)
low = mid +1;
else
return mid;
}
return -1;
}
可见,项目越多,二分检索的优势也就越明显。
2.2排序
二分检索只能用在元素已经排好序的数组上。如果事先不知道被处理的数据,那么就必须在程序运行中做排序。最好的排序算法之一是快速排序。
下面是quicksort函数做整数数组的排序:
/* quicksort:sort v[0]..v[n-1] into increasing order */
void quicksort(int v[],int n)
{
int i,last;
if ( n <= 1 )
return ;
swap(v,0,rand() % n);
last = 0;
for(i = 1;i < n; i++)
if( v[i] <v[0])
swap(v, ++last, i);
swap(v, 0, last);
quicksort(v, last);
quicksort(v+loast+1, n-last-1);
}
/* swap:interchange v[i] and v[j] */
void swap(int v[],int i,int j)
{
int temp;
temp = v[i];
v[i] = v[j];
v[j] = temp;
}
2.3库
C和C++的标准库里已包含了排序函数。他应该能够对付恶劣的输入函数,并运行的尽可能快。多年的历史证明,程序员能把二分检索程序写正确也是很不容易的。
2.4一个java快速排序
定义接口:
interface Cmp{
int cmp(Object x,Object y);
}
定义了对Integer类型经行比较的函数:
//Icmp:Inter comparison
class Icmp implements Cmp{
public int cmp(Object o1,Object o2)
{
int i1 = ((Integer) o1).intValue();
int i2 = ((Integer) o2).intValue();
if (i1 < i2)
return -1;
else if (i1 == i2)
return 0;
else
return 1;
}
}
定义了字符串的比较:
//Scmp:String comparison
class Scmp implements Cmp{
String s1 = (String) o1;
String s2 = (String) o2;
return s1.compareTo(s2);
}
quicksort函数实现:
//Quicksort.sort:quicksort v[left]..v[right]
static void sort(Object[] v, int left, int right, Cmp cmp)
{
int i,last;
if(left > = right)
return;
swap(v, left, rand(left, right));
last = left;
for(i = left+1; i<=right; i++)
if(cmp.cmp(v[i], v[left] )< 0)
swap(v, ++last, i)
swap(v, left, last);
sort(v, left, last-1, cmp);
sort(v, last+1, right, cmp);
}
随机数由一个函数生成,它产生范围在left和right之间的随机数:
static Random rgen = new Random();
//Quicksort.rand:return random integer in [left, right]
static int rand(int left, int right)
{
return left + Math.abs(rgen.nextInt())%(right - left + 1);
}
最后,如果调用Quicksort.sort对一个String数组排序,如下写:
String[] sarr = new String[n];
// fill n elements of sarr.....
Quicksort.sort(sarr, 0, sarr.length -1,new Scmp());
2.5大O记法
在这种描述中使用最基本的参数是n,即问题的规模,把复杂性或运行时间表达为n的函数。
2.8树
树是一种分层性数据结构。
二分检索树的构建方式:在树里递归向下,根据情况确定向左或向右,直到找到了链接新节点的正确位置。节点应该正确的初始化为Nameval类型的对象,它包含一个名字,一个值和俩个空指针。新节点总是作为叶子节点加入的,他没有子节点。
typedef struct Nameval Nameval;
struct Nameval {
char *name;
int value;
Nameval *left; /* lesser */
Nameval *right; /* greater */
}
树有插入、查找、遍历等算法。
2.9散列表
散列表是典型应用的符号表,在一些值(数据)与动态的字符串(关键码)集合的成员间建立一种关联。在实践中,散列函数应该预先定义好,事先分配好一个适当大小的数组,这些通常在编译时完成。数组的每一个元素是一个链表,链接起具有该散列值得所有数据项。