構造隊列的算法問題

問題如下:
小明同學把1到n這n個數字按照一定的順序放入了一個隊列Q中。現在他對隊列Q執行了如下程序:

while(!Q.empty()) //隊列不空,執行循環
{ 
    int x=Q.front(); //取出當前隊頭的值x 
    Q.pop(); //彈出當前隊頭 
    Q.push(x); //把x放入隊尾 
    x = Q.front(); //取出這時候隊頭的值 
    printf("%d\n",x); //輸出x 
    Q.pop(); //彈出這時候的隊頭 
}

做取出隊頭的值操作的時候,並不彈出當前隊頭。 小明同學發現,這段程序恰好按順序輸出了1,2,3,…,n。現在小明想讓你構造出原始的隊列,你能做到嗎?

輸入描述:
第一行一個整數T(T ≤ 100)表示數據組數,每組數據輸入一個數n(1 ≤ n ≤ 100000),輸入的所有n之和不超過200000。

輸出描述:
對於每組數據,輸出一行,表示原始的隊列。數字之間用一個空格隔開,不要在行末輸出多餘的空格.

輸入例子:
4
1
2
3
10

輸出例子:
1
2 1
2 1 3
8 1 6 2 10 3 7 4 9 5

根據問題可以得出原來算法的java程序:

static int[] outResult(Queue<Integer> queue){
        int[] array = new int[queue.size()];
        int i = 0;
        while (!queue.isEmpty()) {
            int head = queue.poll();
            queue.add(head);
            array[i++] = queue.poll();
        }
        return array;
    }

定義數組下標0,對應隊列的頭,依次類推。可以用一些數據來測試以上java程序。
測試用例:

        //源數組{4,1,6,2,5,3,7} 進入隊列queue
        int[] testArray = new int[]{4,1,6,2,5,3,7};
        Queue<Integer> queue = new LinkedList<Integer>();
        for (int i = 0; i < testArray.length; i++) {
            queue.add(testArray[i]);
        }
        //source -> result
        System.out.print("source:"+queue);
        //經過問題所述算法演算
        int[] a = Qtest.outResult(queue);
        System.out.println("->result:"+Arrays.toString(a));
        //輸出爲source:[4, 1, 6, 2, 5, 3, 7]->result:[1, 2, 3, 4, 5, 6, 7]

以上是由源數據到結果數據的順向過程,我們的目標是要得出其逆向過程。我們可以先舉些例子來逐步推導。我們不難得出,當隊列queue大小爲1的時候,結果數據爲{queue[0]},源數據也爲{queue[0]};當queue大小爲2的時候,結果數據爲{queue[0],queue[1]},源數據爲{queue[1],queue[0]}。從源數據到 結果數據,即執行outResult方法,我們可以發現奇數位的數據是首先輸出的,然後偶數位的數據一個一個地放到後面再組成一個新的隊列,按照此規律輸出。到這裏,我們可以意識到是一個循環過過程,可以用遞歸實現,而遞歸的邊界就是size爲1和2的時候,隊列的操作就只有兩種:1.把數據放到隊列尾部;2.把數據輸出;而且這兩個操作是間隔實現的,也就是說按照先1後2的順序。這時候,我們可以發現,只要把結果數據從中間分成均勻的兩部分,後一部分就往前一部分間隔的插入。以下爲隊列大小size>2的時候,結果數據到數據的推導實例。

//結果數據->源數據
//size = 1
1->1
//size = 2
1 2->2 1
//size = 3
1 2 3->1 
      2 3->2
           3
           -> 3 2->2 1 3 
//size = 4
1 2 3 4-> 1 2
         3 4->3
              4
              -> 4 3->4 1 3 2
//size = 5
1 2 3 4 5->1 2
          3 4 5->3
                4 5->4
                     5
                     -> 5 4->4 3 5-> 3 1 5 2 4
//size = 6
1 2 3 4 5 6->1 2 3
            4 5 6->4
                  5 6->5
                       6
                       ->6 5->5 4 6->5 1 4 2 6 3
//size = 7
1 2 3 4 5 6 7->1 2 3
              4 5 6 7->4 5
                      6 7->6
                           7
                           ->7 6->7 4 6 5-> 4 1 6 2 5 3 7

解釋一下上面的推導過程:

//size = 4

1 2 3 4(結果數據)-> 1 2(將數組分成(1 2)size/2和(3 4)size-size/2前後兩部分)
                  3 4->3(繼續對半分割)
                       4(直到數組爲大小爲1)
                       -> 4 3(結果數據3 4的源數據)->4 1 3 2(源數據)

最後總結得出逆向算法:

static int[] outSource(final int[] array){
        final int size = array.length;
        int[] resultArray = new int[size];
        final int rest = size % 2;
        if(size == 1){
            return array;
        }else if(size == 2){
            return new int[]{array[1],array[0]};
        }else if(size > 2){
            for (int i = 0; i < size / 2; i++) {
                resultArray[2*i+1] = array[i];
            }
            int[] part = Arrays.copyOfRange(array, size / 2, size);
            int[] postArray = outSource(part);

            if(rest == 0){
                for (int i = 0; i < postArray.length; i++) {
                    resultArray[2*i] = postArray[i];
                }
            }else{
                for (int i = 0; i < postArray.length-1; i++) {
                    resultArray[2*i] = postArray[i+1];
                }
                resultArray[size-1] = postArray[0];
            }
            return resultArray;
        }else{
            return null;
        }
    }

根據上面的算法,還得出一個規律,就是當數組大小爲偶數的時候,將後半部分的數組一個一個地插入前半部分數組的每個元素的前頁面。

1 2 3 4 5 6
 1 2 3
| | |  -> 5 1 4 2 6 3
5 4 6

如果數組大小爲奇數的時候,就要先把後半部分的數組的第一個元素放到尾部,然後再執行上面的操作。

1 2 3 4 5->1 2
          3 4 5->3
                4 5->4
                     5
                     -> 5 4->4 3 5-> 3 1 5 2 4
                             3 5 4(4移到尾部)
                              | | -> 3 1 5 2 4
                              1 2

完整的測試用例:

        int[] testArray = new int[]{4,1,3,2};
        Queue<Integer> queue = new LinkedList<Integer>();
        for (int i = 0; i < testArray.length; i++) {
            queue.add(testArray[i]);
        }
        //source -> result
        System.out.print("source:"+queue);
        int[] a = Qtest.outResult(queue);
        System.out.println("->result:"+Arrays.toString(a));

        //result -> source
        System.out.print("result:"+Arrays.toString(a));
        int[] b = outSource(a);
        System.out.println("->source:"+Arrays.toString(b));
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章