0
点赞
收藏
分享

微信扫一扫

Java数据结构与算法 线性查找和二分查找

八怪不姓丑 2022-04-20 阅读 55

查找

查找(searching)是在一组数据项中找到指定的目标元素(target element)或者判定组内不存在目标的过程。要查找的数据项组有事成为查找池(search pool)。

在此讨论两种常用的查找方法:线性查找和二分查找。我们在此只限定在数组内进行查找,后续会讨论其他不同结构中数据的查找技术。

我们的目标是尽可能高效地完成查找。利用算法分析的术语来说,在查找目标的过程中进行最少量的比较。一般而言,查找池中的数据项越多,找到目标需要的比较次数也就越多。所以对于查找问题来说,问题的大小由查找池中数据项的个数来决定。

要想找到目标,必须要对对象进行比较。我们实现的这些查找算法都作用于Comparable对象的数组。所以涉及的元素必须实现Comparable接口,而且互相之间必须是可比较的。Comparable接口中包含方法compareTo,如果对象分别小于、等于或大于要被比较的对象时,则方法会分别返回小于0、等于0或大于0的一个整数。所以,实现Comparable接口的任何一个类要定义类中任意两个对象之间的相互次序。

下面举一个例子,查找Contact对象数组。每个Contact对象表示一个人,他有姓(last name)、名(first name)以及电话号码(phone number)等属性。

public class Contact implements Comparable
{
	private String firstName, lastName, phone;

	public Contact (String first, String  last, String telephone)
	{
		firstName = first;
		lastName = last;
		phone = telephone;
	}

	public String toString()
	{
		return lastName + "," + firstName + "," + phone;
	}

	public int compareTo(Object other)
	{
		int result;
		if (lastName.equals(((Contact)other).lastName)
			result = firstName.compareTo(((Contact)other).firstName);
		else
			result = lastName.compareTo(((Contact)other).lastName);

		return result;
	}
}

Contact类实现了Comparable接口,所以实现了compareTo方法。本例中,按contact(联系人)的姓进行排序;如果两个人的姓相同,再由他们的名字决定先后次序。

还有一种使用泛型(generic)实现的Comparable接口版本,泛型技术将在之后的博文里讨论。这里只使用基本的非泛型接口。注意,编译这个类时可能会出现一些警告信息,但不会妨碍所创建程序的运行。

下例创建一个代表足球选手的Contact对象数组,然后查找一名具体的选手。它创建要查找的目标对象,对象中包含的是与Contact对象进行比较时要用到的值,本例中这个值即是人的姓和名。当查找算法在表中找到目标时,它返回指向表中这个Contact对象的引用。如果返回的引用是null,则表明表中没有目标。

public class SearchPlayerList
{
	public static void main (String[] args)
	{
		Contact[] players = new Contact[7];
		players[0] = new Contact ("Eden","Hazard","456-852-9517");
		players[1] = new Contact ("Didier","Drogba","120-634-7986");
		players[2] = new Contact ("Timo","Werner","326-950-7643");
		players[3] = new Contact ("Thiago","Silva","946-573-4063");
		players[4] = new Contact ("Mason","Mount","993-643-7564");
		players[5] = new Contact ("Frank","Lampard","886-477-1015");
		players[6] = new Contact ("Kai","Havertz","297-431-1579");

		Contact target = new Contact ("Mason","Mount"," ");

		Contact found = (Contact)Searching.linearSearch(players,target);

		if (found == null)
			System.out.println("Player Not Found.");
		else
			System.out.println("Found:" + found);
	}
}

线性查找

如果查找池是某种类型的一个表,比如一个数组,简单的查找方法是从表头开始,依次将每个值与目标元素进行比较。最后,要么找到目标,要么到达表尾而目标不存在于组中。这种方法就称为线性查找(linear search),因为它从一端开始以线性的方式扫描查找池。

下面举例一个search类,包含两个静态方法,一个用线性查找来查找一个项,另一个用二分查找法来查找一个项。二分查找法将在下一节中讨论。

public class Searching
{
	public static Comparable linearSearch(Comparable[] data, Comparable target)
	{
		Comparable result = null;
		int index = 0;
		while (result== null && index < data.length)
		{
			if (data[index].compareTo(target) == 0)
				result = data[index];
			index++;
		}
		return result;
	}

	public static Comparable binarySearch (Comparable[] data, Comparable target)
	{
		Comparable result = null;
		int first = 0, last = data.length-1, mid;
		while (result ==null && first <= last)
		{
			mid = (first + last)/2;
			if (data[mid].compareTo(target) == 0)
				result = data[mid];
			else
				if (data[mid].compareTo(target) > 0)
					last = mid - 1;
				else
					first = mid + 1;
		}
		return result;
	}
}

linearSearch方法的参数是要在其中进行查找的元素数组及要查找的目标项。方法返回指向目标的引用,如果表中找不到目标项,则引用是null。

值得注意的一点是,linearSearch的参数和返回类型都是Comparable对象,而不是Contact对象,即实现的linearSearch方法作用于Comparable对象,而不是作用于任何具体的类。这种情形是多态的一个例子。任何实现Comparable接口的类的对象都可以赋予指向Comparable对象的引用。多以linearSearch方法可以用来查找任意元素为Comparable对象的数组,这是显示多态性能的一个示例。

linearSearch方法中的while循环查找数组元素,当找到目标或到达数组末尾时从循环中退出。如果找到目标,则数组中命中元素的值赋给result,循环中断,返回引用。如果查找了整个数组但没有找到元素,则result的值仍为null,返回这个值表示表中没有目标元素。

二分查找

线性查找很容易理解,不过它的效率不高,而二分查找改进了查找过程的效率,但只能用于查找池是有序的情况。二分查找(binary search)算法借助于查找池中数据的有序性,在每次比较时去除查找池中的大部分元素。

二分查找并不是从表的一端开始查找,而是从中间开始。如果表中间位置的元素并不是要找的目标,则继续查找。因为表是有序的,可知如果目标在表中,则根据目标与中间元素的大小关系,它一定在数组的一边或另一边。因为表是有序的,所以每次与所选值进行比较后可以减少查找池中的一半元素。余下的一半元素成为包含目标元素的可行候选者(viable candidate)。以同样的方式继续查找,检测可行候选者的中间元素,又排除掉一半元素。每次比较都将可行候选者的范围缩小一半,直到最后找到目标元素,或者是没有可行候选者,这意味着查找池中没有目标元素。
在这里插入图片描述

上节程序中Searching类的静态方法binarySearch实现了二分查找。和linearSearch方法一样,它接收的参数是要在其中进行查找的Comparable对象数组和要查找的目标值。在算法的任意一趟查找中,可行候选者区间的边界由整型变量first和last表示。初始时,设置它们包含整个数组。与线性查找类似,binarySearch方法中的while循环要一直执行,直到找到目标或是没有候选者时为止。如果找到目标,则给result赋值,并退出循环。否则,如果first的值越过last的值,则所有的候选者都被排除,循环结束返回null。在循环的每次迭代中,取first和last下标的中间值(使用整除),以计算可行候选者的中间点。如果目标找到,则返回值result置为数组中命中的元素。否则根据要保留的是左侧还是右侧可行候选者区间,利用中间点的左、右邻值来调整last或first的值。

一般情况下,二分查找比线性查找更高效,因为每次比较时它排除了很多候选者。但二分查找要求表只能是有序的,而线性查找没有这个要求。

举报

相关推荐

0 条评论