1.前提:使用二分法需要将数组先排序好
2.优势:在大数据集合中一次可过滤一半
3.使用:
3.1 方式1:普通遍历
/// <summary>
/// 遍历获取二分法查值索引
/// </summary>
/// <param name="students">集合:必须已排序好(此处以正序为例)</param>
/// <param name="key">查找值</param>
/// <returns></returns>
public int GetEFF(int[] students,int key)
{
int left = 0;
int right = students.Length;
while (left <= right) //1.left大于right时需要退出
{
int avg = (left + right) / 2; //2.对半拆
if (key > students[avg]) //3.查找值大于中间值则证明在集合右边,所以需要给left赋中间值加1(从中间值右侧第一个索引开始)
{
left = avg + 1;
}
else if (key < students[avg]) //4.查找值小于中间值则证明在集合左边,所以需要给right赋中间值减1(从中间值左侧第一个索引开始)
{
right = avg - 1;
}
else
{
return avg;
}
}
return -1;
}
3.2 方式2:递归遍历
/// <summary>
/// 递归二分法查值索引
/// </summary>
/// <param name="left">左索引</param>
/// <param name="right">右索引</param>
/// <param name="students">集合:必须已排序好(此处以正序为例)</param>
/// <param name="key">查找值</param>
/// <returns></returns>
public int dgGetEFF(int left,int right, int[] students, int key)
{
if (left > right) //1.left大于right时需要退出
{
return -1;
}
else
{
int avg = (left + right) / 2; //2.对半拆
if (key > students[avg]) //3.查找值大于中间值则证明在集合右边,所以需要给left赋中间值加1(从中间值右侧第一个索引开始)
{
return dgGetEFF(left + 1, right, students, key);
}
else if (key < students[avg]) //4.查找值小于中间值则证明在集合左边,所以需要给right赋中间值减1(从中间值左侧第一个索引开始)
{
return dgGetEFF(left, right - 1, students, key);
}
else
{
return avg;
}
}
}
4.数组存在重复元素
/// <summary>
/// 二分法获取指定元素索引集合
/// </summary>
/// <param name="source">集合:必须已排序好(此处以正序为例)</param>
/// <param name="key">查找值</param>
/// <returns></returns>
public static List<int> GetEFFS(int[] source, int key)
{
//左索引
int left = 0;
//右索引
int right = source.Length;
//下标
int mid = 0;
//数组的中位值的索引值集合
List<int> mids = new List<int>();
while (left <= right) //1.left大于right时需要退出
{
mid = (left + right) / 2; //2.对半拆
if (key > source[mid]) //3.查找值大于中间值则证明在集合右边,所以需要给left赋中间值加1(从中间值右侧第一个索引开始)
{
left = mid + 1;
}
else if (key < source[mid]) //4.查找值小于中间值则证明在集合左边,所以需要给right赋中间值减1(从中间值左侧第一个索引开始)
{
right = mid - 1;
}
else
{
//因为已经排序好,找到所要查询的坐标以后,判断它左右是否与查询值一致,若一致,则添加下标
mids.Add(mid);
//进行左索引递归
Getindexs(source, mid, key, true, mids);
//进行右索引递归
Getindexs(source, mid, key, false, mids);
//递归结束返回所有重复索引值
return mids;
}
}
return mids;
}
/// <summary>
/// 递归获取mid左右元素等于key的下标值
/// </summary>
/// <param name="source">数组</param>
/// <param name="mid">查询值下标</param>
/// <param name="key">查询值</param>
/// <param name="isLeft">往左还是往右递归</param>
/// <param name="ids">相同元素下标集合</param>
static void Getindexs(int[] source,int mid,int key, bool isLeft,List<int> ids)
{
if (isLeft)
{
int left = mid-1;
if (source[left] != key)
{
return;
}
else
{
ids.Add(left);
Getindexs(source,left, key, true,ids);
}
}
else
{
int right = mid+1;
if (source[right] != key)
{
return;
}
else
{
ids.Add(right);
Getindexs(source, right, key, false, ids);
}
}
}
5.二分法获取范围元素
5.1 方式1:找到最小值下标,后向右递归获取所有元素下标直到大于最大值退出
/// <summary>
/// 二分法获取指定元素范围索引集合
/// </summary>
/// <param name="source">集合</param>
/// <param name="min">最小值</param>
/// <param name="max">最大值</param>
/// <returns></returns>
private static List<int> GetWhereIndexs(double[] source, double min, double max)
{
//数组左边界索引值
int left = 0;
//数组的右边界索引值
int right = source.Length;
//数组的中位值的索引值集合
int mid = 0;
//范围元素下标集合
List<int> mids = new List<int>(); ;
//在循环中,根据中位值与目标值对比,更新左(右)边界的值,从而缩小检索范围,直到找到目标值。
while (left <= right)
{
mid = (left + right) / 2; //2.对半拆
if (min > source[mid]) //3.查找值大于中间值则证明在集合右边,所以需要给left赋中间值加1(从中间值右侧第一个索引开始)
{
left = mid + 1;
}
else if (min < source[mid]) //4.查找值小于中间值则证明在集合左边,所以需要给right赋中间值减1(从中间值左侧第一个索引开始)
{
right = mid - 1;
}
else
{
//因为已经排序好,先找最小的min,找到后往右递归直到大于max时退出
mids.Add(mid);
//递归获取从最小值开始到最大值下标集合
GetMaxindexs(source,mid,min,max,mids);
//递归结束返回所有重复索引值
return mids;
}
}
return mids;
}
/// <summary>
/// 递归获取mid往右元素等于key的同时小于等于max的下标值
/// </summary>
/// <param name="source">数组</param>
/// <param name="mid">查询值下标</param>
/// <param name="min">查询值(最小值)</param>
/// <param name="max">最大值</param>
/// <param name="ids">相同元素下标集合</param>
static void GetMaxindexs(double[] source, int mid, double min, double max, List<int> ids)
{
int left = mid + 1;
if (source[left] > max)
{
return;
}
else
{
ids.Add(left);
GetMaxindexs(source, left, min, max, ids);
}
}
5.2 方式2:找到最小值下标min,在找到最大值下标max,去除头尾就是我们想要的范围集合,即满足:
O(min) >= a <= O(max)