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}이됩니다.

회전 (arr, 6)은 {4, 5, 6, 7, 8, 9, 1, 2, 3}


1
지속적인 기억이란 무엇입니까? 확실히 O (1) 메모리 사용을 불가능하게 만드는 처리되는 어레이를 저장하기 위해 최소한 O (n) 메모리 가 필요합니다 .
Ad Hoc Garf Hunter 17 년

2
객관적인 주요 당첨 기준이없는 질문은 주제를 벗어난 주제이므로 논란의 여지가 없습니다. 이것이 인기 콘테스트가되어야 할 이유는 전혀 없습니다.
James

결선 투표. 로부터 인기-대회 위키 ( 여기 ), "중요한 부분에 무엇을 결정하는 참가자에게 자유를 부여하고,이 자유를 사용하도록 인센티브를." 나는 어떤 알고리즘에 도전을 열어 두는 것이 최소한 팝콘으로 작동하는 정도가 아니라 단순한 도전에 대한 창의성을 장려한다고 생각하지 않습니다. 이것은 코드 골프 도전에 더 적합 할 것 입니다.
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
while 루프 조건을 다음과 같이 작성해야합니다.a <-- b
Justhalf

C 프로그램이 인기 콘테스트에서 우승 한
적이있었습니다

너는 최고야! 얼마나 우아하고 최적화되어 있습니까? 비트 배열로이 작업을 수행 할 수 있습니까?

9

APL (4)

¯A⌽B
  • A는 회전 할 자리 수입니다
  • B는 회전 할 배열의 이름입니다

APL이 실제로 그것을 요구했는지는 확실하지 않지만 구현에서 (내부) 보았을 때 시간이 걸리고 A메모리가 일정하다는 것을 알았습니다 .


이 경우 한 골프 :)했다
글렌 Teitelbaum

그래도 그것을하지 않습니다.
marinus

@ marinus : 내가 본 구현에서 확실히합니다.
Jerry Coffin

이것이 어떻게 기능입니까? {⍵⌽⍨-⍺}또는 일 수 있습니다 {⌽⍺⌽⌽⍵}. NARS2000에서는 다음과 같이 우아하게 작성 될 수 있습니다 ⌽⍢⌽.
Adám

5

Colin의 아이디어에 대한 긴 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);
  }
}

일정한 메모리 솔루션처럼 보이지 않습니까?
microbian

예, 지속적인 메모리 솔루션입니다. "malloced"항목은 배열의 임시 복사본이므로 원본 데이터를 반복해서 복사 할 수 있으므로 다른 회 전량을 테스트 할 수 있습니다.
Stephen Montgomery-Smith

실제 회전은 "회전"기능입니다. 정수 5 개와 복식 2 개를 사용합니다. 또한 하나의 정수를 사용하고 최대 O (log (n)) 연산을 사용하는 "gcd"함수를 호출합니다.
Stephen Montgomery-Smith

알았다. 나는 당신의 대답을 올렸습니다.
microbian

@ StephenMontgomery-Smith-이 O(log(n))작업 은 어떻습니까 . 봐 by1 인은 해당 'J'루프는 s_arr / g 또는 N -이 O (N) 작업 인
글렌 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 ++ 솔루션을 보지 못하므로 문자를 계산하지 않기 때문에 시도해 볼 것이라고 생각했습니다.

이것은 "인플레 이스 (in-place)"회전이므로, 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))), 배열 요소의 단일 임시 사본과 몇 가지 상태 변수 만 필요합니다. 파이썬에서 이렇게 :

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변수의 크기는 일정하지 않습니다. 가면서이 배열을 생성해야합니다.
Keith Randall

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

rotate137 자로 축소 된 기능 :

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

Factor에는 회전 가능한 배열에 대한 내장 유형 <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 }

Ben Voigt의 인상적인 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

자바 스크립트 45

나는 골프를 좋아하기 때문에 어쨌든 골프에 갔다. t배열의 <= 크기 인 한 최대 O (N) 입니다.

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

tO (N)의 비율 을 처리하기 위해 다음을 사용할 수 있습니다 (무게 58 자).

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

반환하지 않고 배열을 편집합니다.


1
–1r(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시간.
Ben Voigt

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

여기 데모 .

축소 된 자바 스크립트, 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

하스켈

분할이 θ (k)이고 결합이 θ (nk)이므로 실제로는 θ (n)입니다. 그래도 메모리는 확실하지 않습니다.

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

배열을 복사하는 것은 일정한 공간이 아닙니다. @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

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

자바

마지막 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

펄 5 , 42 바이트

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

온라인으로 사용해보십시오!

서브 루틴은 거리를 첫 번째 매개 변수로 회전하고 배열을 두 번째 매개 변수로 참조합니다. 작동 시간은 회전 거리에 따라 일정합니다. 배열 크기는 런타임에 영향을 미치지 않습니다. 배열은 오른쪽에서 요소를 제거하고 왼쪽에 배치하여 제자리에 수정됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.