顺序表查找+折半查找(二级)

我们讲了各种数据 结构之后,比如讲了线性表了,讲了栈和队列,讲了树和二叉树,讲了图之后呢,我们最后还有两个专题,

一个叫查找,一个叫排序,我们先看看查找,查找包括哪些内容啊,第一个线性表的查找,数组或者链表的查找,这是一个,还有一个呢,

树的查找,涉及到的一个概念二叉查找树,还有一个是哈希表的查找,哈下表的查找开始我们讲集合的时候其实已经接触过了,

最后我们再讲一下Java里面有哪些查找树和哈希表,这就是我们查找树的内容,线性表的查找我们以数组为例来进行讲解,

线性查找有顺序查找和折半查找,跟底层采用什么存储结构是没有关系的,只不过我们目前是采用数组来实现,大家也可以

采用相关的链表,现在我们先来看这一部分


1. 顺序查找:

比如我们这里面定义了一个数组,这个数组里面存了分数,我要在这里面找一个分数,找100分,在成绩中查找分数是100的

第一个分数,怎么办,有没有顺序,他是不是大小排列的,不是大小排列的,他不是大小排列的,所以这个时候你只能干什么,

只能逐个来比较,最终找到第一个100的,这个效率是高还是低,这个效率是低的,但是你没有办法,你只能这么来,要是学生

呢,就复杂了呗,数组里面放的就不是int了,成了Student了,那也类似吗,但是还有一种情况,提高效率的话,这个数组里面

放的不能是分数了,他现在是不是有序的,有序的我们就能改变一种思路,你没有必要一个一个找了,可以折半查找,我要找

8份,你怎么找8分,我先找最中间的那个,这个6居然比8小,只要这一半,可以再折半,每次砍掉长度的½,这个效率是非常高的,

但是他有前提条件,有什么条件,它是有序的,还有一点呢,那对这个折半查找,他要求采用数组,要是你不采用数组的话,

他底层链表也是不连续的,这样一来效率也不怎么高,这是我们所说的




我们再来看看折半查找,折半查找也叫二分查找,使用这种查找的话需要满足两个条件

1. 第一个要求是顺序存储结构

2. 第二个必须是有序的,比如这个数组从5一直到52,是不是一直都是有序的,这种情况下我们要是再按照顺序查找的话

我们就会发现他的缺点,效率比较低,那么我们怎么来办,折半查找,我们来看一下,我们要在这里找21,该怎么找,就是

这么来找,我们上面写的0到10是什么意思,是索引,在往下边是分数,是两个指针,也就是两个整形的变量,low首先指向

第一个,是0吧,high指向最后,是10,(0+10)/2=5,整除就可以了,5,mid是什么意思,中间,索引就是5,你找的是谁,我们找

的就是21,拿着21跟56比,这一半就全部排出了,肯定不在有一半了,一共有一亿个树,一下子就消灭了5000万,5000万

不用考虑了,哪还有5000万呢,在分一次就是2500万,是不是又没有了,我们讲过2倍2倍的增,现在我们是½的减,这个速度是

非常快的,那告诉我下一步该怎么办了,这个Low变不变,;low还是0,low是不变的,mid是计算出来的,下次改的是high,

起点还是low,终点是high,它的索引是4,怎么成了4了,之前那个幂的是5,5前面这个一个呗,找他就可以了,

high=mid-1=4,一个是0,一个是4,头0尾4,取中间值,(0+4)/2=2,mid等于2,拿着这个21和19比,这个肯定不会的,就排除了

那下一步告诉我指针该怎么变,low和high该怎么变,high要不要变,high不变,范围又是这一块了,high不变还是4,

low应该是几,是mid+1,是不是3,(3+4)/2你可别来个3.5,没有3.5,3,mid指向3,指向3了,找到了,他这个时间复杂度是多少啊

就是我们之前说的log2 n,每次他都会减少二分之一,这个log2 n是什么含义,这个n和log2 n有什么差别,如果n是10亿次,

如果你的级别是n,你就得找10亿次,但是如果你是log2 n,n给他10亿的话,就是2的30次方就是10亿,30次就够了,20次和

10亿次天差地别,这你知道这个效率有多高,明确一下,这个效率是特别高的,这是我们讲的一个内容,我们举了这个例子

好在什么程度,我们的high动了一次,high要变成mid-1,下次再比的话low又要变一下,low要变成mid+1,这里面就是

我们要写的一个算法,你告诉我他什么时候可以结束啊,什么时候开始结束,这个值等于21的时候就结束了,是那样吗

那你给我找个85,找个85的,这里面没有85,一个80,一个88,那怎么办,那就是按照刚才的规则一直在动,动来动去,

就会出现一个情况,什么情况,这个图已经很明白了,low,high,取中间,low等于9,mid等于9,到这儿还没有结束呢,

结果拿着88和85相比,他是不是在这一边呢,结果变成low等于9,mid现在也是9,他在这边的就是high了,high等于mid-1

他的前一个,high变成8了,你看怎么了,low比high还大,说明没有找不到,所以找到没找到,内容都在这儿,

这是我们讲的递归,明确折半查找的思路,第二个知道他的时间复杂度,log2 n级别的,这个效率特别高,下边我们

就来写这个代码了
package com.learn.search;

/**
 * 查找功能:在分数中查询指定分数的索引,在第几个位置
 * 
 * 关于这个顺序的查找非常的简单,
 * 那我们分析一下这个时间复杂度是多少,
 * 他的时间复杂度写一个大欧,
 * T(n) = O(n)
 * 如果这个数组的长度是n个的话,
 * 那平均下来会找一半,去掉系数是O(n)
 * 所以这个数如果几百万的话,查找次数还是挺多的
 * 效率还是挺低的
 * 再写一个S(n),空间复杂度是多少
 * 不需要用到什么变量,只要一个index,一个i
 * 别的他不需要,所以这个不需要占用更多的空间
 * S(n) = O(1)
 * 效率低下
 * 这个讲数组操作的时候都应该已经讲过
 * 非常简单
 * 逐个比较,如果找到,返回数据或者索引
 * 找不到就返回null或者-1
 * 可以是顺序表,也可以是链表
 * 在各个节点查找概率相同的情况下,
 * 默认的查询长度是一半,
 * 所以时间复杂度是O(n)
 * 这个非常的简单
 * @author Leon.Sun
 *
 */
public class TestSearch1 {

	public static void main(String[] args) {
		
		/**
		 * 第一步给定分数数组
		 * 
		 * 给定数组
		 */
		int [] scoreArr = {89,45,78,45,100,98,86,100,65};
		
		/**
		 * 给定要查找的分数
		 * 
		 * 分数就是他了
		 * 
		 * 找个65
		 */
		// int score = 100;
		
		/**
		 * 一共9个数,索引是8
		 */
		// int score = 65;
		
		/**
		 * 找个650,是-1
		 */
		int score = 65;
		
		/**
		 * 完成查找
		 * 
		 * 最终我们会得到一个index
		 * 
		 * 默认的index是-1
		 */
//		int index = -1;
		
		/**
		 * 最难的这一步怎么办
		 * 能怎么办,来一个for循环
		 * 
		 * 但是对于查找我们要提取一个方法
		 */
//		for(int i=0;i<scoreArr.length;i++) {
//			/**
//			 * 逐个判断
//			 */
//			if(scoreArr[i]==score) {
//				/**
//				 * 那怎么办,找到了没有
//				 * 能return 吗,return那main方法就结束了
//				 * 这不能用return,index就等于i
//				 * 找到就是i
//				 */
//				index = i;
//				/**
//				 * 再加个操作
//				 * 找到100后面就不比了,
//				 * 
//				 * break一下也就不循环了
//				 */
//				break;
//			}
//		}
		
		
		/**
		 * 输出结果
		 * 
		 * 输出一下,怎么输出啊
		 * 那就看index等于几了呗
		 * 如果index等于-1,那就输出一下不存在
		 * 
		 * 一共是分4步,我们都一斤完成3步了
		 * 
		 */
//		if(index==-1) {
//			System.out.println("该分数不存在");
//		}else {
//			/**
//			 * 100的索引是4,是4吗
//			 * 
//			 */
//			System.out.println(score + "索引是:" + index);
//		}
		
		/**
		 * 怎么查找,刚才我们要查找的话,你不仅知道你要干什么
		 * 你还要知道怎么来完成,你们知道这个思路吗,
		 * 现在不用了,你只要知道你想干什么就行了
		 * 我想查找,直接调用一个search,
		 * 这个他里面是怎么实现的,我才不管呢
		 * 
		 * 这边传谁,传一个scoreArr,再传一个score
		 * 有没有问题,没有了,
		 */
		int index = search(scoreArr,score);
		
	}
	
	
	/**
	 * 返回值是什么,是int,索引吗
	 * 需要传一个什么内容,
	 * 
	 * 这里面我们应该传一个数组,还要穿一个score
	 * 
	 * 方法提取,这个方法就可以重用
	 * 
	 * 我们这里改一下不叫scoreArr,难道只能改分数吗
	 * 不仅可以找分数,还可以找其他的
	 */
	public static int search(int[] arr,int key) {
		int index = -1;
		
		for(int i=0;i<arr.length;i++) {
			if(arr[i]==key) {
				index = i;
				break;
			}
		}
		
		
		if(index==-1) {
			System.out.println("该分数不存在");
		}else {
			System.out.println(key + "索引是:" + index);
		}
		
		/**
		 * 返回这个索引就行了
		 */
		return index;
	}
	
}
package com.learn.search;

/**
 * 告诉我他又什么前提,
 * 前提是什么,第一个顺序结构,第二个按照关键字有序排列
 * 满足这两个条件
 * @author Leon.Sun
 *
 */
public class TestSearch2 {

	public static void main(String[] args) {
		/**
		 * 第一步干什么啊,给定数组
		 */
		int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
		
		/**
		 * 第二步给定要查找的值
		 * 你要找谁,找key
		 */
		// int key = 2;
		/**
		 * 10的索引是几
		 */
		int key = 10;
		
		/**
		 * 再找一个6
		 */
//		int key = 6;
		
		/**
		 * 进行查找
		 * 这个查找叫折半查找或者叫二分查找
		 */
//		int index = -1;
		
		int index = binarySearch(array,key);
		
		/**
		 * 输出结果
		 * 
		 * 最终要返回他的索引
		 */
		if(index==-1) {
			System.out.println("不存在");
		}else {
			System.out.println(key + "的索引是" + index);
		}
		
	}
	
	/**
	 * 你知道我们这里面该怎么办
	 * 两种方案:
	 * 第一个方案:不使用递归
	 * @param array
	 * @param key
	 * @return
	 */
	public static int binarySearch(int[] array,int key) {
		/**
		 * 首先指定low和high
		 */
		int low = 0;
		int high = array.length - 1;
		
		/**
		 * 折半查找
		 * 
		 * while一直查找
		 * 如果low一直小于high就可以一直进行
		 * 如果Low小于high就一直循环着,
		 * 循环怎么办,
		 * 
		 * 你看他什么时候循环就结束了,
		 * 第一个return了就结束了
		 * 找到了就结束了,还有一个循环完了就结束了
		 * low大于high了及
		 * 这里改成小于等于,
		 * 所以有些细节方面,
		 * 总体思路明白当是我们写代码的时候要注意细节,
		 * 不使用递归的我们先想到这儿,
		 * 这和递归有关系吗,
		 * 折半查找还有递归
		 * 差不多吧,我觉得和递归有关系
		 * 有什么关系,
		 * 首先这个数组我要进行折半查找,
		 * 起始点是0,终止点是10,
		 * 下一次我要在这个数组里面再次进行折半查找
		 * 我们把这部分看成是一个数组呗,
		 * 起始是0,这是4吗,
		 * 下一次我要在这两个里面进行折半查找吗
		 * 这是3这是4,
		 * 递归查找,我们可以把数组的范围缩小
		 * 指定他的起始和终止位置
		 * 一直在进行折半查找
		 * 我们的操作一直在重复
		 * 从这个角度来说我们再来写一个折半查找的程序
		 */
		while(low<=high) {
			/**
			 * 下一步要求得mid
			 * 
			 * 这个mid半天没变过
			 */
			int mid = (low+high)/2;
			/**
			 * 判断它是否等于
			 * 
			 * 我们看一下这一步是不是可以省略
			 * 直接进行折半查找就可以了
			 * 是否等于,等于就直接返回
			 * 这是key等于它
			 */
			if(key==array[mid]) {
				return mid;
			}else if(key<array[mid]) {
				/**
				 * key小于array[mid],
				 * 就是key是21,你找的是21,
				 * 中间是56,是不是给high,low不用给
				 * 
				 */
				high = mid-1;
			}else {
				/**
				 * 这个else意味着一个含义
				 * key>array[mid]
				 */
				low = mid + 1;				
			}			
		}
		
		/**
		 * 实在找不到先返回-1
		 * 
		 * 如果没找到就返回-1
		 */
		return -1;
	}
	
}
package com.learn.search;

/**
 * 这是我们说的折半查找
 * 折半查找给了大家两种思路,
 * 哪两种思路啊,
 * 第一个是非递归的,
 * 第二个是递归的,
 * 那非递归的告诉我时间复杂度是多少,
 * 然后空间复杂度
 * S(n) = O(1);
 * T(n) = O(log2 n)
 * 不管你循环了多少次,
 * 并没有分配更多的变量,
 * 
 * 当我们采用的递归的话,
 * 递归怎么办,
 * 他是不是还是每次减少一半,
 * 这实现上还是差不多的,
 * T(n) = O(log2n);
 * 花的时间是差不多的,
 * 因为都是每次找一半,
 * 但是空间复杂度S(n)呢,
 * 每一次的空间复杂度是1,
 * 但是他这个方法调用了logn次呗
 * 我只调用一次这个方法是有限的,
 * 可是调用了log2 n次了,
 * 所以我们可以让1*log2n
 * S(n) = O(1*log2n);
 * 所以最后就是log2n
 * S(n) = O(log2 n);
 * 重复的调用,重复的分配空间,
 * 这样他花的空间就比较多的,
 * 从这个角度来说我们应该有哪一个,
 * 使用非递归的,
 * 那我们怎么还来讲一下递归的,
 * 在这个例子来说代码差不多
 * 就找这种分析问题的思路,
 * 扩展一下这种思路,
 * 也是可以这么来解决的,
 * 到这里我们就把线性表查找里的顺序查找和折半查找给大家讲了
 * 顺序查找非常的简单,折半查找是我们的一个重点,
 * 首先掌握算法,
 * 然后能写出代码,
 * 最后一步一定要知道折半查找的时间复杂度和空间复杂度
 * 尤其他的时间复杂度,logn这个性能非常高的,
 * 
 * @author Leon.Sun
 *
 */
public class TestSearch3 {

	public static void main(String[] args) {
		int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
		
		// int key = 2;
//		int key = 10;
		
		int key = 60;
		
//		int index = -1;
		
		int index = binarySearch(array,key);
		
		if(index==-1) {
			System.out.println("不存在");
		}else {
			System.out.println(key + "的索引是" + index);
		}
		
	}
	
	/**
	 * 我们要使用递归
	 * 大家想一下,刚才我们使用递归的话,是什么不一样
	 * 一样的是什么,不一样的是什么,
	 * 这是一次递归,这又是一次递归,
	 * 这还是一次递归,相同的是什么,
	 * 你永远找到的是21,你找的数据永远在这儿,
	 * 不同的是什么意思,
	 * low和high不一样,范围不一样了
	 * 初始状态从0到10
	 * 那么基于这一点的话我们开始写程序了
	 * 我们要进行这样的一个查找,
	 * 我们要在这里面找这个值,
	 * @param array
	 * @param key
	 * @return
	 */
	public static int binarySearch(int[] array,int key) {
		int low = 0;
		int high = array.length - 1;
		/**
		 * 第一次找的话整个数组,key是21,low是0,high是最后一个元素
		 * 
		 */
		return binarySearch(array, key, low, high);
	}
	
	/**
	 * 我们增加两个参数,low和high
	 * 因为每次调用array和key是相同的,
	 * 但是low和high两个是一样的,这两个是不一样的
	 * 
	 * 怎么来理解,一下子传入4个参数,
	 * 
	 * 每次递归array和key都是一样的,
	 * 不一样的是low和high
	 * 两个都相当于指针,
	 * 
	 * @param array
	 * @param key
	 * @param low
	 * @param high
	 * @return
	 */
	public static int binarySearch(int[] array,int key,int low,int high) {
		/**
		 * 请问这里面该怎么写,递归
		 * 首先要写递归的结束条件,
		 * 如果low大于high了,
		 * 你传进来的low已经大于high了,
		 * 那就没找到呗,
		 * 这是个结束条件
		 * 
		 * low大于high,
		 */
		if(low>high) {
			return -1;
		}
		
		/**
		 * 这是中间
		 * 
		 * 获取mid
		 */
		int mid = (low+high)/2;
		
		/**
		 * 找到就结束了
		 */
		if(key==array[mid]) {
			return mid;
		}else if(key<array[mid]) {
			/**
			 * key找的是它,
			 * 下面我要重复刚才的操作呗
			 * 数据在array里面放着呢,
			 * 查找的还是key,你现在找的是21,
			 * 现在找到56这里了,我们要在前面的位置找,
			 * low还是0,high等于mid-1,
			 * low没有变,high=mid-1
			 * 
			 * 我们都写上一个return
			 * 
			 * 小于就递归查找
			 */
			return binarySearch(array, key, low, mid-1);
		}else {
			/**
			 * 再来个else,就相当于key>array[mid]
			 * 他在右边找,
			 * 这是哪个内容,21大于19了,
			 * high没变,low变了,
			 * 还是要进行折半查找,
			 * 那这个时候要怎么办,
			 * 比如high没有变,
			 * low变成mid+1,
			 * 别忘了写了return
			 * 
			 * 大于也递归查找
			 */
			return binarySearch(array, key, mid+1, high);
		}
		
	}
	
}

猜你喜欢

转载自blog.csdn.net/Leon_Jinhai_Sun/article/details/89713670
今日推荐