Obróć tablicę liczb całkowitych za pomocą algorytmu O (n) [zamknięty]


10

Napisz funkcję, która obraca tablicę liczb całkowitych o podaną liczbę k. k elementów od końca powinno przejść na początek tablicy, a wszystkie inne elementy powinny przejść w prawo, aby zrobić miejsce.

Rotacja powinna odbywać się na miejscu.

Algorytm nie powinien działać w więcej niż O (n), gdzie n jest rozmiarem tablicy.

Do wykonania operacji należy również użyć stałej pamięci.

Na przykład,

jeśli tablica jest inicjowana z elementami arr = {1, 2, 3, 4, 5, 6, 7, 8, 9}

rotate (arr, 3) spowoduje, że elementami będą {7, 8, 9, 1, 2, 3, 4, 5, 6}

rotate (arr, 6) spowoduje {4, 5, 6, 7, 8, 9, 1, 2, 3}


1
Co oznacza tutaj ciągła pamięć? Z pewnością wymaga co najmniej O (n) pamięci przynajmniej do przechowywania przetwarzanej tablicy, co uniemożliwia wykorzystanie pamięci O (1) .
Ad Hoc Garf Hunter

2
Głosuję za zamknięciem tego pytania jako nie na temat, ponieważ pytania bez obiektywnego podstawowego kryterium wygranej są nie na temat, ponieważ uniemożliwiają bezdyskusyjną decyzję, która pozycja powinna wygrać. Nie ma absolutnie żadnego powodu, aby był to konkurs popularności.
James

Zagłosowano zamknąć. Z wiki konkursu popularności ( tutaj ): „Daje uczestnikom swobodę decydowania o tym, co robić w kluczowych częściach i zachęca ich do korzystania z tej wolności”. Nie sądzę, aby pozostawienie wyzwania otwartemu na dowolny algorytm liczyło się jako zachęcanie do kreatywności w tak prostym wyzwaniu, przynajmniej nie w takim stopniu, w jakim działa on jako popcon. Byłoby to bardziej odpowiednie jako wyzwanie do gry w golfa .
mbomb007,

Odpowiedzi:


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

Zminimalizowane:

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
Powinieneś napisać warunek pętli while jakoa <-- b
justhalf

Kiedyś programy C wygrywały konkursy popularności ...
Anubian Noob

Jesteście najlepsi! Jak elegancki i zoptymalizowany. Czy możesz to zrobić za pomocą tablicy bitów?

9

APL (4)

¯A⌽B
  • A to liczba miejsc do obrócenia
  • B to nazwa tablicy, która ma zostać obrócona

Nie jestem pewien, czy APL faktycznie tego wymagało, ale w implementacji, którą widziałem (elementy wewnętrzne), zajęłoby to czas proporcjonalny do Astałej pamięci.


+1, gdyby to był golf :)
Glenn Teitelbaum

Nie robi tego jednak na miejscu.
marinus

@marinus: Z pewnością robi to we wdrożeniach, które widziałem.
Jerry Coffin

Jak to jest funkcja? Może być {⍵⌽⍨-⍺}lub {⌽⍺⌽⌽⍵}. W NARS2000 może być elegancko napisany jako ⌽⍢⌽.
Adám,

5

Oto długo rozwinięta wersja C pomysłu Colina.

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

To nie wygląda na stałe rozwiązanie pamięci, prawda?
mikrobian

Tak, jest to stałe rozwiązanie pamięci. „Malloced” rzeczy to tymczasowa kopia tablicy, dzięki czemu mogę kopiować do niej oryginalne dane raz za razem, dzięki czemu mogę testować różne wielkości obrotu.
Stephen Montgomery-Smith

Rzeczywistym obrotem jest funkcja „obracaj”. Używa 5 liczb całkowitych i dwóch podwójnych. Wywołuje także funkcję „gcd”, która używa jednej liczby całkowitej i używa co najwyżej operacji O (log (n)).
Stephen Montgomery-Smith

Rozumiem. Podniosłem twoją odpowiedź.
mikrob

@ StephenMontgomery-Smith - jak to O(log(n))działa? Spójrz na bybycie 1, twoja pętla `j 'to s_arr / g lub N - to operacje O (N)
Glenn Teitelbaum

3

do

Nie jestem pewien, jakie są kryteria, ale ponieważ bawiłem się algorytmem, oto mój wpis:

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

Dla pewności zagram w golfa; 126 bajtów, można skrócić:

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

Nie widzę tu zbyt wielu rozwiązań C ++, więc pomyślałem, że spróbuję tego, ponieważ nie liczy znaków.

Jest to prawdziwa rotacja „na miejscu”, dlatego wykorzystuje 0 dodatkowego miejsca (oprócz wymiany technicznej i 3 liczb całkowitych), a ponieważ pętla ma dokładnie N, spełnia również złożoność 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
        }
}

Uwaga: Celowo nie korzystałem, std::rotateponieważ ten rodzaj pokonuje cel
Glenn Teitelbaum

2

Jeśli wykonasz każdy z możliwych cykli obrotu kolejno o n (jest ich GCD (n, len (arr))), to potrzebujesz tylko jednej tymczasowej kopii elementu tablicy i kilku zmiennych stanu. W ten sposób w Pythonie:

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
Myślę, że masz dobry pomysł, ale twoja cyclezmienna ma niestały rozmiar. Będziesz musiał wygenerować tę tablicę podczas podróży.
Keith Randall

2

C (137 znaków)

#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");
}

Funkcja rotatezmniejszona do 137 znaków:

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

Współczynnik ma wbudowany typ dla tablic obrotowych <circular>, więc w rzeczywistości jest to operacja 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 }

Mniej cheatyczna Factor odpowiednik imponującego rozwiązania C Bena Voigta:

: 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

Poszedłem na golfa, bo lubię golfa. Ma maksymalną wartość O (N), o ile t<= rozmiar tablicy.

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

Aby obsłużyć tdowolną proporcję w O (N), można zastosować następujące elementy (o wadze 58 znaków):

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

Nie zwraca, edytuje tablicę w miejscu.


1
+1 zar(o,t) => rot
Conor O'Brien

1

REBEL - 22

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

Dane wejściowe: k wyrażone jako jedność całkowita za _pomocą cyfry, następnie spacja, a następnie rozdzielana spacjami tablica liczb całkowitych.

Dane wyjściowe: spacja, a następnie tablica obrócona.

Przykład:

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

Stan końcowy:

 3 4 5 1 2

Wyjaśnienie:

W każdej iteracji, zastępuje jeden _i tablicę [array] + tailz tail + [array].

Przykład:

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

Nie sądzę, że to O (n). Kopiowanie tablicy jest O(n)i robisz to nrazy.
Ben Voigt

1

Jawa

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++;
    }
}

Demo tutaj .

Minified 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

W rzeczywistości jest to θ (n), ponieważ podział to θ (k), a złączenie to θ (nk). Nie jestem jednak pewien co do pamięci.

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

Stała
złożoność czasowa pamięci O (n)



0

pyton

   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

Kopiowanie tablicy nie jest stałą przestrzenią. @ Odpowiedź MadisonMay robi w zasadzie to samo, co ten kod z wieloma mniejszymi znakami.
Blckknght

0

vb.net O (n) (nie pamięć stała)

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

Rubin

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

0

C (118)

Prawdopodobnie był nieco zbyt łagodny w przypadku niektórych specyfikacji. Wykorzystuje pamięć proporcjonalną do shift % length. Może również obracać się w przeciwnym kierunku, jeśli minie ujemna wartość przesunięcia.

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]

Gdybym tylko l[-n:len(l)-n]działał tak, jak bym tego oczekiwał. Po prostu wraca []z jakiegoś powodu.


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

Czy ktoś mógłby sprawdzić, czy to rzeczywiście spełnia wymagania? Myślę, że tak, ale nie studiowałem jeszcze 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

Jawa

Zamień ostatnie k elementów na pierwsze k elementów, a następnie obróć pozostałe elementy o k. Jeśli na końcu pozostało mniej niż k elementów, obróć je o k% liczby pozostałych elementów. Nie sądzę, żeby ktokolwiek powyżej przyjął to podejście. Wykonuje dokładnie jedną operację wymiany dla każdego elementu, robi wszystko na swoim miejscu.

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 bajtów

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

Wypróbuj online!

Podprogram wykorzystuje obrót jako pierwszy parametr, a odniesienie do tablicy jako drugi. Czas pracy jest stały na podstawie odległości obrotu. Rozmiar tablicy nie wpływa na czas działania. Tablica jest modyfikowana na miejscu poprzez usunięcie elementu z prawej strony i umieszczenie go po lewej stronie.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.