zoukankan      html  css  js  c++  java
  • 二分查找解决数组元素定位问题

    二分查找(Binary Search)

    问题定义:

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

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

     1   class Program
     2   {
     3     static void Main(string[] args)
     4     {
     5       int[] sorted_array = new int[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
     6 
     7       int index = LinearSearch(sorted_array, 6);
     8       Console.WriteLine(index);
     9 
    10       Console.ReadKey();
    11     }
    12 
    13     static int LinearSearch(int[] sorted_array, int x)
    14     {
    15       for (int i = 0; i < sorted_array.Length; i++)
    16       {
    17         if (sorted_array[i] == x)
    18         {
    19           return i;
    20         }
    21       }
    22 
    23       return -1;
    24     }
    25   }

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

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

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

    1. 将 x 与数组的中间的值进行比较;
    2. 如果 x 与中间的值相等,则直接返回中间值的位置;
    3. 如果 x 较小,则若存在必出现在中间值的左侧;
    4. 如果 x 较大,则若存在必出现在中间值的右侧;

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

    递归方式(Recursive)实现:

     1     static void Main(string[] args)
     2     {
     3       int[] sorted_array = new int[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
     4 
     5       int index = BinarySearchByRecursive(
     6         sorted_array, 0, sorted_array.Length, 6);
     7       Console.WriteLine(index);
     8 
     9       Console.ReadKey();
    10     }
    11 
    12     static int BinarySearchByRecursive(
    13       int[] sorted_array,
    14       int left,
    15       int right,
    16       int x)
    17     {
    18       if (left <= right)
    19       {
    20         int middle = left + (right - left) / 2;
    21 
    22         if (x == sorted_array[middle])
    23           return middle;
    24 
    25         if (x < sorted_array[middle])
    26           return BinarySearchByRecursive(
    27             sorted_array, left, middle - 1, x);
    28 
    29         return BinarySearchByRecursive(
    30           sorted_array, middle + 1, right, x);
    31       }
    32 
    33       return -1;
    34     }

    迭代方式(Iterative)实现:

     1     static void Main(string[] args)
     2     {
     3       int[] sorted_array = new int[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
     4 
     5       int index = BinarySearchByIterative(
     6         sorted_array, 0, sorted_array.Length, 6);
     7       Console.WriteLine(index);
     8 
     9       Console.ReadKey();
    10     }
    11 
    12     static int BinarySearchByIterative(
    13       int[] sorted_array,
    14       int left,
    15       int right,
    16       int x)
    17     {
    18       while (left <= right)
    19       {
    20         int middle = left + (right - left) / 2;
    21 
    22         if (x == sorted_array[middle])
    23           return middle;
    24 
    25         if (x < sorted_array[middle])
    26           right = middle - 1;
    27         else
    28           left = middle + 1;
    29       }
    30 
    31       return -1;
    32     }

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

     1     static void Main(string[] args)
     2     {
     3       int[] sorted_array = new int[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
     4 
     5       for (int i = 0; i < sorted_array.Length; i++)
     6       {
     7         int index = BinarySearchByIterativeWithLessComparison(
     8           sorted_array, 0, sorted_array.Length, i);
     9         Console.WriteLine(index);
    10       }
    11 
    12       Console.ReadKey();
    13     }
    14 
    15     static int BinarySearchByIterativeWithLessComparison(
    16       int[] sorted_array,
    17       int left,
    18       int right,
    19       int x)
    20     {
    21       int middle;
    22 
    23       while (right - left > 1)
    24       {
    25         middle = left + (right - left) / 2;
    26 
    27         if (sorted_array[middle] <= x)
    28           left = middle;
    29         else
    30           right = middle;
    31       }
    32 
    33       if (sorted_array[left] == x)
    34         return left;
    35       else
    36         return -1;
    37     }

    现在我们通过使用二分查找(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。
     1     static void Main(string[] args)
     2     {
     3       int[] sorted_array = new int[8] { 2, 2, 4, 6, 7, 8, 10, 12 };
     4 
     5       int index = -1;
     6 
     7       for (int i = 0; i < sorted_array.Length; i++)
     8       {
     9         index = Floor(sorted_array, sorted_array[i]);
    10         Console.WriteLine(index);
    11       }
    12 
    13       index = Floor(sorted_array, 1);
    14       Console.WriteLine(index);
    15 
    16       index = Floor(sorted_array, 5);
    17       Console.WriteLine(index);
    18 
    19       index = Floor(sorted_array, 9);
    20       Console.WriteLine(index);
    21 
    22       index = Floor(sorted_array, 13);
    23       Console.WriteLine(index);
    24 
    25       Console.ReadLine();
    26     }
    27 
    28     static int Floor(
    29       int[] sorted_array,
    30       int x)
    31     {
    32       if (x < sorted_array[0])
    33         return -1;
    34 
    35       return BinarySearchFloorPosition(
    36         sorted_array, 0, sorted_array.Length, x);
    37     }
    38 
    39     static int BinarySearchFloorPosition(
    40       int[] sorted_array,
    41       int left,
    42       int right,
    43       int x)
    44     {
    45       int middle;
    46 
    47       while (right - left > 1)
    48       {
    49         middle = left + (right - left) / 2;
    50 
    51         if (sorted_array[middle] <= x)
    52           left = middle;
    53         else
    54           right = middle;
    55       }
    56 
    57       return left;
    58     }

    问题定义:

    给定包含 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。
     1     static void Main(string[] args)
     2     {
     3       int[] sorted_array = new int[8] { 2, 2, 4, 6, 7, 7, 10, 12 };
     4 
     5       int index = -1;
     6 
     7       for (int i = 0; i < sorted_array.Length; i++)
     8       {
     9         index = Ceiling(sorted_array, sorted_array[i]);
    10         Console.WriteLine(index);
    11       }
    12 
    13       index = Ceiling(sorted_array, 1);
    14       Console.WriteLine(index);
    15 
    16       index = Ceiling(sorted_array, 5);
    17       Console.WriteLine(index);
    18 
    19       index = Ceiling(sorted_array, 9);
    20       Console.WriteLine(index);
    21 
    22       index = Ceiling(sorted_array, 13);
    23       Console.WriteLine(index);
    24 
    25       Console.ReadLine();
    26     }
    27 
    28     static int Ceiling(
    29       int[] sorted_array,
    30       int x)
    31     {
    32       if (x > sorted_array[sorted_array.Length - 1])
    33         return -1;
    34       if (x <= sorted_array[0])
    35         return 0;
    36 
    37       return BinarySearchCeilingPosition(
    38         sorted_array, 0, sorted_array.Length, x);
    39     }
    40 
    41     static int BinarySearchCeilingPosition(
    42       int[] sorted_array,
    43       int left,
    44       int right,
    45       int x)
    46     {
    47       int middle;
    48 
    49       while (right - left > 1)
    50       {
    51         middle = left + (right - left) / 2;
    52 
    53         if (sorted_array[middle] < x)
    54           left = middle;
    55         else
    56           right = middle;
    57       }
    58 
    59       return right;
    60     }

    问题定义:

    给定包含 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 次。

     1     static void Main(string[] args)
     2     {
     3       int[] sorted_array = new int[8] { -1, 2, 3, 8, 8, 8, 8, 10 };
     4 
     5       int count = CountOccurrences(sorted_array, 8);
     6       Console.WriteLine(count);
     7 
     8       Console.ReadKey();
     9     }
    10 
    11     static int GetLeftPosition(
    12       int[] sorted_array,
    13       int left,
    14       int right,
    15       int x)
    16     {
    17       int middle;
    18 
    19       while (right - left > 1)
    20       {
    21         middle = left + (right - left) / 2;
    22 
    23         if (sorted_array[middle] >= x)
    24           right = middle;
    25         else
    26           left = middle;
    27       }
    28 
    29       return right;
    30     }
    31 
    32     static int GetRightPosition(
    33       int[] sorted_array,
    34       int left,
    35       int right,
    36       int x)
    37     {
    38       int middle;
    39 
    40       while (right - left > 1)
    41       {
    42         middle = left + (right - left) / 2;
    43 
    44         if (sorted_array[middle] <= x)
    45           left = middle;
    46         else
    47           right = middle;
    48       }
    49 
    50       return left;
    51     }
    52 
    53     static int CountOccurrences(
    54       int[] sorted_array,
    55       int x)
    56     {
    57       int left = GetLeftPosition(sorted_array, -1, sorted_array.Length - 1, x);
    58       int right = GetRightPosition(sorted_array, 0, sorted_array.Length, x);
    59 
    60       return (sorted_array[left] == x && x == sorted_array[right]) ?
    61              (right - left + 1) : 0;
    62     }

    问题定义:

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

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

     1     static void Main(string[] args)
     2     {
     3       int[] A = new int[8] { 6, 7, 8, 9, 10, 2, 3, 4 };
     4 
     5       int minimum = BinarySearchIndexOfMinimumRotatedArray(
     6         A, 0, A.Length - 1);
     7       Console.WriteLine(minimum);
     8 
     9       Console.ReadKey();
    10     }
    11 
    12     static int BinarySearchIndexOfMinimumRotatedArray(
    13       int[] A,
    14       int left,
    15       int right)
    16     {
    17       int middle;
    18 
    19       if (A[left] <= A[right])
    20         return left;
    21 
    22       while (left <= right)
    23       {
    24         if (left == right)
    25           return left;
    26 
    27         middle = left + (right - left) / 2;
    28 
    29         if (A[middle] < A[right])
    30           right = middle;
    31         else
    32           left = middle + 1;
    33       }
    34 
    35       return -1;
    36     }

    问题定义:

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

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

     1     static void Main(string[] args)
     2     {
     3       int[] sorted_array = new int[8] { -10, -1, 0, 3, 10, 11, 30, 50 };
     4 
     5       int index = -1;
     6 
     7       index = BinarySearchFixedPosition(
     8         sorted_array, 0, sorted_array.Length - 1);
     9       Console.WriteLine(index);
    10 
    11       Console.ReadLine();
    12     }
    13 
    14     static int BinarySearchFixedPosition(
    15       int[] array,
    16       int left,
    17       int right)
    18     {
    19       if (right >= left)
    20       {
    21         int middle = (left + right) / 2;
    22 
    23         if (middle == array[middle])
    24           return middle;
    25         else if (middle > array[middle])
    26           return BinarySearchFixedPosition(array, (middle + 1), right);
    27         else
    28           return BinarySearchFixedPosition(array, left, (middle - 1));
    29       }
    30 
    31       return -1;
    32     }

    问题定义:

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

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

     1     static void Main(string[] args)
     2     {
     3       int[] array = new int[5] { 1, 3, 20, 4, 1 };
     4 
     5       int index = -1;
     6 
     7       index = FindPeakPosition(array);
     8       Console.WriteLine(index);
     9 
    10       Console.ReadLine();
    11     }
    12 
    13     static int FindPeakPosition(int[] array)
    14     {
    15       return BinarySearchPeakPosition(
    16         array, 0, array.Length - 1);
    17     }
    18 
    19     static int BinarySearchPeakPosition(
    20       int[] array,
    21       int left,
    22       int right)
    23     {
    24       int middle = left + (right - left) / 2;
    25 
    26       // compare middle element with its neighbors (if neighbors exist)
    27       if ((middle == 0 || array[middle - 1] <= array[middle])
    28         && (middle == array.Length - 1
    29           || array[middle + 1] <= array[middle]))
    30         return middle;
    31 
    32       // if middle element is not peak and its left neighbor is greater than it
    33       // then left half must have a peak element
    34       else if (middle > 0 && array[middle - 1] > array[middle])
    35         return BinarySearchPeakPosition(array, left, (middle - 1));
    36 
    37       // if middle element is not peak and its right neighbor is greater than it
    38       // then right half must have a peak element
    39       else
    40         return BinarySearchPeakPosition(array, (middle + 1), right);
    41     }

    本文《二分查找解决数组元素定位问题》由 Dennis Gao 发表自博客园,未经作者本人同意禁止任何形式的转载,任何自动或人为的爬虫转载行为均为耍流氓。

  • 相关阅读:
    Windows如何安装苹果系统
    python生成.h和.m文件
    python脚本为已创建的ios .h和.m文件添加费代码
    (转)xcode报错:Undefined symbols for architecture x86_64
    (转)iOS内购(iap)总结
    (转)iOS的静态库和动态库(.a、.framework)、Undefined symbols for architecture x86_64、Invalid bitcode signature错误的...
    (转)iOS开发之内购-AppStore
    (转)Cocos 2d-X Lua 游戏添加苹果内购(二) OC和Lua交互代码详解
    关于git服务器的搭建
    关于计算机(概念漫谈)
  • 原文地址:https://www.cnblogs.com/gaochundong/p/binary_search.html
Copyright © 2011-2022 走看看