查找算法的java实现

查找算法:

在java中,常用的查找算法有四种:

1、顺序(线性)查找

2、二分查找(必须掌握)

3、插值查找

4、斐波拉契查找(黄金分割点查找)


① 顺序查找:

思路一:找到第一个值为value的数的下标

public static int seqSearch(int[] arr, int value) {
		// 线性查找是逐一比对,发现有相同值时,返回下标
		for (int i = 0; i < arr.length; i++) {
			if (arr[i] == value)
				return i;
		}
		return -1;
	}

思路二:找到值为value数的所有位置

//查找数列里数value出现的所有位置
	public static ArrayList<Integer> seqSearchArr(int[] arr, int value) {
		ArrayList<Integer> temp = new ArrayList<Integer>();
		int index = 0;
		for (int i = 0; i < arr.length; i++) {
			if (arr[i] == value)
				temp.add(i);
		}
		System.out.println("值为" + value + "的数,出现在该数列的序号有:");
		for(int i = 0 ; i <temp.size() ; i++){
			System.out.printf("%d " , temp.get(i));
		}
		System.out.println();
		return temp;
	}

 

 


② 二分查找:

思路:

1、首先确定该数组的中间下标 : mid = ( left  + right ) / 2 ;

2、然后让需要查找的数findVal 和 arr[mid]进行比较;

a、findVal > arr[mid],说明要查找的数在mid的右边,因此需要递归从右侧查找;

b、findVal < arr[mid],说明要查找的数在mid的左边,因此需要递归从左侧查找;

c、findVal == arr[mid],说明找到返回数值

 

3、结束递归的几种情形:

a、找到了需要查找的数;

b、递归完整个数组,仍然没有找到findVal,也需要结束递归,当left > right时,需要退出(找不到)。

4、使用二分查找的前提:该数组必须是有序的。

package Search;

import java.util.ArrayList;
import java.util.Arrays;

public class BinarySearch {
	// 二分查找:该数组必须是有序的
	public static void main(String[] args) {
		int arr[] = { 1, 8, 10, 89, 1000, 1000, 1000, 1234 };
		ArrayList<Integer> temp = new ArrayList<Integer>();
		ArrayList<Integer> resIndex = binarySearchArr(arr, 0, arr.length - 1, 1000);
		System.out.println("resIndex = " + resIndex);
	}

	/**
	 * 二分查找算法(基本写法,还可以升级)
	 * 
	 * @param arr
	 * @param left
	 *            左边的索引
	 * @param right
	 * @param findVal
	 *            需要查找的数
	 * @return 如果找到,返回下标;反之返回 -1
	 */
	public static int binarySearch(int[] arr, int left, int right, int findVal) {
		// 当left > right 时,说明找不到
		if (left > right)
			return -1;
		// 否则才向下查找
		int mid = (left + right) / 2;
		int midVal = arr[mid];

		if (findVal > midVal) {
			return binarySearch(arr, mid + 1, right, findVal);
		} else if (findVal < midVal) {
			return binarySearch(arr, left, mid - 1, findVal);
		} else {
			return mid;
		}
	}

	/**
	 * 加餐:课后思考题 对于{1,8,10,89,1000,1000,1234}这样的有序数列中,含有多个相同的值 如何将所有的数值都查找到,如1000
	 * 
	 * 思路分析: 1、找到mid值时,不要马上返回 2、向mid
	 * 索引值的左边扫描,将所有满足查找值如1000的元素的下标加入到一个集合ArrayList中 3、向mid
	 * 索引值的右边扫描,将所有满足查找值如1000的元素的下标加入到一个集合ArrayList中
	 */
	public static ArrayList<Integer> binarySearchArr(int[] arr, int left, int right, int findVal) {
		// 当left > right 时,说明找不到
		if (left > right)
			return new ArrayList<Integer>();
		// 否则才向下查找
		int mid = (left + right) / 2;
		int midVal = arr[mid];

		if (findVal > midVal) {
			return binarySearchArr(arr, mid + 1, right, findVal);
		} else if (findVal < midVal) {
			return binarySearchArr(arr, left, mid - 1, findVal);
		} else {
			// 在找到mid的索引值后,不要马上返回,而是将其加入到一个ArrayList集合中
			ArrayList<Integer> resIndexList = new ArrayList<Integer>();
			// 向mid索引的左边扫描,将所有满足1000的元素的下标加入到集合中
			int temp = mid - 1;
			while (true) {
				if (temp < 0 || arr[temp] != findVal) {
					// 如果扫描到了最左端,或是扫描到了不是findVal的值,说明已经找不到了
					break;
				}
				resIndexList.add(temp);// 把mid左边合格的下标放进去
				temp--;
			}
			resIndexList.add(mid);// 然后把mid放入集合中,接着往mid右边扫描

			temp = mid + 1;
			while (true) {
				if (temp > arr.length - 1 || arr[temp] != findVal) {
					break;
				}
				resIndexList.add(temp);
				temp++;
			}
			return resIndexList;
		}
	}
}

③ 插值查找:(前提也是有序数列)

主要是对二分查找的优化,用到了公式:

mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left])

通过对中间值进行了加权改进,在数据大小比较连续,关键字比较均匀的情形下,会有效减少定位次数。但如果关键字之间差距较大,效率可能低于二分查找。

 

public class InsertValueSearch {
	// 插值查找算法
	static int count = 0;//查找次数打印

	public static void main(String[] args) {
		int[] arr = new int[100];
		for (int i = 0; i < 100; i++) {
			arr[i] = i + 1;
		}
		// System.out.println(Arrays.toString(arr));
		int index = insertValueSearch(arr, 0, arr.length - 1, 66);
		System.out.println("index = " + index);

	}

	/**
	 * 插值查找算法(也要求数组必须有序)
	 *
	 */
	public static int insertValueSearch(int[] arr, int left, int right, int findVal) {
		// 这个判断很重要,它不仅可以起到优化作用,而且必须需要(插值查找的公式要求)
		count ++ ;
		System.out.println("查找第" + count + "次");

		if (left > right || findVal < arr[0] || findVal > arr[arr.length - 1]) {
			return -1;
		}

		// 插值查找的公式就在这里
		int mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]);
		int midVal = arr[mid];
		if (findVal > midVal) {
			return insertValueSearch(arr, mid + 1, right, findVal);
		} else if (findVal < midVal) {
			return insertValueSearch(arr, left, mid - 1, findVal);
		} else {
			System.out.println("已经找到位置,标号为: " + mid);
			return mid;
		}
	}

}

 

④ 黄金分割查找法:

(1)黄金分割点:把一条线段分割成两个部分,其中一部分与全长之比 等于 另一部分与该部分之比约为0.618

(2)斐波拉契数列:1、1、2、3、5、8、13、21、34、55......发现斐波拉契数列的两个相邻数的比例,无限接近与0.618。因此斐波拉契数列来实现黄金分割查找,在查找中找到黄金分割点。

(3)mid = low + F(k -1 ) -1 ,之所以后面要 -1,是因为序号从 0 开始

原始斐波拉契递推式为 :

F( k ) - 1 ={ F( k - 1 ) - 1 + F(k - 2) - 1 }+ 1

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, 8));
	}

	//因为后面我们必须使用到斐波拉契数列,所以构造一个方法用来获取斐波拉契数列
	public static int[] fib(){
		//这里用非递归的方式
		int[] f = new int[maxSize];//用于获取斐波拉契数列的前maxSize个元素
		f[0] = 1 ;
		f[1] = 1 ; 
		for(int i = 2 ; i < maxSize ; i ++)
			f[i] = f[i - 1] + f[i - 2 ];
		return f ;
	}
	

	/** 使用非递归方式编写斐波拉契查找算法
	 * 
	 * @param arr
	 * @param key
	 * @return 返回对应下标,没有找到就返回 -1
	 */
	public static int fibSearch(int[] arr , int key){
		int low = 0 ; 
		int high = arr.length - 1 ;	//high前半部分的数是数列存在的数,后半部分的数是补全数
		int k = 0 ; //表示斐波拉契分割数值的下标fib(k)
		int mid = 0 ; 
		int f[] = fib();
		//接下来获取斐波拉契数列的下标:
		
		while(high > f[k] - 1 ){
			//遍历斐波拉契数列,看这个数组的元素个数需要用数列里的多少个数
			k ++ ;
		}
		//因为f[k]的值可能大于 arr 的长度,因此我们需要使用Arrays类,因为构造一个新的数组,并指向arr
	    //不足的部分暂时使用 0 来填充
		int[] temp = Arrays.copyOf(arr, f[k]);
		//实际上需要使用arr数组最后的数来填充temp,使其满足斐波拉契数列的长度
		//即{1,8,10,89,1000,1234,0,0} ->{1,8,10,89,1000,1234,1234,1234}
		for(int i = high + 1 ; i < temp.length ; i++){
			temp[i] = arr[high];
		}
		
		//使用while来循环处理,找到我们的数key
		while(low <= high){
			//在斐波拉契数列的查找公式,mid就是黄金分割点
			mid = low + f[k - 1] - 1 ;
			if(key < temp[mid]){
				//往斐波拉契数列中值的左侧查找
				high = mid - 1 ; 
				k -- ; 
				/**
				 * 之所以这里要k--,是因为
				 * 1、全部元素 = 前面的元素 + 后面的元素,即f[k] = f[k - 1] + f[k - 2]
				 * 因为前面有f[k - 1]个元素,所以如果在前部分查找,那前部分可以继续拆分f[k-1] = f[k-2] + f[k -3]
				 * 
				 * 所以在前半部分查找,使下次循环是 mid = low + f[k-1-1] - 1 
				 */
			}else if(key > temp[mid]){
				low = mid + 1 ;
				k -= 2 ; // 在数组黄金分割点的后半部分,即要在f[k-2]部分查找
				//下次查找时,mid =low + f[k-1-2] -1 
			}else{
				//找到了要查找的元素(key == temp[mid])
				if(mid <= arr.length - 1) return mid;
				else return arr.length - 1 ; //说明查找得到的元素是补全值
			}
		}
		return -1;//如果在整个while循环中没有遇到return语句,说明没找到,返回-1
	}
	
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值