Обертати цілий масив з алгоритмом O (n) [закрито]


10

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

Обертання слід робити на місці.

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

Також для виконання операції повинна використовуватися постійна пам'ять.

Наприклад,

якщо масив ініціалізований елементами arr = {1, 2, 3, 4, 5, 6, 7, 8, 9}

обернути (arr, 3) призведе до того, що елементи будуть {7, 8, 9, 1, 2, 3, 4, 5, 6}

повернути (arr, 6) призведе до {4, 5, 6, 7, 8, 9, 1, 2, 3}


1
Що мається на увазі під постійною пам’яттю тут? Безумовно, йому потрібно як мінімум O (n) пам'яті як мінімум просто для збереження масиву, який обробляється, що робить неможливим використання O (1) пам'яті.
Ad Hoc Garf Hunter

2
Я голосую за те, щоб закрити це питання поза темою, оскільки питання без об'єктивного первинного критерію виграшу є поза темою, оскільки вони унеможливлюють безперечне вирішення, який запис має бути виграним. Немає абсолютно ніяких причин, щоб це був конкурс на популярність.
Джеймс

Проголосували за закриття. З вікі -конкурсу на популярність ( тут ) "Надає абітурієнтам свободу вирішувати, що робити у вирішальних частинах, та стимулює їх використовувати цю свободу". Я не думаю, що залишення виклику відкритим для будь-якого алгоритму не сприймає творчості для такого простого виклику, принаймні, не настільки, наскільки він працює як попкон. Це більше підходило б як виклик коду-гольфу .
mbomb007

Відповіді:


18

C (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
Ви повинні були записати умову циклу whilea <-- b
justhalf

Був час, коли програми C вигравали конкурси на популярність ...
Anubian Noob

Ти найкращий! Як елегантно та оптимізовано. Чи могли б ви це зробити за допомогою бітового масиву?

9

APL (4)

¯A⌽B
  • A - кількість місць, які потрібно обертати
  • B - ім'я масиву, який потрібно обертати

Я не впевнений, чи дійсно цього вимагав APL, але в реалізації я бачив (внутрішні), що це потребує часу, пропорційного Aта постійної пам'яті.


+1, якби це був гольф :)
Глен Тейтельбаум

Це не робить це на місці.
marinus

@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);
  }
}

Це не схоже на рішення постійної пам'яті, чи не так?
мікробій

Так, це рішення постійної пам'яті. "Недостатній" матеріал - це тимчасова копія масиву, щоб я міг копіювати в нього вихідні дані знову і знову, щоб я міг перевірити наявність різної кількості обертання.
Стівен Монтгомері-Сміт

Фактичне обертання означає функцію "повернути". Він використовує 5 цілих чисел і два парних. Він також називає функцію "gcd", яка використовує одне ціле число, і використовує не більше операцій O (log (n)).
Стівен Монтгомері-Сміт

Зрозумів. Я підвищив вашу відповідь.
мікробій

@ StephenMontgomery-Smith - як це відбувається O(log(n)). Подивіться, byщо це 1, ваш цикл `j 's_arr / g або N - це операції O (N)
Glenn Teitelbaum

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тому що такий вид перемагає мету
Glenn Teitelbaum

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 }

Менш обманливий фактор, еквівалентний вражаючому рішенням Бен Вогта:

: 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
Conor O'Brien

1

РЕБЕЛЬ - 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

Java

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

Хаскелл

Це насправді θ (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

Пітон 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

Копіювання масиву не є постійним простором. @ Відповідь МедісонМей робить те саме, що і цей код із значно меншою кількістю символів.
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

C (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

Пітон 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

C ++, 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

Java

Замініть останні 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.