Xoay một mảng số nguyên với thuật toán O (n) [đã đóng]


10

Viết hàm xoay một mảng nguyên bằng một số k cho trước. k phần tử từ cuối sẽ di chuyển đến phần đầu của mảng và tất cả các phần tử khác sẽ di chuyển sang phải để tạo khoảng trống.

Việc quay vòng nên được thực hiện tại chỗ.

Thuật toán không nên chạy nhiều hơn O (n), trong đó n là kích thước của mảng.

Ngoài ra một bộ nhớ không đổi phải được sử dụng để thực hiện các hoạt động.

Ví dụ,

nếu mảng được khởi tạo với các phần tử Array = {1, 2, 3, 4, 5, 6, 7, 8, 9}

xoay (mảng, 3) sẽ dẫn đến các phần tử là {7, 8, 9, 1, 2, 3, 4, 5, 6}

xoay (mảng, 6) sẽ cho kết quả {4, 5, 6, 7, 8, 9, 1, 2, 3}


1
Có nghĩa là gì bởi bộ nhớ liên tục ở đây? Chắc chắn nó yêu cầu ít nhất bộ nhớ O (n) tối thiểu chỉ để lưu trữ mảng đang được xử lý khiến việc sử dụng bộ nhớ O (1) không thể thực hiện được.
Ad Hoc Garf Hunter

2
Tôi đang bỏ phiếu để đóng câu hỏi này ngoài chủ đề vì các câu hỏi không có tiêu chí chiến thắng chính khách quan là ngoài chủ đề, vì chúng không thể quyết định không thể chối cãi mục nào sẽ thắng. Hoàn toàn không có lý do mà đây nên là một cuộc thi phổ biến.
James

Bình chọn để đóng. Từ wiki cuộc thi phổ biến ( tại đây ), "Cung cấp tự do cho những người tham gia quyết định những việc cần làm trong những phần quan trọng và khuyến khích họ sử dụng quyền tự do này." Tôi không nghĩ rằng để lại thách thức cho bất kỳ thuật toán nào được coi là khuyến khích sự sáng tạo cho một thử thách đơn giản như vậy, ít nhất là không đến mức nó hoạt động như một popcon. Điều này sẽ phù hợp hơn như là một thử thách golf-code .
mbomb007

Câu trả lời:


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

Giảm thiểu:

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
Bạn nên viết điều kiện vòng lặp while làa <-- b
justhalf 6/11 '

Đã từng có thời gian các chương trình C giành chiến thắng trong các cuộc thi nổi tiếng ...
Anubian Noob

Bạn là nhất! Làm thế nào thanh lịch và tối ưu hóa .. Bạn có thể làm điều này với mảng bit?

9

APL (4)

¯A⌽B
  • A là số vị trí cần xoay
  • B là tên của mảng được xoay

Tôi không chắc chắn nếu APL thực sự yêu cầu nó, nhưng trong quá trình triển khai tôi đã thấy (phần bên trong), điều này sẽ mất thời gian tỷ lệ thuận Avà bộ nhớ không đổi.


+1 nếu đây là golf :)
Glenn Teitelbaum

Nó không làm điều đó tại chỗ mặc dù.
bến tàu

@marinus: Nó chắc chắn làm trong các triển khai tôi đã thấy.
Jerry Coffin

Làm thế nào đây là một chức năng? Có thể {⍵⌽⍨-⍺}hoặc {⌽⍺⌽⌽⍵}. Trong NARS2000 nó có thể được viết thanh lịch như ⌽⍢⌽.
Adám

5

Đây là một phiên bản C dài của ý tưởng của Colin.

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

Nó không giống như một giải pháp bộ nhớ liên tục, phải không?
microbian

Có nó là một giải pháp bộ nhớ không đổi. Công cụ "malloced" là một bản sao tạm thời của mảng để tôi có thể sao chép dữ liệu gốc vào nó nhiều lần, để tôi có thể kiểm tra các số vòng quay khác nhau.
Stephen Montgomery-Smith

Xoay thực tế là gì chức năng "xoay." Nó sử dụng 5 số nguyên và hai nhân đôi. Nó cũng gọi một hàm "gcd" sử dụng một số nguyên và sử dụng tối đa các hoạt động O (log (n)).
Stephen Montgomery-Smith

Hiểu rồi. Tôi đưa ra câu trả lời của bạn.
microbian

@ StephenMonteimery-Smith - O(log(n))hoạt động này như thế nào . Nhìn vào by1, vòng lặp `j 'của bạn là s_arr / g hoặc N - đây là hoạt động O (N)
Glenn Teitelbaum

3

C

Không chắc chắn các tiêu chí là gì, nhưng vì tôi đã vui với thuật toán, đây là mục của tôi:

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

Tôi cũng sẽ đánh golf nó cho một biện pháp tốt; 126 byte, có thể được thực hiện ngắn hơn:

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

Tôi không thấy rất nhiều giải pháp C ++ ở đây, vì vậy tôi đoán rằng tôi sẽ thử giải pháp này vì nó không tính các ký tự.

Đây là phép quay "tại chỗ" thực sự, vì vậy sử dụng 0 không gian thừa (ngoại trừ trao đổi kỹ thuật và 3 int) và vì vòng lặp chính xác là N, cũng đáp ứng độ phức tạp 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
        }
}

Lưu ý: Tôi cố tình không sử dụng std::rotatevì loại đó đánh bại mục đích
Glenn Teitelbaum

2

Nếu bạn thực hiện lần lượt từng chu kỳ quay có thể của n (có GCD (n, len (mảng)) trong số này, thì bạn chỉ cần một bản sao tạm thời của một phần tử mảng và một vài biến trạng thái. Như thế này, trong 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
Tôi nghĩ rằng bạn có ý tưởng đúng, nhưng cyclebiến của bạn có kích thước không đổi. Bạn sẽ phải tạo ra mảng này khi bạn đi.
Keith Randall

2

C (137 ký tự)

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

Chức năng rút rotategọn thành 137 ký tự:

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

Yếu tố có một loại tích hợp cho các mảng có thể xoay được <circular>, vì vậy đây thực sự là một hoạt động 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 }

Một yếu tố ít gian lận tương đương với giải pháp C ấn tượng của Ben Voigt:

: 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

Đã đi chơi golf vì tôi thích golf. Nó ở mức tối đa O (N) miễn tlà <= kích thước của mảng.

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

Để xử lý tbất kỳ tỷ lệ nào trong O (N), có thể sử dụng các giá trị sau (cân bằng 58 ký tự):

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

Không trả về, chỉnh sửa mảng tại chỗ.


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

1

REBEL - 22

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

Dữ liệu vào: k được biểu thị dưới dạng một số nguyên đơn sử dụng _dưới dạng một chữ số, theo sau là khoảng trắng, sau đó là một mảng các số nguyên được phân tách bằng dấu cách.

Đầu ra: Một khoảng trắng, sau đó mảng xoay.

Thí dụ:

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

Trạng thái cuối cùng:

 3 4 5 1 2

Giải trình:

Ở mỗi lần lặp, nó thay thế một _và một mảng [array] + tailbằng tail + [array].

Thí dụ:

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

Tôi không nghĩ đây là O (n). Sao chép một mảng là O(n), và bạn làm điều đó nlần.
Ben Voigt

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

Demo tại đây .

Javascript tối thiểu , 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

Đây thực sự là θ (n), vì phần tách là θ (k) và phép nối là θ (nk). Không chắc chắn về bộ nhớ mặc dù.

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

Con trăn 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

Bộ nhớ không đổi
độ phức tạp thời gian O (n)



0

trăn

   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

Sao chép mảng không phải là không gian không đổi. Câu trả lời của @ MadisonMay về cơ bản giống như mã này với nhiều ký tự hơn.
Blckknght

0

vb.net O (n) (không phải bộ nhớ liên tục)

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

Hồng ngọc

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

0

C (118)

Có lẽ là một chút quá khoan dung với một số thông số kỹ thuật. Sử dụng bộ nhớ tỷ lệ thuận với shift % length. Cũng có thể xoay theo hướng ngược lại nếu giá trị dịch chuyển âm được thông qua.

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

Con trăn 2, 57

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

Nếu chỉ l[-n:len(l)-n]làm việc như tôi mong đợi nó. Nó chỉ trở lại []vì một số lý do.


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

Ai đó có thể vui lòng kiểm tra nếu điều này thực sự đáp ứng các yêu cầu? Tôi nghĩ là có, nhưng tôi chưa nghiên cứu về CS (chưa).


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

Hoán đổi các phần tử k cuối cùng với các phần tử k đầu tiên, sau đó xoay các phần tử còn lại bằng k. Khi bạn có ít hơn k phần tử còn lại ở cuối, hãy xoay chúng theo k% số phần tử còn lại. Tôi không nghĩ bất cứ ai ở trên đã thực hiện phương pháp này. Thực hiện chính xác một thao tác hoán đổi cho mọi yếu tố, thực hiện mọi thứ tại chỗ.

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 byte

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

Hãy thử trực tuyến!

Chương trình con lấy khoảng cách để xoay làm tham số đầu tiên và tham chiếu đến mảng là tham số thứ hai. Thời gian chạy không đổi dựa trên khoảng cách của vòng quay. Kích thước mảng không ảnh hưởng đến thời gian chạy. Mảng được sửa đổi tại chỗ bằng cách loại bỏ một phần tử từ bên phải và đặt nó ở bên trái.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.