将字符串重新排序一半字符

时间:2011-08-28 16:15:19

标签: algorithm recursion

这是一个面试问题。

  

给定一个字符串,如:123456abcdef,由n / 2个整数后跟n / 2个字符组成。将字符串重新排序为包含为1a2b3c4d5e6f。算法应该是就地的。

我给出的解决方案是微不足道的 - O(n ^ 2)。只需将字符向左移动n / 2个位置即可。

我尝试使用递归作为 -
一个。将上半部分的后半部分与第二部分的前半部分交换 - 例如
    123 456 abc def
    123 abc 456 def
湾递归两半。

我遇到的pbm是交换因元素数量而异 - 例如。

下一步该怎么做?     123 abc     12ab 3c

该怎么做:12345 abcde                      123abc 45ab

这是一个非常古老的问题,可能是重复的。请让我知道.. :)

另一个例子: 输入:38726zfgsa 输出:3z8f7g2s6a

7 个答案:

答案 0 :(得分:5)

以下是我如何解决问题:

1) Divide the string into two partitions, number part and letter part
2) Divide each of those partitions into two more (equal sized)
3) Swap the second the third partition (inner number and inner letter)
4) Recurse on the original two partitions (with their newly swapped bits)
5) Stop when the partition has a size of 2

例如:

123456abcdef - > 123456 abcdef - > 123 456 abc def - > 123 abc 456 def

123abc - > 123 abc - > 12 3 ab c - > 12 ab 3 c

12 ab - > 1 2 a b - > 1 a 2 b

...等

另一半的递归也一样..

所有这些都可以在适当的位置完成,唯一的问题就是交换不同大小的分区(但它会被一个分开,因此不难处理)。

答案 1 :(得分:2)

如果你有一个位图来标记已经移动了哪些元素,那么通过追逐圆周期的元素很容易置换一个数组。我们没有单独的位图,但如果你的字符是字母(或者至少有高位清除),那么我们可以使用每个字符的最高位来标记它。这会生成以下程序,该程序不是递归的,因此不使用堆栈空间。

class XX
{
  /** new position given old position */
  static int newFromOld(int x, int n)
  {
    if (x < n / 2)
    {
      return x * 2;
    }
    return (x - n / 2) * 2 + 1;
  }
  private static int HIGH_ORDER_BIT = 1 << 15; // 16-bit chars
  public static void main(String[] s)
  {
    // input data - create an array so we can modify
    // characters in place
    char[] x = s[0].toCharArray();
    if ((x.length & 1) != 0)
    {
      System.err.println("Only works with even length strings");
      return;
    }
    // Character we have read but not yet written, if any
    char holding = 0;
    // where character in hand was read from
    int holdingPos = 0;
    // whether picked up a character in our hand
    boolean isHolding = false;
    int rpos = 0;
    while (rpos < x.length)
    { // Here => moved out everything up to rpos
      // and put in place with top bit set to mark new occupant
      if (!isHolding)
      { // advance read pointer to read new character
        char here = x[rpos];
        holdingPos = rpos++;
        if ((here & HIGH_ORDER_BIT) != 0)
        {
         // already dealt with
         continue;
        }
        int targetPos = newFromOld(holdingPos, x.length);
        // pick up char at target position
        holding = x[targetPos];
        // place new character, and mark as new
        x[targetPos] = (char)(here | HIGH_ORDER_BIT);
        // Now holding a character that needs to be put in its
        // correct place
        isHolding = true;
        holdingPos = targetPos;
      }
      int targetPos = newFromOld(holdingPos, x.length);
      char here = x[targetPos];
      if ((here & HIGH_ORDER_BIT) != 0)
      { // back to where we picked up a character to hold
        isHolding = false;
        continue;
      }
      x[targetPos] = (char)(holding | HIGH_ORDER_BIT);
      holding = here;
      holdingPos = targetPos;
    }
    for (int i = 0; i < x.length; i++)
    {
      x[i] ^= HIGH_ORDER_BIT;
    }
    System.out.println("Result is " + new String(x));
  }
}

答案 2 :(得分:1)

现在,如果我问某人这个问题,那么我正在寻找他们在白板上写的第一个

assertEquals("1a2b3c4d5e6f",funnySort("123456abcdef"));
...

然后可能会要求更多的例子。

(然后,根据,如果任务是交错数字和字母,我认为你可以用两个步行指针,indexLetter和indexDigit来做,并根据需要推进它们直到你到达终点。)

答案 3 :(得分:1)

在你的递归解决方案中,为什么不在n / 2%2 == 0(n%4 == 0)时进行测试,并以不同的方式处理这两种情况

作为templatetypedef注释,您的递归不能就地。

但是这是一个使用您想要递归的方式的解决方案(不到位):

def f(s):
    n=len(s)
    if n==2:  #initialisation
        return s
    elif n%4 == 0 :  #if n%4 == 0 it's easy
        return f(s[:n/4]+s[n/2:3*n/4])+f(s[n/4:n/2]+s[3*n/4:])
    else:  #otherwise, n-2 %4 == 0
        return s[0]+s[n/2]+f(s[1:n/2]+s[n/2+1:])

答案 4 :(得分:1)

我们走了。递归,每次削减一半,就地。使用@Chris Mennie概述的方法。获得正确的分裂是棘手的。比Python长很多,不是吗?

/* In-place, divide-and-conquer, recursive riffle-shuffle of strings;
 * even length only.  No wide characters or Unicode; old school. */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void testrif(const char *s);
void riffle(char *s);
void rif_recur(char *s, size_t len);
void swap(char *s, size_t midpt, size_t len);
void flip(char *s, size_t len);
void if_odd_quit(const char *s);

int main(void)
{
    testrif("");
    testrif("a1");
    testrif("ab12");
    testrif("abc123");
    testrif("abcd1234");
    testrif("abcde12345");
    testrif("abcdef123456");

    return 0;
}

void testrif(const char *s)
{
    char mutable[20];

    strcpy(mutable, s);
    printf("'%s'\n", mutable);
    riffle(mutable);
    printf("'%s'\n\n", mutable);
}

void riffle(char *s)
{
    if_odd_quit(s);
    rif_recur(s, strlen(s));
}

void rif_recur(char *s, size_t len)
{
    /* Turn, e.g., "abcde12345" into "abc123de45", then recurse. */

    size_t pivot = len / 2;
    size_t half = (pivot + 1) / 2;
    size_t twice = half * 2;

    if (len < 4)
        return;

    swap(s + half, pivot - half, pivot);
    rif_recur(s, twice);
    rif_recur(s + twice, len - twice);
}

void swap(char *s, size_t midpt, size_t len)
{
    /* Swap s[0..midpt] with s[midpt..len], in place. Algorithm from
     * Programming Pearls, Chapter 2. */

    flip(s, midpt);
    flip(s + midpt, len - midpt);
    flip(s, len);
}

void flip(char *s, size_t len)
{
    /* Reverse order of characters in s, in place. */

    char *p, *q, tmp;

    if (len < 2)
        return;

    for (p = s, q = s + len - 1; p < q; p++, q--) {
        tmp = *p;
        *p = *q;
        *q = tmp;
    }
}

void if_odd_quit(const char *s)
{
    if (strlen(s) % 2) {
        fputs("String length is odd; aborting.\n", stderr);
        exit(1);
    }
}

答案 5 :(得分:1)

通过比较123456abcdef1a2b3c4d5e6f,我们可以注意到只有第一个和最后一个字符处于正确的位置。我们还可以注意到,对于每个剩余的n-2个字符,我们可以直接从其原始位置计算出它们的正确位置。他们会到达那里,那里的元素肯定不在正确的位置,所以它必须替换另一个。通过执行n-2个步骤,所有元素都将到达正确的位置:

 void funny_sort(char* arr, int n){
    int pos = 1;    // first unordered element
    char aux = arr[pos];
    for (int iter = 0; iter < n-2; iter++) { // n-2 unordered elements
        pos = (pos < n/2) ? pos*2 : (pos-n/2)*2+1;// correct pos for aux
        swap(&aux, arr + pos);
    }
}

答案 6 :(得分:0)

将每个数字作为其数值。将每个字母的分数设为a = 1.5,b = 2.5 c = 3.5等。根据每个字符的分数运行字符串的插入排序。

[ETA]简单评分不起作用,所以使用两个指针并反转两个指针之间的字符串。一个指针从字符串的前面开始,每个循环前进一步。另一个指针从字符串的中间开始,每隔一个周期前进一次。

123456abcdef
 ^    ^

1a65432bcdef
  ^   ^

1a23456bcdef
   ^   ^

1a2b6543cdef
    ^  ^