旋转阵列

时间:2012-02-14 02:03:58

标签: java algorithm

我正在尝试旋转数组,以便给定数字和数组,将数组旋转一定数量。 IE:

ABCDEFGH

给出3:

fghabcde

如果不使用其他数据结构/最小空间,最好的方法是什么?

这是一个函数头:

public static char[] rotateString(char[] s, int rotateAmount){

}

8 个答案:

答案 0 :(得分:6)

首先,我将做出一个很大的假设,即“更好”意味着“我不想要任何新的数据结构”。

如果是这种情况,那么最简单的解决方案是最好的,因为我不需要按时间进行优化。我知道其他解决方案更优雅,我只发布了它,因为我已经将这个问题读作“确保它在空间方面的最小化”。

private static String rotate( final char[] a, final int n ) {
    for(int i = 0; i < n; i++) {
        char tmp = a[a.length-1];
        for(int j = a.length-1; j >= 0; j--) {
            a[j] = j == 0 ? tmp : a[(j-1+a.length)%a.length];
        }
    }
    return new String(a);
}

所以我很快就把它砍掉了。基本上,我只是旋转长度为1,直到我旋转n次。要优化它,您可能需要gcd(n, a.length)

现在,由于我的解决方案非常糟糕,我还将发布以下代码here

void reverse_string(char* str, int left, int right) {
  char* p1 = str + left;
  char* p2 = str + right;
  while (p1 < p2) {
    char temp = *p1;
    *p1 = *p2;
    *p2 = temp;
    p1++;
    p2--;
  }
}

void rotate(char* str, int k) {
  int n = strlen(str);
  reverse_string(str, 0, n-1);
  reverse_string(str, 0, k-1);
  reverse_string(str, k, n-1);
}

这就是我所假设的C风格实现,它比我的运行速度更快,使用三个反转的基本思想,你可以实现内联移位。

如前所述,

  

诀窍是做三次反向操作。一个用于整个字符串,一个从索引0到k-1,最后索引k到n-1。奇怪的是,这将产生正确的旋转阵列,没有任何额外的空间! (当然,您需要一个临时变量进行交换)。

我还没有在我链接过的博客上验证过这个属性,所以我会发布一些看起来有用的内容,但我自己从未测试过...

答案 1 :(得分:3)

可以找到Collections.rotate(List, int)的Java实现here;它只使用恒定的开销,而且非常干净且易于理解。我不一定只是调用它(尽管有一个像Guava的Chars.asList这样的包装器,只会有一个恒定的开销),但算法整洁干净,你可以很容易地适应你的需要。 / p>

这是O(n),但原因并不明显;大多数工作都在弄清楚为什么它不会多次访问任何一个索引。我会把它留作练习。

答案 2 :(得分:1)

这样的东西? 它需要temp的额外存储空间为O(1)。 尝试使用shift=1运行此功能,以查看其背后的想法。

public static String rotateString(char[] s, int rotateAmount) {
  int length = s.length;
  int shift = (length - rotateAmount) % length;
  for (int start = 0; start < gcd(length, shift); start++) {
    char temp = s[start];
    for (int i = (start + shift)%length; i != start; i = (i + shift) % length) {
      s[(length + i - shift) % length] = s[i];
    }
    s[(length + start - shift) % length] = temp;
  }
  return new String(s);
}

gcd(a,b)ab的最大公分母,可以使用例如Euclid's Algorithm来计算。

时间复杂度为O(n),其中n是数组的长度。

答案 3 :(得分:1)

  1. 由于必须返回结果数组,因此无论如何都必须创建它。
  2. 该功能应该在两个方向上都有效,并且当有一个或多个周转时 - 按数字转动超过数组的长度。

    public static char[] rotateString(char[] s, int rotateAmount){
        int n=s.length;
        char[] res=new char[n];
        if (n==0) return res;
    
        int turns=rotateAmount / n;
        int j=((-turns+1)*n+rotateAmount) % n;
        for (int i=0; i<n; i++){
            if(j==n) j=0;
            res[j]=s[i];
            j++;
        }
        return res;
    }
    
  3. 测试:

        System.out.println("'',+1->"+new String(rotateString("".toCharArray(),+1)));
        System.out.println("123,+1->"+new String(rotateString("123".toCharArray(),+1)));
        System.out.println("123,-1->"+new String(rotateString("123".toCharArray(),-1)));
        System.out.println("123,-5->"+new String(rotateString("123".toCharArray(),-5)));
        System.out.println("123,-6->"+new String(rotateString("123".toCharArray(),-6)));
        System.out.println("123,+6->"+new String(rotateString("123".toCharArray(),+6)));
    

答案 4 :(得分:0)

如何使用Stack?

让我解释一下。当您将abcdefgh推送到堆栈时,它变为 hgfedcba

现在一次弹出一个元素并添加字符串。假设轮换量为3。

当你弹出第一个元素时,你会得到 h 。由于3小于弹出的字符数,因此弹出一个元素并添加到 h 。因此,当您弹出下一个元素时,您会获得 g 。将 g 添加到 h 。所以它变成 hg 。重复旋转量的过程(此处为3)因此变为 fgh

现在创建一个新字符串并从堆栈中弹出其余元素并添加弹出的每个字符。因此它变成“ abcd ”。

合并两个字符串。这是 fghabcd

答案 5 :(得分:0)

以下是c#版本仅供参考(有关详细信息,请参阅:http://codingworkout.blogspot.com/2014/07/rotating-array.html):

void swap<T>(ref T x, ref T y)
        {
            T t = x;
            x = y;
            y = t;
        }
        void Reverse(int[] a, int start, int end)
        {
            int i = start, j = end;
            while (i < j)
            {
                swap<int>(ref a[i], ref a[j]);
                i++;
                j--;
            }
        }
        void Rotate(int[] a, int k)
        {
            a.ThrowIfNull("array");
            k.Throw("k", e => e < 0);
            if (a.Length <= 1)
            {
                return;
            }
            k = k % a.Length;
            this.Reverse(a, 0, a.Length - 1);
            this.Reverse(a, 0, k - 1);
            this.Reverse(a, k, a.Length - 1);
        }

单元测试

[TestMethod]
        public void RotateTests()
        {
            int[] a = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
            for (int i = 1; i <= a.Length; i++)
            {
                int k = a.Length - i + 1;
                int[] b = new int[a.Length];
                this.Rotate(a, i);
                for(int j = 0;j<b.Length;j++)
                {
                    if(k > a.Length)
                    {
                        k = (k % a.Length);
                    }
                    b[j] = k;
                    k++;
                }
                for (int j = 0; j < a.Length;j++ )
                {
                    Assert.IsTrue(a[j] == b[j]);
                }
                this.Rotate(a, a.Length - i);
            }
        }

答案 6 :(得分:0)

我认为这更简单,而且肯定更快

private static String rotate( char[] arrayStr, int K, int N ) {
    int[] my_array = new int[N];
    for(int item = 0; item < N; item++) {
        my_array[(item+K) % N] = arrayStr[item];
    }
    return my_array 
}

其中K =旋转数,N =阵列长度

答案 7 :(得分:-1)

试试这个:

public static char[] rotateString(char[] s, int rotateAmount){
    String oldStr=new String(s);
    String newStr=oldStr.substring(oldStr.length()-rotateAmount,oldStr.length())+oldStr.substring(0,oldStr.length()-rotateAmount);
    return newStr.toCharArray();
}