หมุนอาร์เรย์จำนวนเต็มด้วยอัลกอริทึม O (n) [ปิด]


10

เขียนฟังก์ชั่นที่หมุนอาร์เรย์จำนวนเต็มด้วยตัวเลขที่กำหนด องค์ประกอบ k จากปลายควรย้ายไปยังจุดเริ่มต้นของอาร์เรย์และองค์ประกอบอื่น ๆ ทั้งหมดควรเลื่อนไปทางขวาเพื่อสร้างพื้นที่

การหมุนควรทำในสถานที่

อัลกอริทึมไม่ควรทำงานมากกว่า O (n) โดยที่ n คือขนาดของอาเรย์

ต้องใช้หน่วยความจำคงที่เพื่อดำเนินการ

ตัวอย่างเช่น,

ถ้าอาร์เรย์ถูกเริ่มต้นด้วยองค์ประกอบ arr = {1, 2, 3, 4, 5, 6, 7, 8, 9}

หมุน (arr, 3) จะส่งผลให้องค์ประกอบเป็น {7, 8, 9, 1, 2, 3, 4, 5, 6}

หมุน (arr, 6) จะส่งผลให้ {4, 5, 6, 7, 8, 9, 1, 2, 3}


1
หน่วยความจำคงที่ที่นี่หมายถึงอะไร? แน่นอนว่ามันต้องการหน่วยความจำอย่างน้อยO (n)ขั้นต่ำเพียงเพื่อเก็บอาร์เรย์ที่กำลังประมวลผลทำให้การใช้หน่วยความจำO (1)เป็นไปไม่ได้
Ad Hoc Garf Hunter

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

minified:

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

มันไม่ได้ทำในสถานที่แม้ว่า
marinus

@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 และสองคู่ นอกจากนี้ยังเรียกใช้ฟังก์ชัน "gcd" ซึ่งใช้จำนวนเต็มหนึ่งค่าและใช้ในการดำเนินการ O (บันทึก (n)) ส่วนใหญ่
Stephen Montgomery-Smith

เข้าใจแล้ว ฉันเพิ่มคำตอบของคุณ
microbian

@ StephenMontgomery-Smith - การO(log(n))ดำเนินการนี้เป็นอย่างไร ดูที่byการเป็น 1, `j 'loop ของคุณคือ s_arr / g หรือ N - นี่เป็นการดำเนินการ O (N)
Glenn 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 ++ มากมายที่นี่ดังนั้นฉันจึงคิดว่าฉันจะลองตัวนี้เพราะมันไม่นับตัวอักษร

นี่คือการหมุน "ในสถานที่" ที่แท้จริงดังนั้นใช้ 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เพราะมีจุดประสงค์ในการเอาชนะจุดประสงค์
Glenn Teitelbaum

2

หากคุณทำแต่ละรอบของการหมุนที่เป็นไปได้โดย n ในทางกลับกัน (มี 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ตัวแปรของคุณมีขนาดไม่คงที่ คุณจะต้องสร้างอาร์เรย์นี้ตามที่คุณไป
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");
}

ฟังก์ชั่นrotateลดลงถึง 137 ตัวอักษร:

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 }

ค่าการโกงที่เทียบเท่ากับโซลูชัน C ที่น่าประทับใจของ 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

ไปเล่นกอล์ฟอยู่แล้วเพราะฉันชอบเล่นกอล์ฟ มีค่าสูงสุด O (N) ตราบใดที่t<= ขนาดของอาร์เรย์

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

เพื่อจัดการtสัดส่วนใด ๆ ใน O (N) สามารถใช้งานได้ดังต่อไปนี้ (ชั่งน้ำหนักที่ 58 ตัวอักษร):

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

ไม่ส่งคืนแก้ไขอาร์เรย์ให้เข้าที่


1
+1 สำหรับr(o,t) => rot
Conor O'Brien

1

REBEL - 22

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

อินพุต: k แสดงเป็นจำนวนเต็มแบบนารีที่ใช้_เป็นดิจิตตามด้วยช่องว่างจากนั้นอาเรย์ที่คั่นด้วยช่องว่างของจำนวนเต็ม

เอาต์พุต: ช่องว่างจากนั้นอาร์เรย์จะหมุน

ตัวอย่าง:

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

สถานะสุดท้าย:

 3 4 5 1 2

คำอธิบาย:

ในแต่ละซ้ำจะแทนที่หนึ่ง_และอาร์เรย์ด้วย[array] + tailtail + [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

Haskell

นี่คือθ (n) จริง ๆ แล้วเมื่อแบ่งเป็นθ (k) และการเข้าร่วมคือθ (nk) ไม่แน่ใจเกี่ยวกับความทรงจำ

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

หลาม

def rotate(a, n): a[:n], a[n:] = a[-n:], a[:-n] 

สิ่งนี้จะใช้หน่วยความจำคงที่หรือไม่
SztupY

อืม ... ไม่แน่ใจ
เมดิสันพฤษภาคม

มันไม่ได้เป็นการดำเนินการของหน่วยความจำอย่างต่อเนื่อง
microbian

แย่จัง โทรดี ...
เมดิสันอาจ

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

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

ชวา

สลับองค์ประกอบ 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

Perl 5 , 42 ไบต์

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

ลองออนไลน์!

รูทีนย่อยใช้ระยะทางในการหมุนเป็นพารามิเตอร์แรกและการอ้างอิงไปยังอาร์เรย์เป็นวินาที เวลาเรียกใช้เป็นค่าคงที่ตามระยะทางของการหมุน ขนาดอาร์เรย์ไม่ส่งผลต่อเวลาทำงาน Array ได้รับการแก้ไขโดยการลบองค์ประกอบจากด้านขวาและวางไว้ทางซ้าย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.