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}

drobnoustrój
źródło
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);}
Ben Voigt
źródło
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.

Jerry Coffin
źródło
+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);
  }
}
Stephen Montgomery-Smith
źródło
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;}}}
treamur
źródło
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
        }
}
Glenn Teitelbaum
źródło
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
Colin Watson
źródło
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];}
craesh
źródło
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 }
Björn Lindqvist
źródło
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.

George Reith
źródło
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
Kendall Frey
źródło
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++}}
ValarDohaeris
źródło
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
archaephyrryx
źródło
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)

AMK
źródło
0

Pyton

def rotate(a, n): a[:n], a[n:] = a[-n:], a[:-n] 
Madison May
źródło
Czy to będzie używać stałej pamięci?
SztupY
Hmm ... nie jestem pewien.
Madison May
To nie jest ciągła operacja pamięci.
mikrob
Shucks Dobry telefon ...
Madison May
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
saykou
źródło
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
Adam Speight
źródło
0

Rubin

def rotate(arr, n)
  arr.tap{ (n % arr.size).times { arr.unshift(arr.pop) } }  
end
OI
źródło
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);}
cardinaliti
źródło
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.

cjfaure
źródło
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.

.ıʇǝɥʇuʎs
źródło
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);}
mattnewport
źródło
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)); 
        }
    }
}
Matthew Saltz
źródło
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.

Xcali
źródło