【前置知识】
二分查找是对有序数组(向量)而言的,所以在使用二分查找前要确保数组已经是顺序的(用到排序算法)。
【二分查找原理】
聚会时通常会玩猜数字的游戏:
- 先由坐庄的人来写一个数字(比如在1~1000之内)
- 让大家轮流猜,并告诉大家正确数字比当前猜的数字大或者小
- 每猜一次范围就会缩小,最后猜中的人倒霉挨罚
二分查找要求只能猜中位数,每次让猜的范围缩小一半,这样很快就能猜中,也即很快能获得查找结果。
【二分查找原理概括】
通过每次选取位于当前范围的中间元素与要查找的元素进行比较,让每次查找范围缩小一半,从而迅速获得查找结果。
【查找操作的作用/目的】
我们进行查找操作时的目的是在一个数组中或者在数组的某一范围中找到是否有这个元素,如果有就获取这个元素所在的位置,如果没有就告诉我们没有。这样就有了查找操作的输入输出。
查找操作的输入:要查找的数组A,要查找的元素a,查找范围[lo,hi) 注意是左闭右开的区间
查找操作的输出:如果查找成功返回元素所在位置(即编号、下标),如果失败返回-1(通常约定,-1表示所进行的操作失败了)
【二分查找代码实现】
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace binsearch
{
class Program
{
static void Main(string[] args)
{
int[] A = new int[30];
Random ra = new Random();
for (int i = 0; i < 30; i++)
{
A[i] = ra.Next(40);
}
Program ps = new Program();
ps.bubblesortB(ref A);
Console.WriteLine("排序结果:");
foreach (int a in A)
{
Console.Write(a + " ");
}
int key = ps.BinSearch(A, 10, 0, A.Length);
Console.WriteLine("\n查找结果:"+key);
Console.ReadKey();
}
public int BinSearch(int[]A,int a, int lo,int hi)//查找范围[lo,hi)
{
while (lo<hi)
{
int middle = lo + ((hi - lo) >> 1);
if (a < A[middle])
{
hi = middle;//新的查找范围[lo,middle)
}
else if (a == A[middle])
{
return middle;//找到编号middle
}
else
{
lo = middle + 1;//新的查找范围[middle+1,hi)
}
}
return -1;
}
//冒泡排序 https://blog.csdn.net/enternalstar/article/details/103400089
public void bubblesortB(ref int[] A)
{
bool isSorted = false;
int m = 0;
for (int i = 0; isSorted == false; i++)
{
isSorted = true;
for (int j = 0; j < A.Length - 1 - i; j++)
{
if (A[j] > A[j + 1])
{
int temp = A[j];
A[j] = A[j + 1];
A[j + 1] = temp;
isSorted = false;
}
}
}
}
}
}
【实现结果】(查找元素10)
【考虑有相同元素的情况】
如果我们要查找的元素在数组中有多个,该怎么办呢?
上文现实的算法是返回任意一个元素位置即可,如果要返回最大的位置或者最小的位置呢?也即我们对输出增加了一条新的要求,如果有多个相同元素,返回元素最大(或者最小)的位置。
最简单的方法是先找到有目标元素的位置,再在这个元素左边找最小的位置,右边找最大的位置。
【元素相同情况——改进代码实现】
public int BinSearch2(int[] A, int a, int lo, int hi)//查找范围[lo,hi)
{
int key=-1;
while (lo < hi)
{
int middle = lo + ((hi - lo) >> 1);
if (a < A[middle])
{
hi = middle;//新的查找范围[lo,middle)
}
else if (a == A[middle])
{
key= middle;//找到编号middle
break;
}
else
{
lo = middle + 1;//新的查找范围[middle+1,hi)
}
}
//获得最小的位置
//while(key-1>-1&&a == A[key - 1])
// key--;
//获得最大的位置
while(key+1<A.Length&&a == A[key+1])
key++;
return key;
}
【改进实现结果】(查找元素10)
【要求不同的输出结果】
有其他输出要求时,如:查找第一个大于等于某个数的位置;查找数组中某个数的最小位置,没有返回-1;查找数组中某个数的出现次数等,在不特意要求效率的情况下,都可以用上述改进思路顺畅实现。
【要求效率的情况】
要求输出:如果有相同的元素,返回元素的最大位置。如果没找到元素,返回小于且距离该元素最近的元素的位置。
【要求效率的情况——代码实现】
public int BinSearch3(int[] A, int a, int lo, int hi)//查找范围[lo,hi)
{
while (lo < hi)//循环不能提前退出,只有在lo=hi时才退出
{
int middle = lo + ((hi - lo) >> 1);
if (a < A[middle])//由三个分支改为两个分支
{
hi = middle;//新的查找范围[lo,middle)
//hi在不断缩小,但A[hi]>a
}
else//a>=A[middle]
{
lo = middle + 1;//新的查找范围[middle+1,hi)
//lo在不断增大,但A[lo-1]<=a,退出时lo=hi,A[lo]=A[hi]>a
}
}
return --lo;
}
【实现结果】
【参考】
[1]邓俊辉.数据结构(c++版)第三版
[2]https://blog.csdn.net/yefengzhichen/article/details/52372407
[3]https://blog.csdn.net/sunmenggmail/article/details/7540970