Переместить дубликаты в конец отсортированного массива - PullRequest
0 голосов
/ 01 сентября 2018

Мне задали этот вопрос в интервью. Есть отсортированный массив с дубликатами. Цель состоит в том, чтобы сначала возвратить массив с уникальными элементами и дублировать в конце, сохраняя порядок. Например, [1, 1, 2, 3, 4, 4, 5] должно стать [1, 2, 3, 4, 5, 1, 4].

Мне удалось решить вопрос с дополнительным пробелом (O (n) пробел) и линейным временем (O (n) время), но я не уверен, что это лучший ответ, в идеале не используя линейный пробел.

Я искал stackoverflow и нашел похожие вопросы, но не совсем такие же. Например, был вопрос сортировка массива и перемещение дубликатов до конца, но в моем случае массив уже отсортирован, и цель состоит в том, чтобы перемещать дубликаты только до конца.

Ответы [ 6 ]

0 голосов
/ 15 мая 2019

Вот код C, который помещает дублированные строки в последний из массива. Массив индикатора используется для указания индекса, в котором дублируется строка. То есть: если s [0] == s [1], то для индикатора [1] будет присвоено 0, так как при этом индексе строка повторяется. Затем используйте массив индикаторов, чтобы поменять дублированную строку на последнее действительное место в массиве.

т.е.: если мы нашли этот индикатор [1] = 0, это означает, что в индексе 1 есть дублирующаяся строка, и нам нужно переместить ее в последний из массива, но что если последний элемент массива дублируется тоже !! тогда мы должны перейти ко второму элементу от конца массива

    void put_dublicates_to_last(char**s, int n)
{
    int i = 0, j = 0, flag = 0,counter=0;
    int* indicator = malloc(n * sizeof(int));
    char * temp;
    for (i = 0; i < n; i++)
        indicator[i] = -1;
    for (i = 0; i < n; i++)
    {
        for (j = i + 1; j < n; j++)
        {
            if (strcmp(s[i], s[j]) == 0)
            {
                //swap with the last element
                counter++;
                indicator[j] = 0;
            }
        }
    }
    printf("counter is %d\n", counter);
    //use the indicator to swap with the last elements 
    for (i = 0; i < n; i++)
    {
        for (j = n; j >= 0; j--)
        {
            if (indicator[i] == 0)
            {
                if (indicator[j] != 0)
                {
                    //swap
                    temp = s[i];
                    s[i] = s[j-1];
                    s[j-1] = temp;
                    flag = 1;
                }
            }
            if (flag)
            {
                flag = 0;
                break;
            }

        }

    }

    for (i = 0; i < n; i++)
        printf("%s\n", s[i]);
}
0 голосов
/ 25 сентября 2018

Вам нужно иметь 2-3 указателя (указателя). я: следующие уникальные элементы будут размещены на этой позиции j: указатель линейного обхода в списке

private static void fix(int[] nums) {
    int i = 0;
    int j = 0;
    while (j < nums.length) {
        int k;
        for (k = j + 1; k < nums.length && nums[k] == nums[j]; k++) {

        }

        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
        j = k;
        i++;
    }
}
0 голосов
/ 01 сентября 2018

С риском констатировать очевидное. , , подход во O ( n log n ) времени и O (1) дополнительного пространства:

  1. Просматривать массив, чтобы найти первый элемент с каждым значением, и поменять этот элемент прямо в правильную позицию. (Например, когда вы достигаете четвертого отдельного значения, вы меняете первый элемент с этим значением на позицию # 4.)
    • Этот шаг требует O ( n ) времени и O (1) дополнительного пробела.
    • После этого шага массив состоит из всех уникальных элементов в правильном порядке, за которыми следуют все дубликаты в порядке мусора.
  2. Сортировка дубликатов с использованием heapsort.
    • Этот шаг требует O ( n log n ) времени и O (1) дополнительного пространства.
0 голосов
/ 01 сентября 2018

Если ваши значения находятся в ограниченном диапазоне, существует решение во времени O (n) и пространстве O (1).

Определить максимальное значение в массиве. Получите некоторую константу C > arraymax, например - C = 10 для вашего массива.

Сканирование массива, сжатие уникальных значений и подсчет дубликатов для каждого значения. Если значение V имеет K>0 дубликатов, напишите V+C*K вместо значения.

При следующем сканировании найдите значения с дубликатами, извлеките количество дубликатов и запишите их после сжатых уникальных значений.

def dedup(lst):
    mx = max(lst) + 1
    dupcnt = 0
    delcnt = 0
    start = 0
    for i in range(1, len(lst) + 1):
        if i == len(lst) or (lst[i] != lst[start]):
            lst[start - delcnt] = lst[start] + dupcnt * mx
            delcnt += dupcnt
            start = i
            dupcnt = 0
        else:
            dupcnt += 1
    dupidx = len(lst) - delcnt
    for i in range(0, len(lst) - delcnt):
        dupcnt = lst[i] // mx
        if dupcnt:
           lst[i] %= mx
           for j in range(dupidx, dupidx+dupcnt):
              lst[j] = lst[i]
           dupidx += dupcnt
    return lst

print(dedup([1,2,2,2,3,4,4,5]))
>>> [1, 2, 3, 4, 5, 2, 2, 4]
0 голосов
/ 01 сентября 2018

Не совсем ясно, как следует обрабатывать несколько дубликатов или что вы точно спрашиваете, , но я предполагаю, что вы хотите убедиться, что пространство O (1) удовлетворяется, независимо от сложности времени, вот что я попытаюсь ответить.

С массивами, O (1) пробел, O (N ^ 2) время:

Вы можете сделать это на месте, просто поменяв дублирующие элементы до конца. Вы можете найти дубликаты элементов, сохранив «текущий» указатель и просто проверив, что «следующий» элемент не совпадает с «текущим». Это время O (n ^ 2) в худшем случае. Пример:

[1,1,2,3,4,4,5] # "cur" is index 0 (element 1), and "next" is index 1 (element 1). Swap "next" to end.
[1,2,1,3,4,4,5] # swapping
[1,2,3,1,4,4,5] # swapping
...             # Tedious swapping
[1,2,3,4,4,5,1] # Done swapping. Increment "cur".
[1,2,3,4,4,5,1] # "cur" is index 1 (element 2), and "next" is index 2 (element 3). Increment "cur"
...             # Boring (no duplicates detected)
[1,2,3,4,4,5,1] # "cur" is index 3 (element 4), and "next" is index 4 (element 4). Swap "next" to end.
[1,2,3,4,5,4,1] # swapping
[1,2,3,4,5,1,4] # Done swapping. Increment "cur"
...             # No more duplicates
# Done

Кроме того, на практике тратить время на меньшее пространство обычно не стоит. Память дешева, но медленное время отклика может потерять пользователей, что дорого. Заметным исключением являются встроенные системы, в которых память может быть ограничена, а входы короткими (для небольших входов асимптотическое время выполнения не имеет значения).

Со связанными списками, O (1) пробел, O (N) время:

Если бы у вас был связанный список вместо массива, вы могли бы сделать это за O (n) время и пространство O (1) довольно просто. Связанные списки имеют преимущество перед массивами, когда вы вынужден «сдвигать» элементы вокруг, так как они могут перемещать указатели вместо перемещения ВСЕХ элементов по позиции Стратегия cur / next аналогична для связанных списков, как указано выше с массивом. Вот пример:

1->1->2->3->4->4->5 # "cur" is first element (value 1), and "next" is second element (value 1). Swap "next" to the end.

1
 \
1->2->3->4->4->5    # Move "cur"'s pointer to "next"'s next element.

1->2->3->4->4->5->1 # Set "next"'s pointer to null, set tails pointer to "next"

...                 # Boring stuff with no duplicates

1->2->3->4->4->5->1 # "cur" is fourth element (value 4), and "next" is fifth element (value 4). Swap fifth element to end.

         4
          \
1->2->3->4->5->1    # Move "cur"'s pointer to "next"'s next element.

1->2->3->4->5->1->4 # Set "next"'s pointer to null, set tails pointer to "next"

...                 # No more duplicates
# Done (hopefully it's clear moving and element to the end is O(1) instead of O(n))

Если вы можете превратить массив в связанный список за время O (n) и пространство O (1), проблема решена. Однако это невозможно. Связанные списки занимают больше места на элемент, чем массив, так что просто имея связанный список где-либо в программе, я думаю, что пространство O (1) будет быть нарушенным.

Так как это был вопрос для интервью, возможно, стоило бы отметить, что связанные списки несколько лучше для эффективного решения этой проблемы, независимо от формулировки проблемы. Обычно интервьюерам нравится видеть, что вы можете правильно применять структуры данных, и иногда они поддаются изменению типа ввода.

Умные структуры данных и тупой код работают намного лучше, чем другие наоборот. --Eric S Raymond

0 голосов
/ 01 сентября 2018

ОБНОВЛЕНИЕ: неверно истолковано ваши намерения, когда вы беспокоились о космосе, это PHP-версия «указателей». Поскольку он отсортирован, мы можем пройти цикл один раз, верно? Если нет, то мы, вероятно, выпекаем двойную сортировку в самой сортировке.

function findRepeating(&$arr)
{
    $size = count($arr);
    $previous = -99999;
    for ($i = 0; $i < $size; $i++) {
        if ($i>0)
            $previous = $arr[$i-1];

        if ($arr[$i] == $previous) {
            array_push($arr,$arr[$i]); //push to end
            unset($arr[$i]); //then remove current one
        }
    }
    var_dump($arr);
}

Мы просто берем текущий размер массива, и когда мы находим дубликаты, нажимаем на конец массива, немного увеличивая его размер, который компенсируется unset ().

array(7) {
  [0]=>
  string(1) "1"
  [2]=>
  string(1) "2"
  [3]=>
  string(1) "3"
  [4]=>
  string(1) "4"
  [6]=>
  string(1) "5"
  [7]=>
  string(1) "1"
  [8]=>
  string(1) "4"
}

На языке более низкого уровня вы можете просто перетасовывать указатели, потому что вы знаете конечное значение, поэтому вы принимаете это к сведению и просто добавляете дубликаты после этого и добавляете к смещению по мере его продвижения. Полностью достижимый с массивами или без них, с массивами мы просто меняемся местами. Мой пример на PHP, поэтому вместо перетасовки я просто расширяю массив и временно использую один дополнительный пробел.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...