查找算法(二分查找,差值查找,斐波那契查找,树表查找)
平均查找长度
平均查找长度(Average Search Length,ASL):需和指定key进行比较的关键字的个数的期望值,称为查找算法在查找成功时的平均查找长度。
对于含有n个数据元素的查找表,查找成功的平均查找长度为:
Pi查找表中第i个数据元素的概率
Ci:找到第i个数据元素时已经比较过的次数
顺序查找
ASL = (1+2+3+......+n)/n
时间复杂度O(n)
二分查找,插值查找
时间复杂度O(logn)
二分查找:
以升序数列为例,比较一个元素与数列中的中间位置的元素的大小,如果比中间位置的元素大,则继续在后半部分的数列中进行二分查找;如果比中间位置的元素小,则在数列的前半部分进行比较;如果相等,则找到了元素的位置。每次比较的数列长度都会是之前数列的一半,直到找到相等元素的位置或者最终没有找到要找的元素。
差值查找:
与二分查找仅仅是查找点的不同:
折半查找的前提条件是需要有序表顺序存储,对于静态查找表,一次排序后不再变化,折半查找能得到不错的效率。但对于需要频繁执行插入或删除操作的数据集来说,维护有序的排序会带来不小的工作量,那就不建议使用。
代码(C):
#include<stdio.h> binarySearch(int a[],int n,int key) { int low = 0; int high = n-1; while(low <= high) { /*int mid = (low + high)/2; int midVal = a[mid]; if(midVal < key) { low = mid + 1; } else if(midVal > key) { high = mid - 1; } else { return mid; }*/ //二分插值查找 int mid = low + (key-a[low]) / (a[high]-a[low]) * (high-low); int midVal = a[mid]; if(midVal < key) { low = mid + 1; } else if(midVal > key) { high = mid - 1; } else { return mid; } } return -1; } int main() { int i,val,ret; int a[8] = {-32,12,16,24,36,45,59,98}; for(i = 0;i < 8;i++) { printf("%d ",a[i]); } printf("\n请输入所要查找的元素:"); scanf("%d",&val); ret = binarySearch(a,8,val); if(ret == -1) { printf("查找失败\n"); } else { printf("查找成功\n"); } return 0; }
斐波那契查找
时间复杂度 :O(logn)
大致思路与二分查找相同,首先需要将数据排好序,只是每次划分区间的方法不同。
斐波那契数列:
f(n) = f(n-1)+f(n-2),f(0) = 1,f(2) = 1
{1,1,2,3,5,8,11......}
性质:当n越大,相邻两项的比值越接近0.618(黄金比例)
划分方法:斐波那契查找就是在二分查找的基础上根据斐波那契数列进行分割的。在斐波那契数列找一个等于略大于查找表中元素个数的数F[n],将原查找表扩展为长度为Fn,完成后进行斐波那契分割,即F[n]个元素分割为前半部分F[n-1]个元素,后半部分F[n-2]个元素,找出要查找的元素在那一部分并递归,直到找到。
代码(java):
package search; import java.util.Arrays; public class FibonacciSearch { public static int maxSize = 20; public static void main(String[] args) { int [] arr = {1,8, 10, 89, 1000, 1234}; System.out.println("index=" + fibSearch(arr, 189)); } public static int[] fib() { int[] f = new int[maxSize]; f[0] = 1; f[1] = 1; for (int i = 2; i < maxSize; i++) { f[i] = f[i - 1] + f[i - 2]; } return f; } public static int fibSearch(int[] a, int key) { int low = 0; int high = a.length - 1; int k = 0; int mid = 0; int f[] = fib(); while(high > f[k] - 1) { k++; } int[] temp = Arrays.copyOf(a, f[k]); for(int i = high + 1; i < temp.length; i++) { temp[i] = a[high]; } while (low <= high) { mid = low + f[k - 1] - 1; if(key < temp[mid]) { high = mid - 1; k--; } else if ( key > temp[mid]) { low = mid + 1; k -= 2; } else { if(mid <= high) { return mid; } else { return high; } } } return -1; } }
树表查找
二叉查找树(BinarySearch Tree,也叫二叉搜索树,或称二叉排序树Binary Sort Tree)或者是一棵空树,或者是具有下列性质的二叉树:
1)若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
2)若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
在最坏的情况下,例如将一个从小到大排列的数据依次插入树,那么树将会退化成链表,此时时间复杂度O(n)。
二叉查找树性质:对二叉查找树进行中序遍历,即可得到有序的数列。
数表查找的方法,首先将数据构建成一个二叉查找树,然后将要查找的值与根节点对比,如若比根节点大,则查找根节点的右子树,否则则查找根节点的左子树,递归下去直至找到与查找值相同的值,或查找失败。
代码(C):
#include<stdio.h> #include<malloc.h> #define SIZE 10 typedef struct Node { int data; Node *left; Node *right; }NODE,*PNODE,*PTREE; PTREE search(PTREE pT,int key) { if(pT == NULL||key == pT->data) { return pT; } else if(pT->data > key) { search(pT->left,key); } else { search(pT->right,key); } } void insert(PTREE *pT,int key) { if(*pT == NULL) { *pT = (PNODE)malloc(sizeof(NODE)); (*pT)->data = key; (*pT)->left = NULL; (*pT)->right = NULL; } else { if(key > (*pT)->data) { insert(&(*pT)->right,key); } else if(key < (*pT)->data) { insert(&(*pT)->left,key); } } } void create(PTREE *pT,int datas[SIZE]) { *pT = NULL; for(int i = 0;i < SIZE;i++) { insert(pT,datas[i]); } } void inorder(PTREE pT) { if(pT == NULL) return; else { inorder(pT->left); printf("%d ",pT->data); inorder(pT->right); } } int main() { int datas[SIZE]; printf("请输入%d个数字:\n",SIZE); for(int i = 0;i < SIZE;i++) { scanf("%d",&datas[i]); } PTREE root; create(&root,datas); printf("中序遍历二叉排序数:\n"); inorder(root); printf("\n"); int val; printf("请输入要查找的值:\n"); scanf("%d",&val); printf("检验返回节点对应的值,node->data = \n"); PNODE node = search(root,val); { if(node == NULL) { printf("查无此值!"); } else { printf("%d",node->data); } } return 0; }
代码结果: