将重复项移到排序数组的末尾

时间:2018-09-01 00:29:43

标签: arrays algorithm sorting

在一次采访中有人问我这个问题。 有一个重复的排序数组。 目标是首先返回具有唯一元素的数组,最后返回保留顺序的副本。 例如,[1, 1, 2, 3, 4, 4, 5]应该变成[1, 2, 3, 4, 5, 1, 4]

我能够用一个额外的空间(O(n)空间)和线性时间(O(n)时间)解决问题,但是我不确定这是否是最佳答案,理想情况下不使用线性空间。

我搜索了stackoverflow,发现了类似的问题,但并不完全相同。例如,有一个question对数组进行排序并将重复项移到末尾,但是在我的情况下,该数组已经被排序,目标是仅将重复项移到末尾。

7 个答案:

答案 0 :(得分:4)

如果您的值在有限范围内,则在O(n)时间和O(1)空间中存在解。

确定数组中的最大值。获得一些常数s,例如-为数组使用C > arraymax

扫描数组,压缩唯一值并计算每个值的重复项。如果值C = 10重复了V,则写K>0而不是值。

在下一次扫描中,查找具有重复项的值,提取重复项的数量,并在挤压唯一值后将其写入。

V+C*K

答案 1 :(得分:1)

冒着陈述显而易见的风险。 。 。 O n log n )时间和 O (1)多余空间的一种方法是:

  1. 扫描数组以找到每个值的第一个元素,然后将该元素直接交换到正确的位置。 (例如,当达到第四个不同的值时,将具有该值的第一个元素交换到位置4中。)
    • 此步骤需要 O n )时间和 O (1)额外的空间。
    • 在此步骤之后,数组由正确顺序的所有唯一元素组成,然后是垃圾顺序的所有重复元素。
  2. 使用堆排序对重复项进行排序。
    • 此步骤需要 O n log n )时间和 O (1)额外的空间。 / li>

答案 2 :(得分:0)

更新:误解了您对空间的关注,这是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中,因此我不需改组,而只是扩展数组,所以我只是临时使用一个额外的空间。

答案 3 :(得分:0)

尚不完全清楚应如何处理多个重复项,或者您到底要问什么,,但我认为这是您要确保满足O(1)空间的要求,而与时间复杂度无关,< / strong>,这就是我要尝试的答案。

具有数组,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)

答案 4 :(得分:0)

您需要有2-3个指针(索引)。 i:下一个独特元素将放在此位置 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++;
    }
}

答案 5 :(得分:0)

这是C代码,它将重复的字符串放在数组的最后。 该指标数组用于指示在其中重复字符串的索引。 即:如果s [0] == s [1],则指标[1]将被分配为0,因为在该索引处字符串会重复。 然后使用指标数组将重复的字符串交换到数组中最后一个有效位置。

ie:如果我们发现indicator [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]);
}

答案 6 :(得分:0)

如果我们不关心数组中重复元素的稳定性和排序性,可以使用单个指针和另一个指针来找到下一个最大值。

算法

  • 启动一个指针并遍历数组,只要元素 您所在的位置大于上一个,小于下一个
  • 一旦看到此模式中断,就停止增量并找到一个大于当前数字的数字
  • 用下一个更大的数字替换这个数字。
  • 继续搜索,直到找不到数组中更大的数字
  • 如果达到此条件,请中断循环并返回数组
public static void main(String[] args) {
        // TODO Auto-generated method stub              
        int[] arr = {11, 12, 12, 13, 14, 14, 14, 14,  15};
        rearrangeSort(arr);     
        for(int a : arr) {
            System.out.print(a + " ");
        }       
    }   
    public static void rearrangeSort(int[] arr){
        int unique = 1;
        int find = 0;
        while(unique < arr.length) {
            if(unique == 1 && (arr[unique - 1] == arr[unique])){
                find = findMax(arr, arr[unique], unique);
                swap(arr, unique, find);                
            }else if(unique == 1 && (arr[unique] == arr[unique + 1])){
                find = findMax(arr, arr[unique], unique);
                swap(arr, unique + 1, find);                
            }           
            if(unique > 0 && (arr[unique - 1] < arr[unique]) && (arr[unique] < arr[unique + 1])){
                unique++;
            }
            find = findMax(arr, arr[unique], unique);           
            if(find == 0) {break;}
            swap(arr, unique+1, find);
        }                   
    }       
    public static int findMax(int[] arr, int target, int index){
        while(index < arr.length) {
            if(arr[index] > target) {return index;}
            index++;
        }
        return 0;
    }       
    public static void swap(int[] arr, int idx1, int idx2){
        int temp = arr[idx1];
        arr[idx1] = arr[idx2];
        arr[idx2] = temp;       
    }
}