您的位置:首页 > 其它

二分查找解决数组元素定位问题

2014-11-01 21:07 253 查看
二分查找(Binary Search)

给定包含 n 个元素的已排序数组 sorted_array[],求给定元素 x 的位置。

递归方式(Recursive)实现

迭代方式(Iterative)实现

[b]给定包含 n 个元素的已排序数组 sorted_array[],求小于等于给定元素 x 的最近位置(Floor Value)。[/b]

给定包含 n 个元素的已排序数组 sorted_array[],求大于等于给定元素 x 的最近位置(Ceiling Value)。

给定包含 n 个元素的已排序数组 sorted_array[],其中可能包含若干重复的元素,求给定元素 x 重复的次数。

给定包含 n 个元素的已排序数组 sorted_array[],但数组被从中间某未知点翻转为 A[],求 A[] 数组中的最小元素。

给定包含 n 个元素的已排序数组 sorted_array[],查找其中元素位置等于元素值的位置(Fixed Point)。

给定包含 n 个元素的数组 array[],查找某高点的值大于左右两侧的值的位置(Peak Position)。

问题定义:


给定包含 n 个元素的已排序数组 sorted_array[],求给定元素 x 的位置。


最简单直接的办法就是线性查找(Linear Search),从数组的最左端开始,逐个值与 x 进行比较,如果匹配则返回元素位置,如果不匹配则右移一位继续比较,如果比较到末尾仍为找到则返回 -1。由此可知,线性查找的时间复杂度为 O(n)

class Program
{
static void Main(string[] args)
{
int[] sorted_array = new int[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

int index = LinearSearch(sorted_array, 6);
Console.WriteLine(index);

Console.ReadKey();
}

static int LinearSearch(int[] sorted_array, int x)
{
for (int i = 0; i < sorted_array.Length; i++)
{
if (sorted_array[i] == x)
{
return i;
}
}

return -1;
}
}


二分查找(Binary Search)算法使用了分治法(Divide and Conquer)来不断缩小查找范围,并充分利用已知的信息将查找时间复杂度降低到 O(logn)

那已知信息就是:数组是已排序的。

这样通过如下步骤可以减少比较次数:

将 x 与数组的中间的值进行比较;

如果 x 与中间的值相等,则直接返回中间值的位置;

如果 x 较小,则若存在必出现在中间值的左侧;

如果 x 较大,则若存在必出现在中间值的右侧;

可以通过递归(Recursive)迭代(Iterative)两种方式来实现。

递归方式(Recursive)实现:

static void Main(string[] args)
{
int[] sorted_array = new int[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

int index = BinarySearchByRecursive(
sorted_array, 0, sorted_array.Length, 6);
Console.WriteLine(index);

Console.ReadKey();
}

static int BinarySearchByRecursive(
int[] sorted_array,
int left,
int right,
int x)
{
if (left <= right)
{
int middle = left + (right - left) / 2;

if (x == sorted_array[middle])
return middle;

if (x < sorted_array[middle])
return BinarySearchByRecursive(
sorted_array, left, middle - 1, x);

return BinarySearchByRecursive(
sorted_array, middle + 1, right, x);
}

return -1;
}


迭代方式(Iterative)实现:

static void Main(string[] args)
{
int[] sorted_array = new int[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

int index = BinarySearchByIterative(
sorted_array, 0, sorted_array.Length, 6);
Console.WriteLine(index);

Console.ReadKey();
}

static int BinarySearchByIterative(
int[] sorted_array,
int left,
int right,
int x)
{
while (left <= right)
{
int middle = left + (right - left) / 2;

if (x == sorted_array[middle])
return middle;

if (x < sorted_array[middle])
right = middle - 1;
else
left = middle + 1;
}

return -1;
}


从上面的代码可知,在最坏情况下需要 logn + 1 次比较操作。每个迭代周期内进行了 2 次比较,除非成功匹配了元素。现在我们来尝试尽量减少比较操作的数量,在 while 循环内仅进行一次比较。

static void Main(string[] args)
{
int[] sorted_array = new int[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

for (int i = 0; i < sorted_array.Length; i++)
{
int index = BinarySearchByIterativeWithLessComparison(
sorted_array, 0, sorted_array.Length, i);
Console.WriteLine(index);
}

Console.ReadKey();
}

static int BinarySearchByIterativeWithLessComparison(
int[] sorted_array,
int left,
int right,
int x)
{
int middle;

while (right - left > 1)
{
middle = left + (right - left) / 2;

if (sorted_array[middle] <= x)
left = middle;
else
right = middle;
}

if (sorted_array[left] == x)
return left;
else
return -1;
}


现在我们通过使用二分查找(Binary Search)来解决一些常见问题。

问题定义:


给定包含 n 个元素的已排序数组 sorted_array[],求小于等于给定元素 x 的最近位置(Floor Value)。


例如:sorted_array[] = [2, 3, 4, 6, 7, 8, 10, 12],x = 9,则 FloorValue = 8。

这里需要考虑几个边界条件:

如果数组内的所有元素都小于 x,则最后一个元素即为 FloorValue。

如果数组内的所有元素都大于 x,则实际上不存在 FloorValue,属于异常情况,返回 -1。

static void Main(string[] args)
{
int[] sorted_array = new int[8] { 2, 2, 4, 6, 7, 8, 10, 12 };

int index = -1;

for (int i = 0; i < sorted_array.Length; i++)
{
index = Floor(sorted_array, sorted_array[i]);
Console.WriteLine(index);
}

index = Floor(sorted_array, 1);
Console.WriteLine(index);

index = Floor(sorted_array, 5);
Console.WriteLine(index);

index = Floor(sorted_array, 9);
Console.WriteLine(index);

index = Floor(sorted_array, 13);
Console.WriteLine(index);

Console.ReadLine();
}

static int Floor(
int[] sorted_array,
int x)
{
if (x < sorted_array[0])
return -1;

return BinarySearchFloorPosition(
sorted_array, 0, sorted_array.Length, x);
}

static int BinarySearchFloorPosition(
int[] sorted_array,
int left,
int right,
int x)
{
int middle;

while (right - left > 1)
{
middle = left + (right - left) / 2;

if (sorted_array[middle] <= x)
left = middle;
else
right = middle;
}

return left;
}


问题定义:


给定包含 n 个元素的已排序数组 sorted_array[],求大于等于给定元素 x 的最近位置(Ceiling Value)。


例如:sorted_array[] = [2, 3, 4, 6, 7, 8, 10, 12],x = 9,则 CeilingValue = 10。

这里需要考虑几个边界条件:

如果数组内的所有元素都大于 x,则第一个元素即为 CeilingValue。

如果数组内的所有元素都小于 x,则实际上不存在 CeilingValue,属于异常情况,返回 -1。

static void Main(string[] args)
{
int[] sorted_array = new int[8] { 2, 2, 4, 6, 7, 7, 10, 12 };

int index = -1;

for (int i = 0; i < sorted_array.Length; i++)
{
index = Ceiling(sorted_array, sorted_array[i]);
Console.WriteLine(index);
}

index = Ceiling(sorted_array, 1);
Console.WriteLine(index);

index = Ceiling(sorted_array, 5);
Console.WriteLine(index);

index = Ceiling(sorted_array, 9);
Console.WriteLine(index);

index = Ceiling(sorted_array, 13);
Console.WriteLine(index);

Console.ReadLine();
}

static int Ceiling(
int[] sorted_array,
int x)
{
if (x > sorted_array[sorted_array.Length - 1])
return -1;
if (x <= sorted_array[0])
return 0;

return BinarySearchCeilingPosition(
sorted_array, 0, sorted_array.Length, x);
}

static int BinarySearchCeilingPosition(
int[] sorted_array,
int left,
int right,
int x)
{
int middle;

while (right - left > 1)
{
middle = left + (right - left) / 2;

if (sorted_array[middle] < x)
left = middle;
else
right = middle;
}

return right;
}


问题定义:


给定包含 n 个元素的已排序数组 sorted_array[],其中可能包含若干重复的元素,求给定元素 x 重复的次数。


由于是已排序数组,则相同的元素肯定是连续的。这样可以通过查找 x 的最左侧出现和 x 的最右侧出现,则中间的部分都是 x,出现次数 = right - left + 1。

例如:例如:sorted_array[] = [-1, 2, 3, 8, 8, 8, 8, 10],x = 8,则重复的位置为 [8, 8, 8, 8],则重复次数为 6 - 3 + 1 = 4 次。

static void Main(string[] args)
{
int[] sorted_array = new int[8] { -1, 2, 3, 8, 8, 8, 8, 10 };

int count = CountOccurrences(sorted_array, 8);
Console.WriteLine(count);

Console.ReadKey();
}

static int GetLeftPosition(
int[] sorted_array,
int left,
int right,
int x)
{
int middle;

while (right - left > 1)
{
middle = left + (right - left) / 2;

if (sorted_array[middle] >= x)
right = middle;
else
left = middle;
}

return right;
}

static int GetRightPosition(
int[] sorted_array,
int left,
int right,
int x)
{
int middle;

while (right - left > 1)
{
middle = left + (right - left) / 2;

if (sorted_array[middle] <= x)
left = middle;
else
right = middle;
}

return left;
}

static int CountOccurrences(
int[] sorted_array,
int x)
{
int left = GetLeftPosition(sorted_array, -1, sorted_array.Length - 1, x);
int right = GetRightPosition(sorted_array, 0, sorted_array.Length, x);

return (sorted_array[left] == x && x == sorted_array[right]) ?
(right - left + 1) : 0;
}


问题定义:


给定包含 n 个元素的已排序数组 sorted_array[],但数组被从中间某未知点翻转为 A[],求 A[] 数组中的最小元素。


实际上数组中的最小元素 x 将数组分成了左右两侧,左侧的大于 x,右侧的也大于 x。

static void Main(string[] args)
{
int[] A = new int[8] { 6, 7, 8, 9, 10, 2, 3, 4 };

int minimum = BinarySearchIndexOfMinimumRotatedArray(
A, 0, A.Length - 1);
Console.WriteLine(minimum);

Console.ReadKey();
}

static int BinarySearchIndexOfMinimumRotatedArray(
int[] A,
int left,
int right)
{
int middle;

if (A[left] <= A[right])
return left;

while (left <= right)
{
if (left == right)
return left;

middle = left + (right - left) / 2;

if (A[middle] < A[right])
right = middle;
else
left = middle + 1;
}

return -1;
}


问题定义:


给定包含 n 个元素的已排序数组 sorted_array[],查找其中元素位置等于元素值的位置(Fixed Point)。


例如:sorted_array[] = { -10, -1, 0, 3, 10, 11, 30, 50 },则 Fixed Point = 3。

static void Main(string[] args)
{
int[] sorted_array = new int[8] { -10, -1, 0, 3, 10, 11, 30, 50 };

int index = -1;

index = BinarySearchFixedPosition(
sorted_array, 0, sorted_array.Length - 1);
Console.WriteLine(index);

Console.ReadLine();
}

static int BinarySearchFixedPosition(
int[] array,
int left,
int right)
{
if (right >= left)
{
int middle = (left + right) / 2;

if (middle == array[middle])
return middle;
else if (middle > array[middle])
return BinarySearchFixedPosition(array, (middle + 1), right);
else
return BinarySearchFixedPosition(array, left, (middle - 1));
}

return -1;
}


问题定义:


给定包含 n 个元素的数组 array[],查找某高点的值大于左右两侧的值的位置(Peak Position)。


例如:array[] = { 1, 3, 20, 4, 1 },则 Peak Value = 20,Peak Position = 2。

static void Main(string[] args)
{
int[] array = new int[5] { 1, 3, 20, 4, 1 };

int index = -1;

index = FindPeakPosition(array);
Console.WriteLine(index);

Console.ReadLine();
}

static int FindPeakPosition(int[] array)
{
return BinarySearchPeakPosition(
array, 0, array.Length - 1);
}

static int BinarySearchPeakPosition(
int[] array,
int left,
int right)
{
int middle = left + (right - left) / 2;

// compare middle element with its neighbors (if neighbors exist)
if ((middle == 0 || array[middle - 1] <= array[middle])
&& (middle == array.Length - 1
|| array[middle + 1] <= array[middle]))
return middle;

// if middle element is not peak and its left neighbor is greater than it
// then left half must have a peak element
else if (middle > 0 && array[middle - 1] > array[middle])
return BinarySearchPeakPosition(array, left, (middle - 1));

// if middle element is not peak and its right neighbor is greater than it
// then right half must have a peak element
else
return BinarySearchPeakPosition(array, (middle + 1), right);
}


本文《二分查找解决数组元素定位问题》由 Dennis Gao 发表自博客园,未经作者本人同意禁止任何形式的转载,任何自动或人为的爬虫转载行为均为耍流氓。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐