Повернуть массив целых чисел с помощью алгоритма O (n) [закрыто]


10

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

Вращение должно быть сделано на месте.

Алгоритм не должен работать больше, чем O (n), где n - размер массива.

Также постоянная память должна использоваться для выполнения операции.

Например,

если массив инициализируется с элементами arr = {1, 2, 3, 4, 5, 6, 7, 8, 9}

rotate (arr, 3) приведет к тому, что элементы будут {7, 8, 9, 1, 2, 3, 4, 5, 6}

rotate (arr, 6) приведет к {4, 5, 6, 7, 8, 9, 1, 2, 3}


1
Что подразумевается под постоянной памятью здесь? Конечно, для хранения обрабатываемого массива требуется как минимум O (n) памяти, что делает невозможным использование O (1) памяти.
Специальный охотник за

2
Я голосую за то, чтобы закрыть этот вопрос как не по теме, потому что вопросы без объективного критерия первичного выигрыша являются не по теме, так как не позволяют безоговорочно решить, какая статья должна выиграть. Нет абсолютно никакой причины, что это должен быть конкурс популярности.
Джеймс

Проголосовал за закрытие. На вики -конкурсе популярности ( здесь ) «Дает свободу участникам самим решать, что делать в критически важных частях, и стимулирует их использовать эту свободу». Я не думаю, что оставление задачи открытым для любого алгоритма считается поощрением творчества для такой простой задачи, по крайней мере, не в той степени, в которой он работает как popcon. Это было бы более подходящим как вызов кода гольф .
mbomb007

Ответы:


18

С (104)

void reverse(int* a, int* b)
{
    while (--b > a) {
        *b ^= *a;
        *a ^= *b;
        *b ^= *a;
        ++a;
    }
}

void rotate(int *arr, int s_arr, int by)
{
    reverse(arr, arr+s_arr);
    reverse(arr, arr+by);
    reverse(arr+by, arr+s_arr);
}

уменьшенная:

v(int*a,int*b){while(--b>a){*b^=*a;*a^=*b;*b^=*a++;}}r(int*a,int s,int y){v(a,a+s);v(a,a+y);v(a+y,a+s);}

4
Вы должны быть написаны условие цикла пока какa <-- b
justhalf

Было время, когда C-программы выигрывали конкурсы популярности ...
Anubian Noob

Вы самые лучшие! Как элегантно и оптимизировано. Не могли бы вы сделать это с битовым массивом?

9

APL (4)

¯A⌽B
  • A - количество мест для поворота
  • B - имя массива, который нужно повернуть

Я не уверен, действительно ли APL требовал этого, но в реализации, которую я видел (внутренности), это потребовало бы времени, пропорционального Aи постоянной памяти.


+1, если бы это был гольф :)
Гленн Тейтельбаум

Это не делает это на месте, хотя.
Маринус

@marinus: Конечно, это происходит в тех реализациях, которые я видел.
Джерри Коффин

Как это функция? Может быть {⍵⌽⍨-⍺}или {⌽⍺⌽⌽⍵}. В NARS2000 это может быть изящно написано как ⌽⍢⌽.
Адам

5

Вот длинная версия C-версии идеи Колина.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int gcd(int a, int b) {
  int t;
  if (a < b) {
    t = b; b = a; a = t;
  }
  while (b != 0) {
    t = a%b;
    a = b;
    b = t;
  }
  return a;
}

double arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
int s_arr = sizeof(arr)/sizeof(double);

/* We assume 1 <= by < s_arr */
void rotate(double *arr, int s_arr, int by) {
  int i, j, f;
  int g = gcd(s_arr,by);
  int n = s_arr/g;
  double t_in, t_out;

  for (i=0; i<g; i++) {
    f = i;
    t_in = arr[f + s_arr - by];
    for (j=0; j<n; j++) {
      t_out = arr[f];
      arr[f] = t_in;
      f = (f + by) % s_arr;
      t_in = t_out;
    }
  }
}

void print_arr(double *arr, int s_arr) {
  int i;
  for (i=0; i<s_arr; i++) printf("%g ",arr[i]);
  puts("");
}

int main() {
  double *temp_arr = malloc(sizeof(arr));
  int i;

  for (i=1; i<s_arr; i++) {
    memcpy(temp_arr, arr, sizeof(arr));
    rotate(temp_arr, s_arr, i);
    print_arr(temp_arr, s_arr);
  }
}

Это не похоже на постоянное решение для памяти, не так ли?
микробиан

Да, это постоянное решение для памяти. «Malloced» - это временная копия массива, так что я могу снова и снова копировать исходные данные в нее, чтобы я мог проверять различные значения поворота.
Стивен Монтгомери-Смит

Что делает фактическое вращение, так это функция «вращение». Он использует 5 целых и два двойных. Он также вызывает функцию «gcd», которая использует одно целое число и использует не более O (log (n)) операций.
Стивен Монтгомери-Смит

Понял. Я повысил ваш ответ.
микробиан

@ StephenMontgomery-Smith - как это O(log(n))операции. Посмотрите на by1, ваш цикл `j 's_arr / g или N - это O (N) операций
Гленн Тейтельбаум

3

С

Не уверен, каковы критерии, но так как я получил удовольствие от алгоритма, вот моя запись:

void rotate(int* b, int size, int shift)
{
    int *done;
    int *p;
    int i;
    int saved;
    int c;

    p = b;
    done = p;
    saved = *p;
    for (i = 0; i < size; ++i) {
        c = saved;
        p += shift;
        if (p >= b+size) p -= size;
        saved = *p;
        *p = c;
        if (p == done) {
            p += 1;
            done = p;
            saved = *p;
        }
    }
}

Я тоже буду играть в гольф для хорошей меры; 126 байт, могут быть сокращены:

void r(int*b,int s,int n){int*d,*p,i,t,c;d=p=b;t=*p;for(i=0;i<s;++i){c=t;p+=n;if(p>=b+s)p-=s;t=*p;*p=c;if(p==d){d=++p;t=*p;}}}

3

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

Это действительно вращение «на месте», поэтому используется 0 лишних пробелов (кроме технически подкачки и 3 дюймов), и поскольку цикл равен ровно N, он также удовлетворяет сложности O (N).

template <class T, size_t N>
void rot(std::array<T,N>& x, int shift)
{
        size_t base=0;
        size_t cur=0; 
        for (int i = 0; i < N; ++i)
        {
                cur=(cur+shift)%N; // figure out where we are going
                if (cur==base)     // exact multiple so we have to hit the mods when we wrap
                {
                        cur++;
                        base++;
                }
                std::swap(x.at(base), x.at(cur)); // use x[base] as holding area
        }
}

Примечание: я намеренно не использовал, std::rotateпотому что такого рода поражение цели
Гленн Тейтельбаум

2

Если вы выполняете каждый из возможных циклов поворотов по n (по очереди их GCD (n, len (arr))), то вам потребуется только одна временная копия элемента массива и несколько переменных состояния. Вот так в Python:

from fractions import gcd

def rotate(arr, n):
    total = len(arr)
    cycles = gcd(n, total)
    for start in range(0, cycles):
        cycle = [i % total for i in range(start, abs(n * total) / cycles, n)]
        stash = arr[cycle[-1]]
        for j in reversed(range(1, len(cycle))):
            arr[cycle[j]] = arr[cycle[j - 1]]
        arr[cycle[0]] = stash

1
Я думаю, у вас есть правильная идея, но ваша cycleпеременная имеет непостоянный размер. Вы должны будете сгенерировать этот массив, как вы идете.
Кит Рэндалл

2

C (137 символов)

#include <stdio.h>

void rotate(int * array, int n, int k) {
    int todo = (1<<n+1)-1;
    int i = 0, j;
    int tmp = array[0];

    while (todo) {
        if (todo & 1<<i) {
            j = (i-k+n)%n;
            array[i] = todo & 1<<j ? array[j] : tmp;
            todo -= 1<<i;
            i = j;
        } else tmp = array[++i];
    }
}

int main() {
    int a[] = {1,2,3,4,5,6,7,8,9};
    rotate(a, 9, 4);
    for (int i=0; i<9;i++) printf("%d ", a[i]);
    printf("\n");
}

Функция rotateуменьшена до 137 символов:

void r(int*a,int n,int k){int m=(1<<n+1)-1,i=0,j,t=a[0];while(m)if(m&1<<i){j=(i-k+n)%n;a[i]=(m&1<<j)?a[j]:t;m-=1<<i;i=j;}else t=a[++i];}

2

Фактор имеет встроенный тип для вращающихся массивов <circular>, так что на самом деле это операция O (1):

: rotate ( circ n -- )
    neg swap change-circular-start ;

IN: 1 9 [a,b] <circular> dup 6 rotate >array .
{ 4 5 6 7 8 9 1 2 3 }
IN: 1 9 [a,b] <circular> dup 3 rotate >array .
{ 7 8 9 1 2 3 4 5 6 }

Менее обманчивый фактор-фактор впечатляющего C-решения Бена Фойгта:

: rotate ( n s -- ) 
    reverse! swap cut-slice [ reverse! ] bi@ 2drop ;

IN: 7 V{ 0 1 2 3 4 5 6 7 8 9 } [ rotate ] keep .
V{ 3 4 5 6 7 8 9 0 1 2 }

2

JavaScript 45

В любом случае ходил в гольф, потому что я люблю гольф. Максимальное значение O (N) tравно <= размеру массива.

function r(o,t){for(;t--;)o.unshift(o.pop())}

Для обработки tлюбой пропорции в O (N) можно использовать следующее (весом до 58 символов):

function r(o,t){for(i=t%o.length;i--;)o.unshift(o.pop())}

Не возвращает, редактирует массив на месте.


1
+1 заr(o,t) => rot
Конор О'Брайен

1

REBEL - 22

/_(( \d+)+)( \d+)/$3$1

Входные данные: k выражается как унарное целое число, используя _цифру, затем пробел, затем массив целых чисел, разделенных пробелом.

Выход: пробел, затем массив повернут.

Пример:

___ 1 2 3 4 5/_(( \d+)+)( \d+)/$3$1

Конечное состояние:

 3 4 5 1 2

Объяснение:

На каждой итерации он заменяет единицу _и массив [array] + tailна tail + [array].

Пример:

___ 1 2 3 4 5
__ 5 1 2 3 4
_ 4 5 1 2 3
 3 4 5 1 2

Я не думаю, что это O (N). Копирование массива есть O(n), и вы делаете это nраз.
Бен Фойгт

1

Ява

public static void rotate(int[] arr, int by) {
    int n = arr.length;
    int i = 0;
    int j = 0;
    while (i < n) {
        int k = j;
        int value = arr[k];
        do {
            k = (k + by) % n;
            int tmp = arr[k];
            arr[k] = value;
            value = tmp;
            i++;
        } while (k != j);
        j++;
    }
}

Демо здесь .

Минимизированный Javascript, 114 :

function rotate(e,r){n=e.length;i=0;j=0;while(i<n){k=j;v=e[k];do{k=(k+r)%n;t=e[k];e[k]=v;v=t;i++}while(k!=j);j++}}

1

Haskell

На самом деле это θ (n), поскольку разбиение - θ (k), а объединение - θ (nk). Не уверен насчет памяти, хотя.

rotate 0 xs = xs
rotate n xs | n >= length xs = rotate (n`mod`(length xs)) xs
            | otherwise = rotate' n xs

rotate' n xs = let (xh,xt) = splitAt n xs in xt++xh

1

Python 3

from fractions import gcd
def rotatelist(arr, m):
    n = len(arr)
    m = (-m) % n # Delete this line to change rotation direction
    for i0 in range(gcd(m, n)):
        temp = arr[i0]
        i, j = i0, (i0 + m) % n
        while j != i0:
            arr[i] = arr[j]
            i, j = j, (j + m) % n
        arr[i] = temp

Постоянная память
O (n) временной сложности



0

питон

   import copy
    def rotate(a, r):
        c=copy.copy(a);b=[]
        for i in range(len(a)-r):   b.append(a[r+i]);c.pop();return b+c

Копирование массива не является постоянным пространством. Ответ @ MadisonMay делает то же самое, что и этот код, с гораздо меньшим количеством символов.
Blckknght

0

vb.net O (n) (не постоянная память)

Function Rotate(Of T)(a() As T, r As Integer ) As T()     
  Dim p = a.Length-r
  Return a.Skip(p).Concat(a.Take(p)).ToArray
End Function

0

Рубин

def rotate(arr, n)
  arr.tap{ (n % arr.size).times { arr.unshift(arr.pop) } }  
end

0

С (118)

Вероятно, был слишком мягок с некоторыми спецификациями. Использует память, пропорциональную shift % length. Также может вращаться в противоположном направлении, если передается отрицательное значение сдвига.

r(int *a,int l,int s){s=s%l<0?s%l+l:s%l;int *t=malloc(4*s);memcpy(t,a+l-s,4*s);memcpy(a+s,a,4*(l-s));memcpy(a,t,4*s);}

0

Python 2, 57

def rotate(l,n):
 return l[len(l)-n:len(l)]+l[0:len(l)-n]

Если бы l[-n:len(l)-n]работал так, как я ожидал. Это просто возвращается []по какой-то причине.


0
def r(a,n): return a[n:]+a[:n]

Может кто-нибудь проверить, действительно ли это соответствует требованиям? Я думаю, что это так, но я не изучал CS (пока).


0

С ++, 136

template<int N>void rotate(int(&a)[N],int k){auto r=[](int*b,int*e){for(int t;--e>b;t=*b,*b++=*e,*e=t);};r(a,a+k);r(a+k,a+N);r(a,a+N);}

0

Ява

Поменяйте местами последние k элементов с первыми k элементами, а затем поверните оставшиеся элементы на k. Если в конце осталось менее k элементов, поверните их на k% оставшихся элементов. Я не думаю, что кто-то выше принял этот подход. Выполняет ровно одну операцию подкачки для каждого элемента, делает все на месте.

public void rotate(int[] nums, int k) {
    k = k % nums.length; // If k > n, reformulate
    rotate(nums, 0, k);
}

private void rotate(int[] nums, int start, int k) {
    if (k > 0) {
        if (nums.length - start > k) { 
            for (int i = 0; i < k; i++) {
                int end = nums.length - k + i;
                int temp = nums[start + i];
                nums[start + i] = nums[end];
                nums[end] = temp;
            }
            rotate(nums, start + k, k); 
        } else {
            rotate(nums, start, k % (nums.length - start)); 
        }
    }
}

0

Perl 5 , 42 байта

sub r{$a=pop;map{unshift@$a,pop@$a}1..pop}

Попробуйте онлайн!

Подпрограмма принимает расстояние для поворота в качестве первого параметра и ссылку на массив в качестве второго. Время выполнения является постоянным в зависимости от расстояния вращения. Размер массива не влияет на время выполнения. Массив изменяется на месте, удаляя элемент справа и помещая его слева.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.