PAT (Basic Level) Practice1008 陣列元素迴圈右移問題
1008 陣列元素迴圈右移問題
一、題目
一個數組A中存有
N
(
>
0
)
N(>0)
N(>0)個整數,在不允許使用另外陣列的前
提下,將每個整數迴圈向右移
M
(
≥
0
)
M(≥0)
M(≥0)個位置,即將A中的資料由
(
A
0
A
1
.
.
.
A
N
−
1
)
(A_0 A_1...A_{N-1})
(A0A1...AN−1)變換為
(
A
N
−
M
.
.
.
A
N
−
1
A
0
A
1
.
.
.
A
N
−
M
1
)
(A_{N-M}...A_{N-1}A_0A_1...A_{N-M_1})
(AN−M..
二、輸入輸出
輸入格式
每個輸入包含一個測試用例,第1行輸入 N ( 1 ≤ N ≤ 100 ) N(1≤N≤100) N(1≤N≤100)和 M ( ≥ 0 ) M(≥0) M(≥0);第2行輸入N個整數,之間用空格分隔。
輸出格式
在一行中輸出迴圈右移M位以後的整數序列,之間用空格分隔,序列結尾不能有多餘空格。
三、樣例
輸入樣例
6 2
1 2 3 4 5 6
輸出樣例
5 6 1 2 3 4
四、題目分析
1.只輸出不改變陣列
如果只從完成題目的角度講,不需要移動陣列中的元素
2.按照題目要求:不使用額外陣列,交換次數儘可能少
-
方法一:(程式碼②)
首先以長度為12的陣列為例,(選取長度為12的原因是12的因數較多,便於討論)
為了方便討論,假設陣列元素值和其下標相同:
如果需要時間複雜度最小,每個元素要直接移動到最終位置。因此,只需要一個變數swap
來暫存中間被覆蓋的元素值。
-
情形一:陣列右移5位:
- step1:swap儲存元素5
- step2:5被0元素覆蓋
- step3:0號下標儲存swap的值
- step4:swap儲存10;
- step5:10被0號下標的5覆蓋;
- …
- 最終完成移動,且0號下標的值正好是最後一次交換的值。
由此看來,一次迴圈即可完成右移嗎?
- step1:swap儲存元素5
-
情形二:陣列右移三位
根據情形一總結出的方法進行右移:發現交換隻發生在幾個元素之中
-
情形三:陣列右移9位
根據情形一總結出的方法進行右移:交換依舊發生在幾個元素之中,且步長位3
-
綜合以上情形:
一次迴圈變換元素的分佈和M,N的最大公因數有關,因此完成整個陣列的右移,需要將最大公因數分成的幾個陣列分別右移:
需要兩層迴圈實現,內層迴圈移動一組元素,外層迴圈遍歷所有被分成的組。 -
此演算法的時間複雜度是理論的最小值 O ( n ) O(n) O(n),使用一個swap臨時空間,空間複雜度位 O ( 1 ) O(1) O(1)。
-
-
方法二:
以長度為12的陣列為例,向右移動6位,期望的輸出如下圖所示:即從6輸出到陣列末尾,再從陣列起始位置輸出完整個陣列
經過變換的陣列最終排列應該為:6 7 8 9 10 11 0 1 2 3 4 5 從初始序列到期望序列可以經過逆序變換得到:
-
step1:轉置從陣列倒數第M個元素迴圈輸出到陣列末尾的元素;
-
step2:轉置從陣列起始位置到倒數第M-1個數組元素;
- step3:轉置整個陣列。
因此呼叫三次轉置函式即可完成陣列右移。
-
-
方法四:
std::rotate
五、程式碼
-
程式碼①:
#include <bits/stdc++.h> using namespace std; int main() { int n; int m; cin >> n >> m; int *num = new int[n]; m = m % n;//m>n的移動和m%n等效 for (int i = 0; i < n; i++)//迴圈讀入數值 cin >> num[i]; int flag = 0;//控制輸出格式變數,使第一個輸出前無空格 for (int i = n - m; i < n; i++) { if (flag) cout << ' '; flag = 1; cout << num[i]; }//從陣列倒數第M個元素迴圈輸出到陣列末尾 for (int i = 0; i < n - m; i++) { if (flag) cout << ' '; flag = 1; cout << num[i]; }//從陣列起始位置輸出到倒數第M-1個元素 return 0; }
-
程式碼②:
#include <bits/stdc++.h> using namespace std; inline int Max_common_factor(int a, int b)//求最大公因數 { int c = a % b; while (c) { a = b; b = c; c = a % b; } return b; } int main() { int n; int m; int swap;//用於交換的空間 int max_common_factor;//最大公因數 cin >> n >> m; m = m % n;//m>n的移動和m%n等效 int *num = new int[n]; for (int i = 0; i < n; i++)//迴圈讀入數值 cin >> num[i]; if (m)//當m不為0時需要進行移動,否則直接輸出即可 { max_common_factor = Max_common_factor(n, m);//最大公因數 for (int i = 0; i < max_common_factor; i++)//外層迴圈的次數是最大公因數的值 { int k = i; swap = num[i];//交換空間儲存第一個元素,第一個元素用來暫存被交換的值 for (int j = 0; j < n / max_common_factor; j++) { k = (k + m) % n; num[i] = num[k]; num[k] = swap; swap = num[i]; } } } int flag = 0; for (int i = 0; i < n; i++) { if (flag) cout << ' '; flag = 1; cout << num[i]; }//按照格式輸出陣列 return 0; }
六、總結
-
求最大公因數函式:
int Max_common_factor(int a, int b) { int c = a % b; while (c) { a = b; b = c; c = a % b; } return b; }
-
行內函數:
- 作用:減少函式呼叫的開銷編譯器處理對行內函數的呼叫語句時,是將整個函式的程式碼插入到呼叫語句處,而不會產生呼叫函式的語句。
- 呼叫行內函數之前要有函式的定義,不能只有宣告
- 定義格式:
inline 函式名(/*引數列表*/) { /*函式體*/; };