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}になる結果になります

rotate(arr、6)は、{4、5、6、7、8、9、1、2、3}になります。


1
ここで定数メモリとはどういう意味ですか?確かに、処理中の配列を格納するためだけに少なくともO(n)メモリが必要で、O(1)メモリの使用が不可能になります。
アドホックガーフハンター2017

2
客観的な主要な勝利基準のない質問はトピックから外れるので、このエントリを勝つべきかどうかを明確に決定することができないため、私はこの質問をトピック外として締めくくります。これが人気コンテストになる理由は全くありません。
ジェームズ

閉鎖に投票しました。以下からの人気コンテストのウィキ(ここでは)、「重要な部分で何をすべきかを決定するために参加者に自由を与え、この自由を使用するように奨励します。」アルゴリズムにチャレンジを開放したままにしておくことは、少なくともポプコンとして機能するほどではないので、そのような単純なチャレンジの創造性を奨励するものとは見なしません。これは、コードゴルフの課題としてより適しています
mbomb007 2017

回答:


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プログラムが人気コンテストに勝った時がありました...
Anubian Noob

あなたは最高です!どのようにエレガントで最適化されていますか?これをビット配列で実行できますか?

9

APL(4)

¯A⌽B
  • Aは回転する場所の数です
  • Bは回転する配列の名前です

APLが実際にそれを必要とするかどうかはわかりませんが、私が見た実装(の内部)では、これに比例した時間がかかりA、一定のメモリが必要です。


これがゴルフの場合は+1 :)
Glenn Teitelbaum 2014年

それはその場でそれをしません。
マリナス2014年

@marinus:私が見た実装では確かにそうです。
Jerry Coffin

これは関数ですか?{⍵⌽⍨-⍺}またはでした{⌽⍺⌽⌽⍵}。NARS2000では、エレガントにと書くことができます⌽⍢⌽
アダム

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つのdoubleを使用します。また、1つの整数を使用する関数「gcd」を呼び出し、最大でO(log(n))操作を使用します。
Stephen Montgomery-Smith

とった。私はあなたの答えを上げました。
microbian

@ StephenMontgomery-Smith-このO(log(n))操作はどうですか?見てby1であること、あなたの`J」ループはs_arr / gであるか、N -これはO(N)操作である
グレンTeitelbaum

3

C

基準が何であるかはわかりませんが、アルゴリズムを楽しんだので、これが私のエントリです。

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 intを除く)。ループが正確に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敗北のようなものので、目的を
グレンTeitelbaum

2

可能な回転の各サイクルを順番に実行する場合(これらの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変数は非定数サイズです。この配列は作成するときに作成する必要があります。
キースランドール2014年

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ソリューションに相当する、ごまかしが少ないFactor:

: 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

とにかくゴルフが好きなのでゴルフに行きました。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

説明:

各反復で、1つと_配列[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 2014年

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

デモはこちら

縮小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

ハスケル

分割はθ(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

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

一定のメモリ
O(n)時間の複雑さ



0

python

   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 2014年

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

Python 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%だけ回転させます。上記の誰もがこのアプローチをとっていないと思います。すべての要素に対して1つのスワップ操作を実行し、すべてを適切に実行します。

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}

オンラインでお試しください!

サブルーチンは、回転する距離を最初のパラメーターとして使用し、配列への参照を2番目のパラメーターとして使用します。実行時間は、回転の距離に基づいて一定です。配列サイズは実行時間には影響しません。配列は、要素を右側から削除して左側に配置することにより、適切に変更されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.