本文总结Java常用查找算法顺序查找算法,线性查找算法,二分查找算法,分块查找算法和哈希查找算法。
1、顺序查找算法
/***
* 顺序查找算法按数组的顺序从前往后一直比较,直到找到目标值返回。
* 参考链接:http://developer.51cto.com/art/201406/443115.htm
* http://blog.csdn.net/qq284565035/article/details/41678805
* */
2、线性查找算法
/***
* 线性查找算法解决的问题十分经典,即从某n个元素的序列中选出第k大(第k小)的元素,通过巧妙的分析,
* BFPRT可以保证在最坏情况下仍为线性时间复杂度。该算法的思想与快速排序思想相似,
* 当然,为使得算法在最坏情况下,依然能达到o(n)的时间复杂度,五位算法作者做了精妙的处理。
* 算法步骤:
* 1. 将n个元素每5个一组,分成n/5(上界)组。
* 2. 取出每一组的中位数,任意排序方法,比如插入排序。
* 3. 递归的调用selection算法查找上一步中所有中位数的中位数,设为x,偶数个中位数的情况下设定为选取中间小的一个。
* 4. 用x来分割数组,设小于等于x的个数为k,大于x的个数即为n-k。
* 5. 若i==k,返回x;若i<k,在小于x的元素中递归查找第i小的元素;若i>k,在大于x的元素中递归查找第i-k小的元素。
* 终止条件:n=1时,返回的即是i小元素。
* 参考链接:http://developer.51cto.com/art/201406/443115.htm
* http://blog.csdn.net/td_lsw0829/article/details/37934739
* */
public class LineSearch {
/**
* @param args
* @author TD_LSW
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
// 输入数据数组
int[] a = { 12, 76, 29, 22, 15, 62, 29, 58, 35, 67, 58, 33, 28, 89, 90,28, 64, 48, 20, 77 };
LineSearch line = new LineSearch();
line.lineSearch(a, 22);
}
/**
* 线性查找
* @param a
* @param e
*/
private void lineSearch(int[] a, int e) {
// TODO Auto-generated method stub
// 数据索引计数变量
int count = 1;
for (int i = 0; i < a.length; i++) {
// 输出数据
System.out.println("当前数据是:" + a[i]);
if (e == a[i]) {
System.out.println("共比较次数为:" + count + " 在数组中的索引是:" + i);
break;
}
if (i == a.length - 1 && e != a[i]) {
System.out.println("数据不存在!");
System.out.println("共比较次数为:" + count);
}
count++;
}
}
}
3、二分查找算法
/***
* 二分查找又称折半查找,它是一种效率较高的查找方法。
* 折半查找的算法思想是将数列按有序化(递增或递减)排列,查找过程中采用跳跃式方式查找,即先以有序数列的中点位置为比较对象,
* 如果要找的元素值小 于该中点元素,则将待查序列缩小为左半部分,否则为右半部分。通过一次比较,将查找区间缩小一半。
* 折半查找是一种高效的查找方法。它可以明显减少比较次数,提高查找效率。但是,折半查找的先决条件是查找表中的数据元素必须有序。
* 折半查找法的优点是比较次数少,查找速度快,平均性能好;其缺点是要求待查表为有序表,且插入删除困难。
* 因此,折半查找方法适用于不经常变动而查找频繁的有序列表。
* 二分算法步骤描述
① 首先确定整个查找区间的中间位置 mid = ( left + right )/ 2
② 用待查关键字值与中间位置的关键字值进行比较;
若相等,则查找成功
若大于,则在后(右)半个区域继续进行折半查找
若小于,则在前(左)半个区域继续进行折半查找
③ 对确定的缩小区域再按折半公式,重复上述步骤。
最后,得到结果:要么查找成功, 要么查找失败。折半查找的存储结构采用一维数组存放。 折半查找算法举例
对给定数列(有序){ 3,5,11,17,21,23,28,30,32,50,64,78,81,95,101},按折半查找算法,查找关键字值为81的数据元素。
*
* 参考链接:http://developer.51cto.com/art/201406/443115.htm
* http://www.cnblogs.com/wxd0108/p/5465926.html
* */
public class BinarySearch {
/**
* 二分查找算法
*
* @param srcArray 有序数组
* @param key 查找元素
* @return key的数组下标,没找到返回-1
*/
public static void main(String[] args) {
int srcArray[] = {3,5,11,17,21,23,28,30,32,50,64,78,81,95,101};
System.out.println(binSearch(srcArray, 0, srcArray.length - 1, 81));
}
// 二分查找递归实现
public static int binSearch(int srcArray[], int start, int end, int key) {
int mid = (end - start) / 2 + start;
if (srcArray[mid] == key) {
return mid;
}
if (start >= end) {
return -1;
} else if (key > srcArray[mid]) {
return binSearch(srcArray, mid + 1, end, key);
} else if (key < srcArray[mid]) {
return binSearch(srcArray, start, mid - 1, key);
}
return -1;
}
// 二分查找普通循环实现
public static int binSearch(int srcArray[], int key) {
int mid = srcArray.length / 2;
if (key == srcArray[mid]) {
return mid;
}
int start = 0;
int end = srcArray.length - 1;
while (start <= end) {
mid = (end - start) / 2 + start;
if (key < srcArray[mid]) {
end = mid - 1;
} else if (key > srcArray[mid]) {
start = mid + 1;
} else {
return mid;
}
}
return -1;
}
}
4、分块查找算法
/***
* 分块查找
* a. 首先将查找表分成若干块,在每一块中数据元素的存放是任意的,但块与块之间必须是有序的(假设这种排序是按关键字值递增的,
* 也就是说在第一块中任意一个数据元素的关键字都小于第二块中所有数据元素的关键字,第二块中任意一个数据元素的关键字都小于第三块中所有数据元素的关键字,依次类推);
* b. 建立一个索引表,把每块中最大的关键字值按块的顺序存放在一个辅助数组中,这个索引表也按升序排列;
* c. 查找时先用给定的关键字值在索引表中查找,确定满足条件的数据元素存放在哪个块中,查找方法既可以是折半方法,也可以是顺序查找。
* d. 再到相应的块中顺序查找,便可以得到查找的结果。
* 参考链接:http://developer.51cto.com/art/201406/443115.htm
* http://xiaojun-it.iteye.com/blog/2291852
* */
public class blockSearch {
/**
* 分块查找
*
* @param index
* 索引表,其中放的是各块的最大值
* @param st
* 顺序表,
* @param key
* 要查找的值
* @param m
* 顺序表中各块的长度相等,为m
* @return
*/
public static int blockSearch(int[] index, int[] st, int key, int m) {
// 在序列st数组中,用分块查找方法查找关键字为key的记录
// 1.在index[ ] 中折半查找,确定要查找的key属于哪个块中
int i = binSearch(index, key);
if (i >= 0) {
int j = i > 0 ? i * m : i;
int len = (i + 1) * m;
// 在确定的块中用顺序查找方法查找key
for (int k = j; k < len; k++) {
if (key == st[k]) {
System.out.println("查询成功");
return k;
}
}
}
System.out.println("查找失败");
return -1;
}
// 二分查找普通循环实现
public static int binSearch(int srcArray[], int key) {
int mid = srcArray.length / 2;
if (key == srcArray[mid]) {
return mid;
}
int start = 0;
int end = srcArray.length - 1;
while (start <= end) {
mid = (end - start) / 2 + start;
if (key < srcArray[mid]) {
end = mid - 1;
} else if (key > srcArray[mid]) {
start = mid + 1;
} else {
return mid;
}
}
return -1;
}
}
5、哈希查找算法
/***
* 哈希表查找是通过对记录的关键字值进行运算,直接求出结点的地址,是关键字到地址的直接转换方法,
* 不用反复比较。假设f包含n个结点,Ri为其中某个结点(1≤i≤n),keyi是其关键字值,在keyi与Ri的地址之间建立某种函数关系,
* 可以通过这个函数把关键字值转换成相应结点的地址,有:addr(Ri)=H(keyi),addr(Ri)为哈希函数。
* 解决冲突的方法有以下两种:
* (1)开放地址法
如果两个数据元素的哈希值相同,则在哈希表中为后插入的数据元素另外选择一个表项。当程序查找哈希表时,
如果没有在第一个对应的哈希表项中找到符合查找要求的数据元素,程序就会继续往后查找,直到找到一个符合查找要求的数据元素,或者遇到一个空的表项。
(2)链地址法
将哈希值相同的数据元素存放在一个链表中,在查找哈希表的过程中,当查找到这个链表时,必须采用线性查找方法。
* 参考链接:http://developer.51cto.com/art/201406/443115.htm
* http://xiaojun-it.iteye.com/blog/2291852
* */
public class searchHash {
/****
* Hash查找
*
* @param hash
* @param hashLength
* @param key
* @return
*/
public static int searchHash(int[] hash, int hashLength, int key) {
// 哈希函数
int hashAddress = key % hashLength;
// 指定hashAdrress对应值存在但不是关键值,则用开放寻址法解决
while (hash[hashAddress] != 0 && hash[hashAddress] != key) {
hashAddress = (++hashAddress) % hashLength;
}
// 查找到了开放单元,表示查找失败
if (hash[hashAddress] == 0)
return -1;
return hashAddress;
}
/***
* 数据插入Hash表
*
* @param hash
* 哈希表
* @param hashLength
* @param data
*/
public static void insertHash(int[] hash, int hashLength, int data) {
// 哈希函数
int hashAddress = data % hashLength;
// 如果key存在,则说明已经被别人占用,此时必须解决冲突
while (hash[hashAddress] != 0) {
// 用开放寻址法找到
hashAddress = (++hashAddress) % hashLength;
}
// 将data存入字典中
hash[hashAddress] = data;
}
}
关注微信公众号和今日头条,精彩文章持续更新中。。。。。