Разве не сложность по времени моего кода O (n + m) - PullRequest
1 голос
/ 16 марта 2020

Мой код ниже:

double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size){
    int* new = (int*)malloc(sizeof(int) * (nums1Size+nums2Size));
    int i = 0;
    int count1 = 0;
    int count2 = 0;

    if(nums1Size+nums2Size == 1){
        if(nums1Size == 1)
            return *nums1;
        else
            return *nums2;
    }
    else if(nums1Size == 0){
        if((nums2Size & 0x1) == 0)
            return (double)(nums2[nums2Size/2-1]+nums2[nums2Size/2])/2;
        else
            return (double)nums2[nums2Size/2];
    }
    else if(nums2Size == 0){
        if((nums1Size & 0x1) == 0)
            return (double)(nums1[nums1Size/2-1]+nums1[nums1Size/2])/2;
        else
            return (double)nums1[nums1Size/2];
    }


   while(i != (nums1Size+nums2Size))
   {

       if((nums1[count1 == nums1Size ? count1-1:count1] > nums2[count2 == nums2Size ? count2-1:count2] 
           && (count2) != nums2Size)
          || (count1) == nums1Size)
       {
           *(new+i) = *(nums2+count2);
           count2++;
       }
       else{
           *(new+i) = *(nums1+count1);
           count1++;

       }
       i++;
   }   

    if(((nums1Size+nums2Size) & 0x1) == 0){
        return (double)(new[(nums1Size+nums2Size)/2 - 1] + new[(nums1Size+nums2Size)/2]) / 2;
    }
    else
        return (double)new[(nums1Size+nums2Size)/2];
}

И ниже распределение времени выполнения представлений по Leetcode:

enter image description here

Вопрос в том, , даже если в C есть много отправленных кодов с O (log (m + n)), но я думаю, что сложность Времени моего кода равна O (m + n). так что не имеет смысла, что мой код занимает верхние 2% в Leetcode согласно графику распределения. Конечно, линейный метод быстрее, чем log, для небольшого количества входных данных, но контрольные примеры достаточно велики, чтобы побить O (log (m + n)). Я не знаю, почему мой код проходит с такой скоростью.

буду очень признателен за ваши комментарии!

1 Ответ

2 голосов
/ 17 марта 2020

Из моего главного комментария: Вы выделяете new в начале функции. Если какой-либо из операторов return "раннего побега" будет выполнен, вы потеряете память.

Так нужно ли вводить free() в каждый оператор return? или как я могу исправить свой код?

Не делайте malloc до после верхнего блока ранних побегов.

И, сделайте free внизу. Для этого вам понадобится дополнительная переменная для хранения возвращаемого значения, чтобы вы могли безопасно выполнить free(new) (например, double retval;)

Примечание: Обычно чище заменить (например) *(new + i) на new[i]. Кроме того, поддержание кода в <= 80 символов / строка также является хорошим стилем. </p>


Вот один из способов исправить ваш код [прошу прощения за чистую стирку]:

double
findMedianSortedArrays(int *nums1, int nums1Size, int *nums2, int nums2Size)
{
    int *new;
    int i;
    int count1 = 0;
    int count2 = 0;
    double retval;

    if (nums1Size + nums2Size == 1) {
        if (nums1Size == 1)
            return *nums1;
        else
            return *nums2;
    }

    if (nums1Size == 0) {
        if ((nums2Size & 0x1) == 0)
            return (double) (nums2[nums2Size / 2 - 1] +
                nums2[nums2Size / 2]) / 2;
        else
            return nums2[nums2Size / 2];
    }

    if (nums2Size == 0) {
        if ((nums1Size & 0x1) == 0)
            return (double) (nums1[nums1Size / 2 - 1] +
                nums1[nums1Size / 2]) / 2;
        else
            return (double) nums1[nums1Size / 2];
    }

    // allocate this only when you're sure you'll use it
    new = malloc(sizeof(int) * (nums1Size + nums2Size));

    for (i = 0;  i != (nums1Size + nums2Size);  ++i) {
        if ((nums1[count1 == nums1Size ? count1 - 1 : count1] >
            nums2[count2 == nums2Size ? count2 - 1 : count2] &&
                (count2) != nums2Size)
            || (count1) == nums1Size) {
            new[i] = nums2[count2];
            count2++;
        }
        else {
            new[i] = nums1[count1];
            count1++;
        }
    }

    if (((nums1Size + nums2Size) & 0x1) == 0) {
        retval = (double) (new[(nums1Size + nums2Size) / 2 - 1] +
            new[(nums1Size + nums2Size) / 2]) / 2;
    }
    else
        retval = (double) new[(nums1Size + nums2Size) / 2];

    free(new);

    return retval;
}

Но лично мне не нравятся множественные операторы return в функции. Труднее отлаживать [используя gdb], потому что вам нужно установить точку останова на каждый return.

Вот версия, которая использует do { ... } while (0); как "один раз" через «l oop», что позволяет нам исключить if/else «лестничную» логику c [которую я также лично не люблю], и только single return внизу. YMMV ...

double
findMedianSortedArrays(int *nums1, int nums1Size, int *nums2, int nums2Size)
{
    int *new = NULL;
    int i = 0;
    int count1 = 0;
    int count2 = 0;
    double retval;

    do {
        if (nums1Size + nums2Size == 1) {
            if (nums1Size == 1)
                retval = *nums1;
            else
                retval = *nums2;
            break;
        }

        if (nums1Size == 0) {
            if ((nums2Size & 0x1) == 0)
                retval = (double) (nums2[nums2Size / 2 - 1] +
                    nums2[nums2Size / 2]) / 2;
            else
                retval = nums2[nums2Size / 2];
            break;
        }

        if (nums2Size == 0) {
            if ((nums1Size & 0x1) == 0)
                retval = (double) (nums1[nums1Size / 2 - 1] +
                    nums1[nums1Size / 2]) / 2;
            else
                retval = (double) nums1[nums1Size / 2];
            break;
        }

        // allocate this only when you're sure you'll use it
        new = malloc(sizeof(int) * (nums1Size + nums2Size));

        for (;  i != (nums1Size + nums2Size);  ++i) {
            if ((nums1[count1 == nums1Size ? count1 - 1 : count1] >
                nums2[count2 == nums2Size ? count2 - 1 : count2] &&
                    (count2) != nums2Size)
                || (count1) == nums1Size) {
                new[i] = nums2[count2];
                count2++;
            }
            else {
                new[i] = nums1[count1];
                count1++;
            }
        }

        if (((nums1Size + nums2Size) & 0x1) == 0) {
            retval = (double) (new[(nums1Size + nums2Size) / 2 - 1] +
                new[(nums1Size + nums2Size) / 2]) / 2;
        }
        else
            retval = (double) new[(nums1Size + nums2Size) / 2];
    } while (0);

    if (new != NULL)
        free(new);

    return retval;
}

ОБНОВЛЕНИЕ:

спасибо! Я понял. ваш код более понятен, чем мой на самом деле !. но что вы думаете о производительности между ними? (if/else и do{...}while(0)). Потому что если мы предположим, что компилятор будет работать так, как мы обычно ожидаем, if/else быстрее, чем если бы, который находится в do{...} в пересмотренном коде. Большое спасибо!

На самом деле, если мы разберем обе версии [скомпилированные с -O2], то версия do/while будет на 4 инструкции по сборке короче.

Но, чтобы настроить ее, вы должны измерить ее .

Оптимизатор в значительной степени сделает их похожими.

Основная часть времени функции тратится на for l oop, что одинаково для обоих. Скорость l oop превосходит любые дополнительные издержки do/while, которые могут быть инструкцией ассемблера или двумя [но, опять же, do/while имеет меньше инструкций].

Итак, настройка / оптимизация пролога / epilog-код функции [обычно] не стоит. Ускорение l oop - это.

Чтобы настроить / оптимизировать, либо выполните профилирование, чтобы определить, где код тратит наибольшее количество времени [ или для чего-то такого простого, это явно op], или добавьте метку времени и получите истекшее время для функции [или различных частей].

Как я уже упоминал, трудно добавить точку останова для функции, которая имеет несколько операторов return.

Кроме того, иногда вы не можете подключить отладчик. Или трудно найти значимое место для установки точки останова. Например, если у вас есть программа, которая работает нормально (например, в течение нескольких дней), а затем прерывает работу после (например, в течение 63 часов), вам может потребоваться выполнить внутренний сравнительный анализ и отладку в стиле * 1084:

#ifdef DEBUG
#define dbgprint(_fmt) \
    do { \
        printf(_fmt); \
    } while (0)
#else
#define dbgprint(_fmt) \
    do { \
    } while (0)
#endif

double
findMedianSortedArrays(int *nums1, int nums1Size, int *nums2, int nums2Size)
{
    double retval;

    dbgprint("findMedianSortedArrays: ENTER nums1Size=%d nums2Size=%d\n",
        nums1Size,nums2Size);

    // ... the code

    dbgprint("findMediaSortedArrays: EXIT retval=%g\n",retval);

    return retval;
}

Гораздо проще вставить операторы отладочной печати со второй версией.

Кстати, я все время так делаю. И одна из моих сильных сторон - быстрое улучшение кода и производительности [как я много пишу в реальном времени].

...