希尔排序

希尔排序是插入排序的扩展。希尔排序的一个特点是:子序列的构成不是简单的逐段分割,而是将相隔某个增量的记录组成一个子序列。希尔排序的一个关键是:其步长(也就是增量)的取法,通常认为步长序列中的数字互质很重要。

希尔排序的C/C++代码实现:

void shell_insert(int sort_array[], int length, int h)
{
    int i, j, tmp;
    for (i = h; i < length; ++i)
        if (sort_array[i] < sort_array[i-h]) {
            tmp = sort_array[i];
            sort_array[i] = sort_array[i-h];
            for (j=i-h*2; j>=0 && tmp<sort_array[j]; j-=h)
                sort_array[j+h] = sort_array[j];
            sort_array[j+h] = tmp;
        }
}

void shell_sort(int sort_array[], int length)
{
    int h;
    for (h = 1; h < (length - 1) / 9; h = h * 3 + 1);
    while (h >= 1) {
        shell_insert(sort_array, length, h);
        h /= 3;
    }
}

直接插入排序相比,这里的shell_insert只做了两处修改:
1. 前后记录位置的增量不再是1,而是h;
2. sort_array[0]已不能再作为监视哨,而只能作为一个暂存单元,因为式子j -= h并不一定能使j值到达0。因此,这里干脆用一个temp来代替sort_array[0]做为暂存单元,而让sort_array[0]也加入排序,这样的做法也更通用。
继续阅读

简单排序方法

简单的排序方法主要是指直接插入排序、冒泡排序和简单选择排序。他们都是稳定的排序方法,平均时间复杂度都为O(n2),并且空间复杂度都为O(1)。

直接插入排序

直接插入排序(Straight Insertion Sort)是一种最简单的排序方法,它的基本操作是将一个记录插入到已排好序的有序表中,从而得到一个新的、记录数增1的有序表。直接插入排序的运行时间和数据原始排列顺序密切相关。对于已经排好序(或已几乎排好序)的数据,插入排序的效率会比较高。

直接插入排序的C/C++代码实现:

void insert_sort(int sort_array[], int length)
{
    int i, j;
    for (i = 2; i < length; ++i)
        if (sort_array[i] < sort_array[i-1]) {
            sort_array[0] = sort_array[i];
            sort_array[i] = sort_array[i-1];
            for (j = i - 2; sort_array[0] < sort_array[j]; --j)
                sort_array[j+1] = sort_array[j];
            sort_array[j+1] = sort_array[0];
        }
}

其中第6行在sort_array[0]处设置监视哨,避免在查找插入位置的过程中数组下标出界。

冒泡排序

冒泡排序(Bubble Sort)算法简洁,也很容易理解,因此经常被用来作为介绍排序算法的入门方法。但它对于除少数元素之外的数列排序很没有效率。在这三种简单排序算法当中也是最慢的一种(参考《算法I-IV(C++实现)》Robert Sedgewick著)。而且虽然它可以设置一个标志位以对某种情况下的排序效率进行改进(即判断文件是否已排好序,当其中一步已没有进行任何交换操作,即文件已排好序,就可以中断外部循环。这个改进可以提高冒泡排序对于已排好序的数据的运行效率),但是通常来说它的效率的提高还是比不上能中断内部循环的插入排序。
继续阅读

折半查找

折半查找(二分查找)是一种简单而又高效的查找算法,其查找长度至多为㏒2n+1(判定树的深度),平均查找长度为㏒2(n+1)-1,效率比顺序查找要高,但折半查找只能适用于顺序存储有序表(如对线性链表就无法有效地进行折半查找)。

折半查找的C/C++代码实现:

int binary_search(int search_table[], int length, int key)
{
    int low = 0;
    int high = length - 1;
    while (low <= high) {
        int mid = (low + high) / 2;
        if (key == search_table[mid])
            return mid;
        else if (key < search_table[mid])
            high = mid - 1;
        else
            low = mid + 1;
    }
    return -1;
}

这是折半查找的经典实现。不过它还有bug;效率上也还可以再改进。关于bug部分在这里做一部分摘录(如欲详细了解请见《代码之美》,效率改进详见《编程珠玑》):

每当讨论漂亮的代码,就很容易让人联想起Jon Bentley那本经典的由Addison-Wesley出版的《Programming Pearls》 (中文名《编程珠玑》)。我就是在读那本书的时候,发现了我要找的代码例子:二分查找。
继续阅读

顺序查找

顺序查找(Sequential Search)算法简单,适用面广(对查找表结构无任何要求),但其查找效率较低,等概率下平均查找长度为(n+1)/2。

在条件允许情况下,顺序查找可以设置一个监视哨,目的在于免去查找过程中每一步都要检测整个表是否查找完毕。严版《数据结构》说这个改进能使查找长度在≥1000时查找所需平均时间几乎减少一半。

顺序查找的C/C++代码实现:

int seq_search(int search_table[], int length, int key)
{
    int i;
    search_table[0] = key;
    for (i = length; key != search_table[i]; --i);
    return i;
}

第4行即为设置监视哨,当然监视哨也可以设在高下标处。

查找表

查找表(Search Table)是一种在实际应用中大量使用的数据结构。查找表分为静态查找表和动态查找表,静态查找表只进行查找操作,动态查找表则在查找的同时还进行插入或删除操作。

静态查找表主要是:顺序表、线性链表、索引顺序表等。
动态查找表主要是:二叉排序树、平衡二叉树、B-树、B+树等。

哈希表

根据设定的哈希函数H(key)和处理冲突的方法将一组关键字映像到一个有限的连续的地址集(区间)上,并以关键字在地址集中的“像”作为记录在表中的存储位置,这种表就叫哈希表。

哈希函数的构造方法:
1. 直接定址法 H(key) = key 或 H(key) = a * key + b
2. 除留余数法 H(key) = key % p, p ≤ length (一般情况下,p为质数或不包含<20的质因数的合数。)
3. 等等…

处理冲突的方法:
1. 开放定址法   (1). 线性探测再散列 (2). 二次探测再散列 (3). 伪随机数探测再散列
2. 链地址法
3. 等等…

哈希表的查找长度:依赖于3个因素,哈希函数、处理冲突的方法和哈希表装填因子。