Как найти k-й наименьший элемент в объединении двух отсортированных массивов? - PullRequest
95 голосов
/ 05 января 2011

Это домашнее задание.Они говорят, что требуется O(logN + logM), где N и M - длины массивов.

Давайте назовем массивы a и b.Очевидно, мы можем игнорировать все a[i] и b[i], где i> k.
Сначала давайте сравним a[k/2] и b[k/2].Пусть b[k/2]> a[k/2].Поэтому мы можем также отбросить все b[i], где i> k / 2.

Теперь у нас есть все a[i], где i b[i], где i

Какой следующий шаг?

Ответы [ 16 ]

1 голос
/ 02 марта 2017

Ссылка на код сложность (log (n) + log (m))

Ссылка на код (log (n) * log (m))

Реализация решения (log (n) + log (m))

Я хотел бы добавить свое объяснение к проблеме. Это классическая проблема, когда мы должны использовать тот факт, что два массива отсортированы. нам дали два отсортированных массива arr1 размера sz1 и arr2 размера sz2

а) Предположим, что если

Проверка, является ли k действительным

k is> (sz1 + sz2)

тогда мы не сможем найти k-й наименьший элемент в объединении обоих отсортированных массивов ryt, поэтому вернем неверные данные. б) Теперь, если указанное выше условие имеет значение «ложь», и мы имеем действительное и допустимое значение k,

Управление граничными делами

Мы добавим оба массива по значениям -infinity в начале и + бесконечности в конце, чтобы покрыть граничные случаи k = 1,2 и k = (sz1 + sz2-1), (sz1 + sz2 ) и т.д.

Теперь оба массива имеют размер (sz1 + 2) и (sz2 + 2) соответственно

Основной алгоритм

Теперь мы выполним бинарный поиск по arr1. Мы выполним бинарный поиск по arr1 в поисках индекса i, startIndex <= i <= endIndex </strong>

такой, что если мы найдем соответствующий индекс j в arr2, используя ограничение {(i + j) = k}, то если

если (arr2 [j-1] , то arr1 [i] является k-м наименьшим (случай 1)

иначе, если (arr1 [i-1] , то arr2 [i] является k-м наименьшим (Случай 2)

else означает либо arr1 [i] (Case3)

или arr2 [j-1] (Case4)

Так как мы знаем, что k-й наименьший элемент имеет (k-1) элементов меньше его в объединении обоих массивов ryt? Таким образом,

В Case1 , что мы и сделали, мы убедились, что в arr1 [i] есть всего (k-1) меньших элементов, потому что элементы, меньшие чем arr1 [i] в ​​массиве arr1, - это i- На 1 число больше, чем мы знаем (arr2 [j-1]

Но ответ не всегда приходит из первого массива, т.е. arr1, поэтому мы проверили case2 , который также удовлетворяет аналогично случаю 1, потому что (i-1) + (j-1) = (k-1 ) Теперь, если у нас есть (arr1 [i-1]

В case3 , чтобы сформировать его для любого из случаев 1 или 2, нам нужно увеличить i, и j будет найдено в соответствии с ограничением {(i + j) = k}, то есть в бинарном поиске переместиться в правую часть, т.е. сделать startIndex = middleIndex

В case4 , чтобы сформировать его для любого из случаев 1 или 2, нам нужно уменьшить i, и j будет найдено в соответствии с ограничением {(i + j) = k}, то есть в бинарном поиске переместиться в левую часть, т.е. сделать endIndex = middleIndex.

Теперь, как определить startIndex и endIndex в начале двоичного поиска по arr1 с startindex = 1 и endIndex = ??. Нам нужно решить.

Если k> sz1, endIndex = (sz1 + 1), иначе endIndex = k;

Потому что, если k больше, чем размер первого массива, нам, возможно, придется выполнить бинарный поиск по всему массиву arr1, иначе нам нужно только взять первые k его элементов, потому что элементы sz1-k никогда не могут внести вклад в вычисление kth наименьшего .

КОД, показанный ниже

// Complexity    O(log(n)+log(m))

#include<bits/stdc++.h>
using namespace std;
#define f(i,x,y) for(int i = (x);i < (y);++i)
#define F(i,x,y) for(int i = (x);i > (y);--i)
int max(int a,int b){return (a > b?a:b);}
int min(int a,int b){return (a < b?a:b);}
int mod(int a){return (a > 0?a:((-1)*(a)));}
#define INF 1000000




int func(int *arr1,int *arr2,int sz1,int sz2,int k)

{

if((k <= (sz1+sz2))&&(k > 0))

{
int s = 1,e,i,j;
if(k > sz1)e = sz1+1;
else e = k;
while((e-s)>1)
{
  i = (e+s)/2;
  j = ((k-1)-(i-1)); 
  j++;
  if(j > (sz2+1)){s = i;}
  else if((arr1[i] >= arr2[j-1])&&(arr1[i] <= arr2[j]))return arr1[i];
  else if((arr2[j] >= arr1[i-1])&&(arr2[j] <= arr1[i]))return arr2[j];
  else if(arr1[i] < arr2[j-1]){s = i;}
  else if(arr1[i] > arr2[j]){e = i;}
  else {;}
}
i = e,j = ((k-1)-(i-1));j++;
if((arr1[i] >= arr2[j-1])&&(arr1[i] <= arr2[j]))return arr1[i];
else if((arr2[j] >= arr1[i-1])&&(arr2[j] <= arr1[i]))return arr2[j];
else
{
  i = s,j = ((k-1)-(i-1));j++;
  if((arr1[i] >= arr2[j-1])&&(arr1[i] <= arr2[j]))return arr1[i];
  else return arr2[j];
}

  }

 else

{
cout << "Data Invalid" << endl;
return -INF;

}

}





int main()

{
int n,m,k;
cin >> n >> m >> k;
int arr1[n+2];
int arr2[m+2];
f(i,1,n+1)
cin >> arr1[i];
f(i,1,m+1)
cin >> arr2[i];
arr1[0] = -INF;
arr2[0] = -INF;
  arr1[n+1] = +INF;  
arr2[m+1] = +INF; 
int val = func(arr1,arr2,n,m,k);
if(val != -INF)cout << val << endl;   
return 0;

}

Для решения сложности (log (n) * log (m))

Просто я пропустил, используя преимущество того факта, что для каждого i j можно найти, используя ограничение {(i-1) + (j-1) = (k-1)} Так что для каждого ii дополнительно применялся бинарный поиск во втором массиве найти j такой, что arr2 [j] <= arr1 [i]. Так что это решение может быть дополнительно оптимизировано </p>

1 голос
/ 30 декабря 2016

Вот мое решение в Java.Постараюсь еще больше его оптимизировать

  public class FindKLargestTwoSortedArray {

    public static void main(String[] args) {
        int[] arr1 = { 10, 20, 40, 80 };
        int[] arr2 = { 15, 35, 50, 75 };

    FindKLargestTwoSortedArray(arr1, 0, arr1.length - 1, arr2, 0,
            arr2.length - 1, 6);
    }


    public static void FindKLargestTwoSortedArray(int[] arr1, int start1,
            int end1, int[] arr2, int start2, int end2, int k) {

        if ((start1 <= end1 && start1 >= 0 && end1 < arr1.length)
                && (start2 <= end2 && start2 >= 0 && end2 < arr2.length)) {

            int midIndex1 = (start1 + (k - 1) / 2);
            midIndex1 = midIndex1 >= arr1.length ? arr1.length - 1 : midIndex1;
            int midIndex2 = (start2 + (k - 1) / 2);
            midIndex2 = midIndex2 >= arr2.length ? arr2.length - 1 : midIndex2;


            if (arr1[midIndex1] == arr2[midIndex2]) {
                System.out.println("element is " + arr1[midIndex1]);
            } else if (arr1[midIndex1] < arr2[midIndex2]) {

                if (k == 1) {
                    System.out.println("element is " + arr1[midIndex1]);
                    return;
                } else if (k == 2) {
                    System.out.println("element is " + arr2[midIndex2]);
                    return;
                }else if (midIndex1 == arr1.length-1 || midIndex2 == arr2.length-1 ) {
                    if(k==(arr1.length+arr2.length)){
                    System.out.println("element is " + arr2[midIndex2]);
                    return;
                    }else if(k==(arr1.length+arr2.length)-1){
                        System.out.println("element is " + arr1[midIndex1]);
                        return;
                    }

                }

                int remainingElementToSearch = k - (midIndex1-start1);
                FindKLargestTwoSortedArray(
                        arr1,
                        midIndex1,
                        (midIndex1 + remainingElementToSearch) >= arr1.length ? arr1.length-1
                                : (midIndex1 + remainingElementToSearch), arr2,
                        start2, midIndex2, remainingElementToSearch);

            } else if (arr1[midIndex1] > arr2[midIndex2]) {
                FindKLargestTwoSortedArray(arr2, start2, end2, arr1, start1,
                        end1, k);
            }

        } else {
            return;
        }

    }
}

Это вдохновлено Алго на замечательное видео на YouTube

1 голос
/ 06 ноября 2016
public class KthSmallestInSortedArray {

    public static void main(String[] args) {
        int a1[] = {2, 3, 10, 11, 43, 56},
                a2[] = {120, 13, 14, 24, 34, 36},
                k = 4;

        System.out.println(findKthElement(a1, a2, k));

    }

    private static int findKthElement(int a1[], int a2[], int k) {

        /** Checking k must less than sum of length of both array **/
        if (a1.length + a2.length < k) {
            throw new IllegalArgumentException();
        }

        /** K must be greater than zero **/
        if (k <= 0) {
            throw new IllegalArgumentException();
        }

        /**
         * Finding begin, l and end such that
         * begin <= l < end
         * a1[0].....a1[l-1] and
         * a2[0]....a2[k-l-1] are the smallest k numbers
         */
        int begin = Math.max(0, k - a2.length);
        int end = Math.min(a1.length, k);

        while (begin < end) {
            int l = begin + (end - begin) / 2;

            /** Can we include a1[l] in the k smallest numbers */
            if ((l < a1.length) &&
                    (k - l > 0) &&
                    (a1[l] < a2[k - l - 1])) {

                begin = l + 1;

            } else if ((l > 0) &&
                    (k - l < a2.length) &&
                    (a1[l - 1] > a2[k - 1])) {

                /**
                 * This is the case where we can discard
                 * a[l-1] from the set of k smallest numbers
                 */
                end = l;

            } else {

                /**
                 * We found our answer since both inequalities were
                 * false
                 */
                begin = l;
                break;
            }
        }

        if (begin == 0) {
            return a2[k - 1];
        } else if (begin == k) {
            return a1[k - 1];
        } else {
            return Math.max(a1[begin - 1], a2[k - begin - 1]);
        }
    }
}
1 голос
/ 19 сентября 2015

Первый псевдокод, приведенный выше, не работает для многих значений. Например, Вот два массива. int [] a = {1, 5, 6, 8, 9, 11, 15, 17, 19}; int [] b = {4, 7, 8, 13, 15, 18, 20, 24, 26};

Это не сработало для k = 3 и k = 9 в нем. У меня есть другое решение. Это дано ниже.

private static void traverse(int pt, int len) {
int temp = 0;

if (len == 1) {
    int val = 0;
    while (k - (pt + 1) - 1 > -1 && M[pt] < N[k - (pt + 1) - 1]) {

    if (val == 0)
        val = M[pt] < N[k - (pt + 1) - 1] ? N[k - (pt + 1) - 1]
            : M[pt];
    else {
        int t = M[pt] < N[k - (pt + 1) - 1] ? N[k - (pt + 1) - 1]
            : M[pt];
        val = val < t ? val : t;

    }

    ++pt;
    }

    if (val == 0)
    val = M[pt] < N[k - (pt + 1) - 1] ? N[k - (pt + 1) - 1] : M[pt];

    System.out.println(val);
    return;
}

temp = len / 2;

if (M[pt + temp - 1] < N[k - (pt + temp) - 1]) {
    traverse(pt + temp, temp);

} else {
    traverse(pt, temp);
}

}

Но ... это также не работает для k = 5. Есть четный / нечетный улов k, который не позволяет ему быть простым.

0 голосов
/ 12 августа 2014

Ниже кода C # для поиска k-го наименьшего элемента в объединении двух отсортированных массивов.Сложность времени: O (logk)

        public static int findKthSmallestElement1(int[] A, int startA, int endA, int[] B, int startB, int endB, int k)
        {
            int n = endA - startA;
            int m = endB - startB;

            if (n <= 0)
                return B[startB + k - 1];
            if (m <= 0)
                return A[startA + k - 1];
            if (k == 1)
                return A[startA] < B[startB] ? A[startA] : B[startB];

            int midA = (startA + endA) / 2;
            int midB = (startB + endB) / 2;

            if (A[midA] <= B[midB])
            {
                if (n / 2 + m / 2 + 1 >= k)
                    return findKthSmallestElement1(A, startA, endA, B, startB, midB, k);
                else
                    return findKthSmallestElement1(A, midA + 1, endA, B, startB, endB, k - n / 2 - 1);
            }
            else
            {
                if (n / 2 + m / 2 + 1 >= k)
                    return findKthSmallestElement1(A, startA, midA, B, startB, endB, k);
                else
                    return findKthSmallestElement1(A, startA, endA, B, midB + 1, endB, k - m / 2 - 1);

            }
        }
0 голосов
/ 21 февраля 2012

Проверьте этот код.

import math
def findkthsmallest():

    A=[1,5,10,22,30,35,75,125,150,175,200]
    B=[15,16,20,22,25,30,100,155,160,170]
    lM=0
    lN=0
    hM=len(A)-1
    hN=len(B)-1
    k=17

    while True:
        if k==1:
            return min(A[lM],B[lN])


        cM=hM-lM+1
        cN=hN-lN+1
        tmp = cM/float(cM+cN)
        iM=int(math.ceil(tmp*k))
        iN=k-iM
        iM=lM+iM-1
        iN=lN+iN-1
        if A[iM] >= B[iN]:
            if iN == hN or A[iM] < B[iN+1]:
                return A[iM]
            else:
                k = k - (iN-lN+1)
                lN=iN+1
                hM=iM-1
        if B[iN] >= A[iM]:
            if iM == hM or B[iN] < A[iM+1]:
                return B[iN]
            else:
                k = k - (iM-lM+1)
                lM=iM+1
                hN=iN-1
        if hM < lM:
            return B[lN+k-1]
        if hN < lN:
            return A[lM+k-1]

if __name__ == '__main__':
    print findkthsmallest();
...