簡體   English   中英

在排序后的數組中找到小於目標的第一個元素

[英]Find the first element in a sorted array that is smaller than the target

在排序數組中,可以使用二進制搜索來執行許多不同種類的操作:

  1. 查找小於目標的第一個元素
  2. 找到比目標大的第一個元素,或者
  3. 查找小於或等於目標的第一個元素
  4. 查找第一個大於或等於目標甚至特定的元素
  5. 查找最接近目標的元素。

您可以在堆棧溢出中找到#2和#5的答案,它們的答案使用的是二進制搜索的變異,但是沒有固定的算法來回答這些問題,特別是在調整索引時。

例如,在問題3中,找到排序數組中第一個小於或等於目標的元素:給定int[] stocks = new int[]{1, 4, 3, 1, 4, 6}; ,我想找到小於5的第一個元素。排序后應該返回4,我的代碼如下:

private static int findMin(int[] arr, int target) {
    Arrays.sort(arr);
    int lo = 0;
    int hi = arr.length - 1;
    while (lo < hi) {
        int mid = lo + (hi - lo) / 2;
        if (arr[mid] == target) return mid;
        if (arr[mid] > target) {
            hi = mid - 1;
        } else {
            lo = mid;
        }
    }
    return lo;
}

這里的邏輯是:

  1. 如果您發現mid元素等於目標,則只需返回mid
  2. 如果您發現中間元素大於目標,則只丟棄中間元素以及所有大於目標的元素
  3. 如果您發現mid元素小於目標,則mid元素可能是一個答案,您想保留它,但丟棄任何小於它的東西。

如果運行它,它實際上會進入無限循環,但是只需將hi = arr.length - 1的起始索引調整為hi = arr.length; ,實際上效果很好。 我不知道如何真正設置所有條件:如何編寫條件,如何設置hi和lo的起始索引以及使用lo<=hilo < hi

有什么幫助嗎?

基本上在上述情況下,您需要的最大元素要小於給定值,即需要找到給定元素的底數。 使用O(logn)中的二進制搜索可以很容易地做到這一點:

您需要考慮的情況如下:

  1. 如果最后一個元素小於x,則返回最后一個元素。

  2. 如果中點是地板,則返回中點。

  3. 如果在上述兩種情況下均未找到元素,則檢查該元素是否位於-1的中間值和中間的-1值之間。 如果是這樣,則返回-1。
  4. 否則,繼續在左半部分或右半部分上進行迭代,直到找到滿足條件的元素。 根據檢查給定值大於中值還是小於中值,選擇右半部分或左半部分。

請嘗試以下操作:

static int floorInArray(int arr[], int low, int high, int x)
{
    if (low > high)
        return -1;

    // If last element is smaller than x
    if (x >= arr[high])
        return high;

    // Find the middle point
    int mid = (low+high)/2;

    // If middle point is floor.
    if (arr[mid] == x)
        return mid;

    // If x lies between mid-1 and mid
    if (mid > 0 && arr[mid-1] <= x && x < arr[mid])
        return mid-1;

    // If x is smaller than mid, floor
    // must be in left half.
    if (x < arr[mid])
        return floorInArray(arr, low, mid - 1, x);

    // If mid-1 is not floor and x is
    // greater than arr[mid],
    return floorInArray(arr, mid + 1, high,x);
}

在while循環中, hi == lo時沒有大小寫

當迭代最后一個元素或數組只有1個元素時,這種情況適用。

將while循環設置為while(lo <= hi) ,它將在搜索所有元素時終止

或在hi等於lo時在if循環內設置一個if case。

if(hi == lo)

您可以只使用Arrays.binarySearch(int[] a, int key)而不是執行自己的二進制搜索,然后相應地調整返回的值。

如果數組中包含搜索鍵的索引,則返回該索引; 否則為(-( 插入點 )-1)。 插入點定義為將鍵插入數組的點:第一個元素的索引大於鍵,如果數組中的所有元素都小於指定的鍵,則為a.length。 請注意,這保證了當且僅當找到鍵時,返回值才> = 0。

您的規則沒有指定在存在多個有效選擇(具有多個相等值的#3或#4或具有等距值的#5)時返回哪個索引,因此下面的代碼中的代碼做出了明確的選擇。 如果您不關心模棱兩可,則可以刪除多余的代碼;如果不同意我的解決方案,則可以更改邏輯。

請注意,當返回值<0時, returnValue = -insertionPoint - 1 insertionPoint = -returnValue - 1表示insertionPoint = -returnValue - 1 ,在代碼中均低於-idx - 1 因此插入點之前的索引是-idx - 2

這些方法當然可能返回超出范圍的索引值( -1arr.length ),因此調用方始終需要進行檢查。 對於closest()方法,只有在array為空時才可能發生,在這種情況下它將返回-1

public static int smaller(int[] arr, int target) {
    int idx = Arrays.binarySearch(arr, target);
    if (idx < 0) {
        // target not found, so return index prior to insertion point
        return -idx - 2;
    }
    // target found, so skip to before target value(s)
    do {
        idx--;
    } while (idx >= 0 && arr[idx] == target);
    return idx;
}

public static int smallerOrEqual(int[] arr, int target) {
    int idx = Arrays.binarySearch(arr, target);
    if (idx < 0) {
        // target not found, so return index prior to insertion point
        return -idx - 2;
    }
    // target found, so skip to last of target value(s)
    while (idx < arr.length - 1 && arr[idx + 1] == target) {
        idx++;
    }
    return idx;
}

public static int biggerOrEqual(int[] arr, int target) {
    int idx = Arrays.binarySearch(arr, target);
    if (idx < 0) {
         // target not found, so return index of insertion point
        return -idx - 1;
    }
    // target found, so skip to first of target value(s)
    while (idx > 0 && arr[idx - 1] == target) {
        idx--;
    }
    return idx;
}

public static int bigger(int[] arr, int target) {
    int idx = Arrays.binarySearch(arr, target);
    if (idx < 0) {
         // target not found, so return index of insertion point
        return -idx - 1;
    }
    // target found, so skip to after target value(s)
    do {
        idx++;
    } while (idx < arr.length && arr[idx] == target);
    return idx;
}

public static int closest(int[] arr, int target) {
    int idx = Arrays.binarySearch(arr, target);
    if (idx >= 0) {
        // target found, so skip to first of target value(s)
        while (idx > 0 && arr[idx - 1] == target) {
            idx--;
        }
        return idx;
    }
    // target not found, so compare adjacent values
    idx = -idx - 1; // insertion point
    if (idx == arr.length) // insert after last value
        return arr.length - 1; // last value is closest
    if (idx == 0) // insert before first value
        return 0; // first value is closest
    if (target - arr[idx - 1] > arr[idx] - target)
        return idx; // higher value is closer
    return idx - 1; // lower value is closer, or equal distance
}

測試

public static void main(String... args) {
    int[] arr = {1, 4, 3, 1, 4, 6};
    Arrays.sort(arr);
    System.out.println(Arrays.toString(arr));

    System.out.println("  |         Index        |        Value        |");
    System.out.println("  |   <  <=   ~  >=   >  |  <  <=   ~  >=   >  |");
    System.out.println("--+----------------------+---------------------+");
    for (int i = 0; i <= 7; i++)
        test(arr, i);
}

public static void test(int[] arr, int target) {
    int smaller        = smaller       (arr, target);
    int smallerOrEqual = smallerOrEqual(arr, target);
    int closest        = closest       (arr, target);
    int biggerOrEqual  = biggerOrEqual (arr, target);
    int bigger         = bigger        (arr, target);
    System.out.printf("%d | %3d %3d %3d %3d %3d  |%3s %3s %3s %3s %3s  | %d%n", target,
                      smaller, smallerOrEqual, closest, biggerOrEqual, bigger,
                      (smaller < 0 ? "" : String.valueOf(arr[smaller])),
                      (smallerOrEqual < 0 ? "" : String.valueOf(arr[smallerOrEqual])),
                      (closest < 0 ? "" : String.valueOf(arr[closest])),
                      (biggerOrEqual == arr.length ? "" : String.valueOf(arr[biggerOrEqual])),
                      (bigger == arr.length ? "" : String.valueOf(arr[bigger])),
                      target);
}

輸出量

[1, 1, 3, 4, 4, 6]
  |         Index        |        Value        |
  |   <  <=   ~  >=   >  |  <  <=   ~  >=   >  |
--+----------------------+---------------------+
0 |  -1  -1   0   0   0  |          1   1   1  | 0
1 |  -1   1   0   0   2  |      1   1   1   3  | 1
2 |   1   1   1   2   2  |  1   1   1   3   3  | 2
3 |   1   2   2   2   3  |  1   3   3   3   4  | 3
4 |   2   4   3   3   5  |  3   4   4   4   6  | 4
5 |   4   4   4   5   5  |  4   4   4   6   6  | 5
6 |   4   5   5   5   6  |  4   6   6   6      | 6
7 |   5   5   5   6   6  |  6   6   6          | 7

嘗試樹集。 如果您輸入的是數組,請按照以下步驟操作:

  1. 將數組轉換為哈希集。 src: https: //www.geeksforgeeks.org/program-to-convert-array-to-set-in-java/ 2.將哈希集轉換為Tree set。 樹集將按排序順序存儲值,沒有重復項。
  2. 現在,根據需要調用樹設置方法,例如high(),ceiling(),floor(),lower()方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM