常见查找算法(Java代码实现)
一,顺序查找
查找算法中顺序查找算是最简单的了,无论是有序的还是无序的都可以,只需要一个个对比即可,但其实效率很低。我们来看下代码
public static int search(int[] a, int key) {
for (int i = 0, length = a.length; i < length; i++) {
if (a[i] == key)
return i;
}
return -1;
}
还有说上面的代码可以优化,使用一个哨兵,免去了每次都要越界的判断,但通过实际测试运行效率并没有提高,无论测试的数据是多还是少运行的时间都差不多,我们来看下代码。
public static int search(int[] a, int key) {
int index = a.length - 1;
if (key == a[index])
return index;
a[index] = key;
int i = 0;
while (a[i++] != key) ;
return i == index + 1 ? -1 : i - 1;
}
虽然是无序的,但如果我们知道要查找的数据出现在后面的概率比较大的话,我们还可以从后面进行查找。
public static int search(int[] a, int key) {
for (int i = a.length - 1; i >= 0; i--) {
if (a[i] == key)
return i;
}
return -1;
}
public static int search(int[] a, int key) {
if (key == a[0])
return 0;
int index = a.length - 1;
a[0] = key;
while (a[index--] != key) ;
return index == -1 ? index : index == 0 ? 1 : index + 1;
}
二,二分法查找
二分法查找适用于大的数据,但前提条件是数据必须是有序的,他的原理是先和中间的比较,如果等于就直接返回,如果小于就在前半部分继续使用二分法进行查找,如果大于则在后半部分继续使用二分法进行查找……我们来看下代码
public static int binarySearch(int[] array, int value) {
int low = 0;
int high = array.length - 1;
while (low <= high) {
int middle = (low + high) >> 1;
if (value == array[middle]) {
return middle;
}
if (value > array[middle]) {
low = middle + 1;
}
if (value < array[middle]) {
high = middle - 1;
}
}
return -1;
}
但这样写会有个问题,当数据比较大并且要查找的值在后面的时候,求middle可能会出现溢出,所以一般情况下我们要这样写。
public static int binarySearch2(int[] array, int value) {
int low = 0;
int high = array.length - 1;
while (low <= high) {
int middle = low + ((high - low) >> 1);
if (value == array[middle]) {
return middle;
}
if (value > array[middle]) {
low = middle + 1;
}
if (value < array[middle]) {
high = middle - 1;
}
}
return -1;
}
在Android中有个类ArrayMap,看过他的源码的都知道,这里面也有个二分查找,因为他存储的时候key值要进行排序的,如果想了解更多,可以看下Android ArrayMap源码详解,也可以百度搜下。我们来看下他的二分查找代码
// This is Arrays.binarySearch(), but doesn't do any argument validation.
static int binarySearch(int[] array, int size, int value) {
int lo = 0;
int hi = size - 1;
while (lo <= hi) {
final int mid = (lo + hi) >>> 1;
final int midVal = array[mid];
if (midVal < value) {
lo = mid + 1;
} else if (midVal > value) {
hi = mid - 1;
} else {
return mid; // value found
}
}
return ~lo; // value not present
}
我们看到如果没查到,返回的不是-1而是~lo,这里的~lo肯定是个负数,也就是说如果返回的是一个负数就表示没有找到,如果返回的是一个非负数就表示查找到了。如果是负数我们可以对他取反,他表示的就是要查找的数如果放到数组中应该存放到的位置。比如数组[2,5,6,8,9],如果我们查找的是6就会返回6的下标2,如果查找的是4,就返回返回-2,为啥是-2,因为-2取反就是1,也就是如果把4存放到数组中应该存放到数组下标为1的位置。
上面都是非递归的写法,下面来看一下递归的写法
public static int binarySearch4(int[] array, int value) {
int low = 0;
int high = array.length - 1;
return searchmy(array, low, high, value);
}
private static int searchmy(int array[], int low, int high, int value) {
if (low > high)
return -1;
int mid = low + ((high - low) >> 1);
if (value == array[mid])
return mid;
if (value < array[mid])
return searchmy(array, low, mid - 1, value);
return searchmy(array, mid + 1, high, value);
}
三,插值查找
二分法查然效率很高,但我们为什么要和中间的值进行比较,如果我们和数组1/4或者3/4部分的值进行比较可不可以呢,对于一个要查找的数我们不知道他大概在数组的什么位置的时候我们可以使用二分法进行查找。但如果我们知道要查找的值大概在数组的最前面或最后面的时候使用二分法查找显然是不明智的。比如我们查字典的时候如果是a或者b开头的我们一般会在前面找,如果是y或者z开头的我们一般偏向于往后面找,这个时候如果我们使用二分法从中间开始找显然是不合适的。之前二分法查找的时候我们比较的是中间值,mid=low+1/2*(high-low);但插值查找的时候我们比较的不是中间值,是mid=low+(key-a[low])/(a[high]-a[low])*(high-low),我们来看下插值查找的代码。
public static int insertSearch(int[] array, int key) {
return search(array, key, 0, array.length - 1);
}
private static int search(int[] array, int key, int left, int right) {
while (left <= right) {
if (array[right] == array[left]) {
if (array[right] == key)
return right;
else return -1;
}
int middle = left + ((key - array[left]) / (array[right] - array[left])) * (right - left);
if (array[middle] == key) {
return middle;
}
if (key < array[middle]) {
right = middle - 1;
} else {
left = middle + 1;
}
}
return -1;
}
他和二分法查找代码很相似,只不过计算middle的方式不一样。再来看一下递归的版本
public static int insertSearch(int[] array, int key) {
return search2(array, key, 0, array.length - 1);
}
private static int search2(int array[], int key, int left, int right) {
if (left > right)
return -1;
if (array[right] == array[left]) {
if (array[right] == key)
return right;
else return -1;
}
int mid = left + (key - array[left]) / (array[right] - array[left]) * (right - left);
if (array[mid] == key)
return mid;
if (array[mid] > key)
return search2(array, key, left, mid - 1);
return search2(array, key, mid + 1, right);
}
四,斐波那契查找
斐波那契数列我们都知道{0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 },前后的比值会越来越接近0.618,也就是黄金分割点。0.618也被公认为最具有审美意义的比例数字。斐波那契查找原理其实和二分法查找原理差不多,只不过计算中间值mid的方式不同,还有一点就是斐波那契查找的数组长度必须是f(k)-1,这样我们就可以把斐波那契数列进行划分f(k)-1=f(k-1)+f(k-2)-1=(f(k-1)-1)+1+(f(k-2)-1);然后前面部分和后面部分都还可以继续进行划分。但实际上我们要查找的数组长度不可能都是f(k)-1,所以我们要补齐最后的部分,让数组的长度等于f(k)-1,让数组的最后一位数字把后面铺满。比如我们查找的数组长度是21,而f(8)-1=21-1=20;小于21,所以f(8)-1是不行的,我们需要把数组长度变为f(9)-1=34-1=33,后面多余的我们都用原数组最后的那个值填充。我们来看下代码
public static int fibonacciSearch(int[] array, int key) {
if (array == null || array.length == 0)
return -1;
int length = array.length;
int k = 0;
while (length > fibonacci(k) - 1 || fibonacci(k) - 1 < 5) {
k++;
}
int[] fb = makeFbArray(fibonacci(k) - 1);
int[] temp = Arrays.copyOf(array, fb[k] - 1);
for (int i = length; i < temp.length; i++) {
temp[i] = array[length - 1];//用原数组最后的值填充
}
int low = 0;
int hight = length - 1;
while (low <= hight) {
int middle = low + fb[k - 1] - 1;
if (temp[middle] > key) {//要查找的值在前半部分
hight = middle - 1;
k = k - 1;
} else if (temp[middle] < key) {//要查找的值在后半部分
low = middle + 1;
k = k - 2;
} else {
if (middle <= hight) {
return middle;
} else {
return hight;
}
}
}
return -1;
}
private static int fibonacci(int n) {
if (n == 0 || n == 1)
return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
public static int[] makeFbArray(int length) {
int[] array = new int[length];
array[0] = 0;
array[1] = 1;
for (int i = 2; i < length; i++)
array[i] = array[i - 1] + array[i - 2];
return array;
}
其实斐波那契查找效率并没有那么高,我们再来看一下斐波那契查找的递归实现
public static int search(int[] array, int value) {
if (array == null || array.length == 0) return -1;
int length = array.length;
int k = 0;
while (length > fibonacci(k) - 1 || fibonacci(k) - 1 < 5) {
k++;
}
int[] fb = makeFbArray(fibonacci(k) - 1);
int[] temp = Arrays.copyOf(array, fb[k] - 1);
for (int i = length; i < temp.length; i++) {
temp[i] = array[length - 1];//用原数组最后的值填充
}
return fibonacciSearch(temp, fb, value, 0, length - 1, k);
}
public static int fibonacciSearch(int[] array, int[] fb, int value, int low, int hight, int k) {
if (value < array[low] || value > array[hight] || low > hight) return -1;
int middle = low + fb[k - 1] - 1;
if (value < array[middle]) {
return fibonacciSearch(array, fb, value, low, middle - 1, k - 1);
} else if (value > array[middle]) {
return fibonacciSearch(array, fb, value, middle + 1, hight, k - 2);
} else {
if (middle <= hight) {
return middle;
} else {
return hight;
}
}
}
private static int fibonacci(int n) {
if (n == 0 || n == 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
public static int[] makeFbArray(int length) {
int[] array = new int[length];
array[0] = 0;
array[1] = 1;
for (int i = 2; i < length; i++) array[i] = array[i - 1] + array[i - 2];
return array;
}
原文:https://blog.csdn.net/abcdef314159/article/details/85097414
常见查找算法(Java代码实现)的更多相关文章
- 二分查找算法java实现
今天看了一下JDK里面的二分法是实现,觉得有点小问题.二分法的实现有多种今天就给大家分享两种.一种是递归方式的,一种是非递归方式的.先来看看一些基础的东西. 1.算法概念. 二分查找算法也称为折半搜索 ...
- 常见查找算法之php, js,python版
常用算法 >>>1. 顺序查找, 也叫线性查找, 它从第一个记录开始, 挨个进行对比, 是最基本的查找技术 javaScript 版顺序查找算法: // 顺序查找(线性查找) 只做找 ...
- 排序算法Java代码实现(一)—— 选择排序
以下几篇随笔都是记录的我实现八大排序的代码,主要是贴出代码吧,讲解什么的都没有,主要是为了方便我自己复习,哈哈,如果看不明白,也不要说我坑哦! 本片分为两部分代码: 常用方法封装 排序算法里需要频繁使 ...
- 二分查找算法java
二分查找又称折半查找,它是一种效率较高的查找方法. 折半查找的算法思想是将数列按有序化(递增或递减)排列,查找过程中采用跳跃式方式查找,即先以有序数列的中点位置为比较对象,如果要找的元素值小于该中点元 ...
- 常见排序&查询算法Java代码实现
1. 排序算法代码实现 /** * ascending sort * 外层循环边界条件:总共需要冒泡的轮数--每一轮都将最大或最小的数冒泡到最后 * 内层循环边界条件:冒泡数字移动的边界--最终数字需 ...
- Java基础知识强化61:经典查找之 常见查找算法小结
一.顺序查找 条件:无序或有序队列. 原理:按顺序比较每个元素,直到找到关键字为止. 时间复杂度:O(n) 二.二分查找(折半查找) 条件:有序数组 原理:查找过程从数组的中间元素开始,如果中间元素正 ...
- 算法-java代码实现基数排序
基数排序 第11节 基数排序练习题 对于一个int数组,请编写一个基数排序算法,对数组元素排序. 给定一个int数组A及数组的大小n,请返回排序后的数组.保证元素均小于等于2000. 测试样例: [1 ...
- 算法-java代码实现计数排序
计数排序 第10节 计数排序练习题 对于一个int数组,请编写一个计数排序算法,对数组元素排序. 给定一个int数组A及数组的大小n,请返回排序后的数组. 测试样例: [1,2,3,5,2,3], ...
- 算法-java代码实现希尔排序
希尔排序 第8节 希尔排序练习题 对于一个int数组,请编写一个希尔排序算法,对数组元素排序. 给定一个int数组A及数组的大小n,请返回排序后的数组.保证元素小于等于2000. 测试样例: [1,2 ...
随机推荐
- 树·AVL树/平衡二叉树
1.AVL树 带有平衡条件的二叉查找树,所以它必须满足条件: 1 是一棵二叉查找树 2 满足平衡条件 1.1 平衡条件: 1)严格的平衡条件:每个节点都必须有相同高度的左子树和右子树(过于严格而不被使 ...
- 七天开发进度(六)(微信小程序版(一))
1. 今天主要根据网上教程学习了微信小程序的代码结构,和代码编写-Tabbar配置, 学习了app.js的App和Page方法, 认识了view组件,button组件,input组件,但是还没怎么应用 ...
- 网络编程-Python高级语法-property属性
知识点: 一.什么是property属性? 一种用起来像是使用的实例属性一样的特殊属性,可以对应于某个方法,Python的property属性的功能是:property属性内部进行一系列的逻辑计算,最 ...
- web服务-2、四种方法实现并发服务器-多线程,多进程,协程,(单进程-单线程-非堵塞)
知识点:1.使用多线程,多进程,协程完成web并发服务器 2.单进程-单线程-非堵塞也可以实现并发服务器 1.多进程和协程的代码在下面注释掉的部分,我把三种写在一起了 import socket im ...
- xss的一般防护措施(及CreateDefaultBuilder源码)
从上个礼拜开始,公司的安全小组就开始排查公司项目的安全性,首屈一指的就是xss问题,为此我总结了下我的经验. 1.对后台程序的输出数据做html编码处理,前端做简单的替换处理 2.如果业务需要,后台可 ...
- FZU 2285 迷宫寻宝
思路: bfs求最短路径. #include<stdio.h> #include<iostream> #include<queue> #include<cst ...
- Linux中Buffer和Cache的区别
1. Cache:缓存区,是高速缓存,是位于CPU和主内存之间的容量较小但速度很快的存储器,因为CPU的速度远远高于主内存的速度,CPU从内存中读取数据需等待很长的时间,而 Cache保存着CPU刚 ...
- composer 常用操作
1.search 查询 例如:composer search redis 2.show 展示 例如: composer show -all predis/predis 3.require ...
- css样式的六种选择器
css常用的选择器有: 1.标签选择器: 标签选择器,这种选择器影响范围大,建议尽量应用在层级选择器中. 如: *{margin:0;padding:0} /* 影响所有的标签 */ div{co ...
- Java反射通过getter和setter方法实现类的拷贝
private User copyFieldValues(User userData, User user) { Field[] fields = user.getClass().getDeclare ...