简单静态查找

简单静态查找

更新:2013-11-11

//////////////////////////////////////////////////////////////////////


在这篇博客中,想对一些简单的查找算法做个总结


本文章中代码用到的数据结构有:

//元素的关键字类型
typedef int LSZ_SearchK;
//元素类型
typedef struct LSZ_SearchE{
	LSZ_SearchK key; //元素的键值,用于唯一标识
}LSZ_SearchE;
//顺序存储的树的子树的索引类型
typedef int LSZ_SearchTreeIndex;

//顺序存储的查找树结构的元素类型
typedef struct LSZ_SearchTreeArray{
	LSZ_SearchE *element;
	LSZ_SearchTreeIndex lChild;
	LSZ_SearchTreeIndex rChild;
}LSZ_SearchTreeArray;


为了统一,实现函数中所查找的数组的0下标元素为保留位
查找函数查找失败返回为0


名称:顺序查找
关键字:顺序,监视哨

在顺序表list中查找关键字等于key的数据。
本查找中设置监视哨,这样可以免去每一步都要检查表list是否查找完毕。
为了方便实现监视哨,list的0号元素留空,并且list所占空间为count+1。

int LSZ_search_sequence(LSZ_SearchE list[],
			LSZ_SearchK key,
			int count)
{
	int i;

	list[0].key = key;
	for (i = count; list[i].key != key; i--){
		;
	}
	return i;
}

名称:折半查找
关键字:顺序存储,有序,折半

在有序顺序表list中查找关键字等于key的数据。
注意程序中的折半是用/取整运算的,对结果是没影响的。

int LSZ_search_binary(LSZ_SearchE list[],
		        LSZ_SearchK key,
			int count)
{
	int low = 1, high = count, mid;

	while(low <= high){
		mid = (low + high) / 2; //取中元素
		if(list[mid].key == key){
			return mid;
		}
		else if(list[mid].key < key){
			low = mid + 1; //往前半段查找
		}
		else{
			high = mid - 1;
		}
	}
	return 0;
}

名称:斐波那契查找
关键字:顺序存储,有序,斐波那契,黄金分割

斐波那契序列:0,1,1,2,3,5,8,13...
设斐波那契数列第i个数为f[i]=f[i-1]+f[i-2]。
当i越大时,f[i]/f[i-1]越接近黄金分割比例0.6180339887...
在元素个数为f[i]-1的有序表list中查找关键字等于key的数据。
为什么是有f[i]-1个元素的序列才能用斐波那契查找呢,因为算法
是使用斐波那契的序列作为分割依据,每次判断时,对于长度为
f[i]-1长度的表是list[f[i-1]]元素和关键字比较。如果list[f[i-1]]不
等于关键字,以它为分界,将序列分成两部分(不包括第f[i-1]个元素),
然后每一部分的个数刚好也为同样结构。
前部分长度为f[i-1]-1,后部分的长度为f[i-2]-1=(f[i]-1)-(f[i-1]-1)-1。
此算法为递归求解。
递归设计:
不满足的部分:
对于该部分,先寻找最大能满足斐波那契查找的部分,剩下的部分,
也可以采用递归的方法来查找。设其结果为F2(list,low,key,f,n)
list[low+k]为k号元素(low初始值为0,k>=1)。

F1(list,low,key,f,n)={
        求取合适的斐波那契数f[i];
        判断list[f[i-1]]是否等于key,是则返回low+f[i-1];
        用斐波那契查找F2来查找元素,如果找到则返回;
        如果还有剩余,对剩余部分继续调用F1()查找;
}
对于每一有序列表,可一分成一部分长度满足f[i]-1,和剩下不满足条件的

另一部分。

满足的部分:

设其结果为F1(list,low,key,f,i),f[i]-1为序列长度。
斐波那契数列为0,1,1,2...这里的实现不支持0下标元素。因为当列表中只有
一个元素时,那么查找到的斐波那契数应该是f[3],此时要判断的是下标为
f[2]=1的元素,在继续查找已重复了。如果想使用0下标元素只需将下标减1。

F2(list,low,key,f,i)={
        判断list[f[i-1]]是否等于key,是则返回low+f[i-1];
        如果key小于list[f[i-1]]并且没查找完毕,
            则继续对上半部调用F2()查找;
        如果key大于list[f[i-1]]并且没查找完毕,
            则继续对下半部调用F2()查找;
}

因为递归结构简单,可以用循环结构来解决问题,并做一些优化。

//大于等于1小于等于46,能处理的斐波那契数极限
#define LSZ_SEARCH_FIBONACCI_MAX 46
int LSZ_search_fibonacci(LSZ_SearchE list[],
							LSZ_SearchK key,
							int count)
{
	int i, j, low1 = 0, low2;
	LSZ_SearchK cutPointKey;
	static int fNumber[47] = {0, 1, 1, 2, 3,
				5, 8, 13, 21, 34,
				55, 89, 144, 233, 377,
				610, 987, 1597, 2584, 4181,
				6765, 10946, 17711, 28657, 46368,
				75025, 121393, 196418, 317811, 514229,
				832040, 1346269, 2178309, 3524578, 5702887, 
				227465, 14930352, 24157817, 39088169, 63245986,
				102334155, 165580141, 267914296, 433494437, 701408733,
				1134903170, 1836311903
	}; //列举避免重复计算

	do{
		if(count <= fNumber[LSZ_SEARCH_FIBONACCI_MAX]){
			for(i = 2; count > fNumber[i]; i++){
				; //循环查找适合的斐波那契数
			}
		}
		else{
			i = LSZ_SEARCH_FIBONACCI_MAX + 1; //int能存储的最大斐波那契值为f[46]
		}
		if(key == list[low1 + fNumber[--i]].key){
			return low1 + fNumber[i];
		}
		low2 = low1; //low2用于内循环的规范的斐波那契查找
		j = i - 1; //fNumber[j]-1为用于规范斐波那契查找的数列长度
		while(j >= 2){ //该处为规范的斐波那契查找
			cutPointKey = list[low2 + fNumber[j]].key;
			if (key == cutPointKey){
				return low2 + fNumber[j];
			}
			else{
				if(key < cutPointKey){
					j--; //前部分
				}
				else{
					low2 += fNumber[j];
					j -= 2; //后部分
				}
			}
		}
		low1 += fNumber[i]; //用于查找不满足斐波那契数部分
	}while((count -= fNumber[i]) > 0);
	return 0;
}

名称:静态树表的查找
关键字:有序,元素被查找概率不等,二叉树,次优查找树

当元素被查找的概率不均匀分布时,怎么提高查找效率呢?
被查找概率高的元素如果优先被搜索到,那就很好了。该
算法就是将有序的数列按照其被查找的概率分布在一棵二叉树中,
该树的特点就是,所有分布在右子树的元素都大于分布在左子树的元素,
每棵树的根元素大于或等于左子树的根元素(或小于等于右子树的元素)。
每个元素以它被搜索到的概率作为权值,整棵树的所有元素结点到根的
带权路径长度(可以理解为:到根路径*权值)之和应该最小。
算法的关键是树的构建。
有一个虽然不是最优解,但是近似最优解的数构建方法,这方法构建的树
称为次优查找树。该方法是只对子树进行权值总和的粗略衡量,不管其路
径,而且使左右子树的权值总和尽量平衡,因此可以从根开始构建树。
有大量实验证明,次优查找树和最优查找树性能只差仅为1%到2%。
设元素序列list[i],(l<=i<=h),序列元素对应权值为w[i],(l<=i<=h),
对于元素序列l~h,取list[r]为根结点,两子树的分别为tree1(l~r-1),
tree2(r+1~h),括号里为其包含元素的下标。依据上述衡量思想,设置变
量diff衡量带权路径长,diff[r]=|(w[r+1]+...+w[h])-(w[l]+...w[r-1])|,
显然每次构建树tree(l~h),需要找最小的diff[r]。
为了计算方便,设累计权值,wHeap[i]=w[l]+...+w[i],所以有

diff[r]=|(wHeap[h]-wHeap[r])-(wHeap[r-1]-wHeap[l-1])|
         =|(wHeap[h]+wHeap[l-1])-wHeap[r]-wHeap[r-1]|;
还需要设定wHeap[l-1]=0,w[l-1]=0。
在下面给出递归求解的实现。

int LSZ_search_staticTree(LSZ_SearchE list[],
                            LSZ_SearchTreeArray tree[],
                            LSZ_SearchK key,
                            int count)
{
    LSZ_SearchTreeIndex index;
    LSZ_SearchK keyCheck;

    index = tree[0].lChild;
    do{
        keyCheck = (tree[index].element)->key;
        if(keyCheck == key){
            return index;
        }
        else if(key < keyCheck){
            index = tree[index].lChild;
        }
        else{
            index = tree[index].rChild;
        }
    }while(index != 0);
    return 0;
}
int LSZ_search_setNearOptimalTree(LSZ_SearchE list[],
                                    LSZ_SearchTreeArray tree[],
                                    float weight[],
                                    float weightHeap[],
                                    int count)
{
    int i, sum;

    for(i = sum = 0; i <= count; i++){
        sum += weight[i]; //weight[0]相对于w[l-1]=0
        weightHeap[i] = sum; //weightHeap[0]相当于wHeap[l-1]=0
    }
    tree[0].lChild = tree[0].rChild = LSZ_search_nearOptimalTree(list, tree, weightHeap, 1, count);
}
int LSZ_search_nearOptimalTree(LSZ_SearchE list[],
                                LSZ_SearchTreeArray tree[],
                                float weightHeap[],
                                int low,
                                int high)
{
    int i = low, j, diff,
        diffPart = weightHeap[high] + weightHeap[low - 1],
        min = fabs(diffPart - weightHeap[low] - weightHeap[low - 1]);

    for(j = low + 1; j <= high; j++){
        diff = fabs(diffPart - weightHeap[j] - weightHeap[j - 1]);
        if(diff < min){
            i = j;
            min = diff;
        }
    }
    if(i == low){
        tree[i].lChild = 0;
    }
    else{
        tree[i].lChild = LSZ_search_nearOptimalTree(list, tree, weightHeap, low, i - 1);
    }
    if(i == high){
        tree[i].rChild = 0;
    }
    else{
        tree[i].rChild = LSZ_search_nearOptimalTree(list, tree, weightHeap, i + 1, high);
    }
    tree[i].element = list + i; //存储元素的地址

    return i; //返回根元素下标
}


本博客的版权声明点击打开链接


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值