📜  二分搜索直觉和谓词函数

📅  最后修改于: 2022-05-13 01:56:04.840000             🧑  作者: Mango

二分搜索直觉和谓词函数

二进制搜索算法用于许多编码问题,通常乍一看并不是很明显。但是,肯定有直觉和特定条件可能暗示使用二分搜索。在本文中,我们尝试开发一种二分搜索的直觉。

二分搜索简介

它是一种在排序序列中搜索特定元素、下限或上限的有效算法。如果这是您第一次遇到二进制搜索算法,那么您可能需要在继续之前参考这篇文章:二进制搜索算法背后的基本逻辑。

什么是单调函数?

您可能已经知道这是一个数学概念。

  • 它们是遵循特定顺序的函数。
  • 用数学术语来说,函数的斜率总是非负或非正的。
  • 单调性是使用二分搜索的基本要求。回想一下,二进制搜索只能应用于排序数组[单调函数]。

例如一个不递增的数字数组:

单调函数

什么是谓词函数?

它们是接受输入并返回单个输出 TRUE 或 FALSE 的函数。

例如,一个接受整数的函数,如果整数大于 0 则返回 true,否则返回 false;

伪代码:

单调谓词函数:

  • 单调谓词函数如下所示:

单调谓词函数

  • 另一方面,下面的这个函数是一个谓词函数,但不是单调的:

不单调

  • 可以观察到,在单调谓词函数中,值可以从真变为假,反之亦然,最多一次。可以将其可视化为 0 和 1 的数组,并检查该数组是否按非递增/非递减顺序排序。

二分搜索和单调谓词函数有什么关系?

任务是在给定数组中找到第一个真值:

单调谓词函数

朴素方法:朴素方法从头到尾遍历数组,第一次看到 TRUE 就中断,并返回索引。
时间复杂度: O(n)

高效的方法:但是,更好的方法是利用数组形成单调函数的事实,这意味着可以应用二进制搜索,这比线性搜索要好。
时间复杂度: O(logn)

二分搜索的直觉:

  1. 出现单调函数,例如。一个排序数组。
  2. 需要找出满足/不满足某个条件的最小/最大值。
  3. 可以形成单调谓词函数,并且需要一个过渡点。

示例:巧克力问题

问题陈述:给定一个由N个巧克力棒高度组成的数组arr[] ,任务是找到对所有巧克力进行水平切割的最大高度,使得巧克力的剩余总量至少为K。

例子:

关键观察:与问题相关的主要观察是

  1. 最终输出必须大于等于 0,因为这是可以进行水平切割的最小高度。
  2. 最终输出还必须小于或等于所有巧克力的最大高度,因为高于该高度的所有切割都会产生相同的结果。
  3. 现在有一个下限和上限。
  4. 现在需要找到巧克力切出大于或等于 K 的第一个点。
  5. 我们显然可以对此使用线性搜索,但这将是线性时间复杂度 [O(N)]。
  6. 但是,由于可以形成单调谓词函数,我们不妨使用二分搜索,它在对数时间复杂度 [O(logN)] 下要好得多。

方法:这可以使用二分搜索来解决,因为存在单调谓词函数。按照步骤:

  1. 我们已经从以下事实中得到了二分搜索的提示:我们想要“最大高度 [极值],其中巧克力剩余量的所需总和至少为K [要满足的条件]”。
  2. 我们可以创建一个谓词函数,它获取水平切割的高度,如果巧克力切割大于或等于 7(第一个示例中的 k = 7),则返回 true。

请参阅下面给出的插图以更好地理解

插图:

下面是上述方法的实现。

C++
// C++ program to implement the approach
#include 
using namespace std;
 
// Predicate function
int isValid(int* arr, int N, int K,
            int height)
{
    // Sum of chocolate cut
    int sum = 0;
 
    // Finding the chocolate cut
    // at this height
    for (int i = 0; i < N; i++) {
        if (height < arr[i])
            sum += arr[i] - height;
    }
 
    // Return true if valid cut
    return (sum >= K);
}
 
int maxHeight(int* arr, int N, int K)
{
 
    // High as max height
    int high = *max_element(arr, arr + N);
 
    // Low as min height
    int low = 0;
 
    // Mid element for binary search
    int mid;
 
    // Binary search function
    while (high - low > 1) {
 
        // Update mid
        mid = (high + low) / 2;
 
        // Update high/low
        if (isValid(arr, N, K, mid)) {
            low = mid;
        }
        else {
            high = mid - 1;
        }
    }
 
    // After binary search we get 2 elements
    // high and low which we can manually compare
    if (isValid(arr, N, K, high)) {
        return high;
    }
    else {
        return low;
    }
}
 
// Driver code
int main()
{
    // Defining input
    int arr[4] = { 15, 20, 8, 17 };
    int N = 4;
    int K = 7;
 
    // Calling the function
    cout << maxHeight(arr, N, K);
    return 0;
}


Java
// java program to implement the approach
import java.util.Arrays;
 
class GFG {
 
  // Predicate function
  static boolean isValid(int[] arr, int N, int K,
                         int height) {
    // Sum of chocolate cut
    int sum = 0;
 
    // Finding the chocolate cut
    // at this height
    for (int i = 0; i < N; i++) {
      if (height < arr[i])
        sum += arr[i] - height;
    }
 
    // Return true if valid cut
    return (sum >= K);
  }
 
  static int maxHeight(int[] arr, int N, int K) {
 
    // High as max height
    int[] a = arr.clone();
    Arrays.sort(a);
    int high = a[a.length - 1];
 
    // Low as min height
    int low = 0;
 
    // Mid element for binary search
    int mid;
 
    // Binary search function
    while (high - low > 1) {
 
      // Update mid
      mid = (high + low) / 2;
 
      // Update high/low
      if (isValid(arr, N, K, mid)) {
        low = mid;
      } else {
        high = mid - 1;
      }
    }
 
    // After binary search we get 2 elements
    // high and low which we can manually compare
    if (isValid(arr, N, K, high)) {
      return high;
    } else {
      return low;
    }
  }
 
  // Driver code
  public static void main(String[] args)
  {
 
    // Defining input
    int arr[] = { 15, 20, 8, 17 };
    int N = 4;
    int K = 7;
 
    // Calling the function
    System.out.println(maxHeight(arr, N, K));
  }
}
 
// This code is contributed by saurabh_jaiswal.


Python3
# Python code for the above approach
 
# Predicate function
def isValid(arr, N, K, height):
    # Sum of chocolate cut
    sum = 0
 
    # Finding the chocolate cut
    # at this height
    for i in range(N):
        if (height < arr[i]):
            sum += arr[i] - height
 
    # Return true if valid cut
    return (sum >= K)
 
 
def maxHeight(arr, N, K):
 
    # High as max height
    high = max(arr)
 
    # Low as min height
    low = 0
 
    # Mid element for binary search
    mid = None
 
    # Binary search function
    while (high - low > 1):
 
        # Update mid
        mid = (high + low) // 2
 
        # Update high/low
        if (isValid(arr, N, K, mid)):
            low = mid
        else:
            high = mid - 1
 
    # After binary search we get 2 elements
    # high and low which we can manually compare
    if (isValid(arr, N, K, high)):
        return high
    else:
        return low
 
# Driver code
 
 
# Defining input
arr = [15, 20, 8, 17]
N = 4
K = 7
 
# Calling the function
print(maxHeight(arr, N, K))
 
# This code is contributed by Saurabh Jaiswal


C#
// C# program to implement the approach
using System;
 
public class GFG {
 
  // Predicate function
  static bool isValid(int[] arr, int N, int K,
                      int height) {
    // Sum of chocolate cut
    int sum = 0;
 
    // Finding the chocolate cut
    // at this height
    for (int i = 0; i < N; i++) {
      if (height < arr[i])
        sum += arr[i] - height;
    }
 
    // Return true if valid cut
    return (sum >= K);
  }
 
  static int maxHeight(int[] arr, int N, int K) {
 
    // High as max height
    int[] a = new int[arr.Length];
    a =arr;
    Array.Sort(a);
    int high = a[a.Length - 1];
 
    // Low as min height
    int low = 0;
 
    // Mid element for binary search
    int mid;
 
    // Binary search function
    while (high - low > 1) {
 
      // Update mid
      mid = (high + low) / 2;
 
      // Update high/low
      if (isValid(arr, N, K, mid)) {
        low = mid;
      } else {
        high = mid - 1;
      }
    }
 
    // After binary search we get 2 elements
    // high and low which we can manually compare
    if (isValid(arr, N, K, high)) {
      return high;
    } else {
      return low;
    }
  }
 
  // Driver code
  public static void Main(String[] args)
  {
 
    // Defining input
    int []arr = { 15, 20, 8, 17 };
    int N = 4;
    int K = 7;
 
    // Calling the function
    Console.WriteLine(maxHeight(arr, N, K));
  }
}
 
// This code is contributed by shikhasingrajput


Javascript



输出
15

时间复杂度: O(logN)
辅助空间: O(1)