Java中的线性和二进制搜索算法
在编程语言的世界里,数据结构和算法是所有工程师必须具备的解决问题的技能。在Java或其他语言中,当遇到未排序和已排序数组的问题时,就需要用到线性和二进制搜索。
在这篇文章中,我将分享在解决面试问题时利用这些方法的方法。
主要收获
最后,读者将了解以下概念。
- 什么是Java中的数据结构和算法。
- 何时以及如何使用线性搜索算法。
- 在排序的Java数组问题中二进制搜索的概念。
- 面试问题的解决方案。
什么是Java中的数据结构和算法
在计算机科学中,数据结构与数据的结构方式、存储方式、流动模式和引用方法有关。
在编程语言中,有许多结构化数据的方法。其中之一是数组或列表;这就是我们讨论的重点。
有许多方法可以引用通过数组存储的数据。
算法是为解决数据结构的问题而制定的方法或逐步规则。
例如,堆栈和队列是描述现实世界应用中数据通过数组或列表流动的算法。
堆栈使用的是后进先出的算法,这被称为LIFO 。
这意味着数据被堆叠在彼此的上面。因此,在调用时,最后一个将是第一个出现的。
然而,Queue使用先入先出的类比,也就是所谓的FIFO 。
把它想象成一个公共队列,第一个进来的人将是第一个被关注的人。
还有其他的数据结构和算法,如链表、哈希图等等。
然而,我们将研究存储在数组中的数据的二进制和线性搜索算法。
线性搜索算法
线性搜索可用于遍历数组问题,如搜索一个特定的元素,寻找一个最大或最小的元素等。
请注意,线性搜索算法主要是在数组,特别是没有经过升序或降序排序的情况下才有用。
使用线性搜索算法需要对整个数组进行循环,N 次。这意味着程序将以未知的次数跑完数组中的所有元素。
这种算法的时间复杂度应是O(N) 。时间复杂度说的是循环跑完数组的确切长度所需的时间。
从根本上说,线性搜索算法的空间复杂度在大多数情况下总是O(1) 。也就是说,常数为1,因为循环会运行一次,没有其他内存空间被消耗。
请注意,对于线性搜索算法来说,在给定的数组中不存在其他的数组循环。
让我们用线性搜索算法找到下面问题的答案。
编写一个程序来寻找未知长度的任何给定数组中的最大整数。
在这个问题中,我们必须做一个可重复使用的函数,它将接受一个数组作为参数。
下面是解决方案。
public class MaxNum {
public static void main(String[] args) {
int[] arr = {33, -1, 0, 89, 9, 62, 8, 2, 97};
int max = maxNum(arr);
System.out.println(max);
}
static int maxNum(int[] arr){
// returns -1 if length of array is zero
if(arr.length == 0){
return -1;
}
// making the first element constant for comparison
int temp = arr[0];
// loop running through the entire array
for (int i = 0; i < arr.length; i++) {
// checking the specific element greater than constant
if(arr[i] > temp) {
temp = arr[i]; // setting the max element to the constant
}
}
// returning the max at the end
return temp;
}
}
在上面的程序中,我们有一个maxNum ,该函数有一个参数,返回任何给定数组的最大元素。
调用上面的数组将向控制台打印97 ,作为提供的数组的最大整数。
Java中的二进制搜索
二进制搜索是一种搜索算法,用于解决整数排序数组的问题。
要利用这种算法,必须知道给定数组的顺序是升序还是降序。
为了利用二进制搜索算法,最好的方法是在每个时间点将数组分成两个空间。
这是通过数组的start,middle, 和end 索引实现的。
当循环运行时,根据问题的直觉,数组的左、中、右三面都得到了比较。
请注意,这个算法通过将给定的数组划分为两个空间来运行,直到它只剩下一个元素进行比较,即当开始、中间和结束索引都指向一个特定的元素。
此外,这种搜索算法的最佳情况被称为O(1) 。这意味着问题在第一次划分时就得到了解决。
每个二进制搜索算法的时间复杂度据说是O(logN) ,而这又对应于它的最坏情况。
这种情况是如何发生的呢?
第一次划分是(N) = N / 2^0,
第二次划分是(N / 2) = N / 2^1,
第三次划分是(N / 4) = N / 2^2,
.
.
.
最后一次划分,N / 2^k.
请注意,在最后一次除法时,将只剩下一个元素。因此,我们可以说N / 2^k = 1 ,其中k 是要进行的未知除数。
从N / 2^k = 1 ,我们有N = 2^k 。
对N = 2^k ,
,我们有k = logN / log2 。
忽略常数log2 ,则k = logN 。
现在让我们看几个问题来详细解释这些概念。
面试问题的解决方案
- 写一个程序,在一个数组中找到目标整数的上限。其中上限数字是大于或等于给定目标的最小数字。用升序数组类
[2, 4, 6, 7, 9, 10, 16, 18, 21],测试你的算法,目标为11。
下面是解决方案。
public class CeilingSolutions {
public static void main(String[] args) {
// given array for testing
int[] arr = {2, 4, 6, 7, 9, 10, 16, 18, 21};
int target = 12;
int ans = ceiling(arr, target);
System.out.println(ans); // prints 16
}
// return smallest number >= target
static int ceiling(int[] ar, int target){
int start = 0;
int end = ar.length - 1;
// while start index is not greater than end index
while(start <= end){
int mid = start + (end - start) / 2;
// if target is not found in ascending array, return -1
if(target > ar[ar.length - 1] ) {
return -1;
}
// if target is greater than element at mid, shift end index to element before mid
if(target < ar[mid]){
end = mid -1;
}
// else if target is less than element at mid, shift start index to element after mid
else if(target > ar[mid]){
start = mid + 1;
}
// else mid element is the answer, return mid
else {
return mid;
}
}
// case when it remains only one element --> worst case where start > end
return ar[start];
}
}
解释。
在第一次划分中,给定的数组将被划分为两个空间,其中中间的元素将是9 ,索引4=(0+8)/2 。
当起始索引小于或等于结束索引时,将进行以下检查。
在第一个条件中,如果没有找到目标,它将返回减一。
现在,给定的目标11 大于中间元素,所以它将把起始索引转移到索引5,即4+1 。那么搜索空间就是从索引5到8,而中间元素将在索引6=(5+8)/2 。
同样,目标11 ,小于索引6的中间元素。因此,终点索引将转移到索引5 =6-1 。
这里的搜索空间仍然是一个元素,即10 。
然而,开始、中间和结束索引都指向同一个元素10 ;在索引5。
这被认为是最坏的情况,因为搜索算法在每次迭代时都通过划分空间来完成所有的空间。
但是目标11 ,大于索引5的元素,这仍然是搜索的唯一元素。
这将促使起始索引向前移动到索引6,通过这样做,已经违反了while循环的条件。
然后,循环将停止运行,返回语句返回起始索引的元素是大于11 的最小的整数。
最后,当函数被调用时,它返回16 作为数组中11 的最高整数。
- 找出目标
8在数组[40, 35, 15, 7, 6, 3, 1, 0, -3]中的下限整数。
本题使用了二进制搜索的概念,因为所给的数组是按降序排列的。
我们将使用与上述问题解决方案相同的策略。唯一的区别是决定开始和结束索引的条件。
请注意,底限整数是指小于或等于目标的最大整数。
下面是问题的解决方案。
public class FloorSolutions {
public static void main(String[] args) {
int[] givenArray = {40, 35, 15, 7, 6, 3, 1, 0, -3};
int target = 8;
int ans = floor(givenArray, target);
System.out.println(ans); // prints 7
}
static int floor(int[] arr, int target) {
int start = 0;
int end = arr.length - 1;
while (start <= end) {
int mid = start + (end - start) / 2;
if (target > arr[mid]) {
end = mid - 1;
} else if (target < arr[mid]) {
start = mid + 1;
} else {
return mid;
}
}
return arr[start];
}
}
结论
线性搜索和二进制搜索是处理未排序和已排序数组的搜索算法。
每当有问题需要用到其中任何一种时,都要参考这个概念。