原題: https://www.patest.cn/contests/pat-b-practise/1008
題意理解: 假設n=6
, 需要移位的序列是: 1 2 3 4 5 6
, 如果m=2
, 從6開始看, 把6
移動2個位置, 6就到了2的位置, 同樣把5移動2個位置, 5就到了1的位置, 依次類推.
下面考慮一些特殊情況:
當m=0
, 很顯然原序列不進行任何移動
當m=6
, 把6移動6個位置, 結果仍然是原序列
當m=7
, 現在m>n
, 根據題意這種情況是可能的, 把6往右移動7個位置, 我們發現6移到了1的位置.
總結以上分析, 可以得出, 移動的次數m = m % n
解題思路: 本題有多種解法, 但不少方法感覺不太道義或不太合理.
第1種, 不管題目要求, 直接使用2個數組.
第2種, 利用C語言特性, 通過循環一個一個讀取數據, 讀取一個數據就通過計算把這個數據放在合適
的位置, 利用這種方法, 簡直可以實現數據"0"移動
第3種, 不進行數據移動, 而是控制打印順序. 這個很好理解, 比如6個數, [1 2 3 4 5 6], m=2,
也就是全體往右移動2個位置, 我們可以先打印5 6
, 再打印1 2 3 4
第4種, 使用鏈表實現, 這個方法感覺比較合理(下有詳細解釋)
第5種, 使用數組模擬鏈表實現, 用鏈表實現代碼量可能很長, 用數組模擬鏈表很簡單, 聲明一個空間
較大的數組, 然后從數組的中間開始存數據, 這樣涉及到移動數據時, 只需把后面的數據插在前面即可.
這種方法缺點很明顯, 就是占用空間可能比較大, 但就解本題來說, 是完全可以的.
第6種, 如果必須限定只能使用數組, 數組大小只能等於n. 可以利用移位算法來實現, 這種方法比較難
理解, 我在網上看到有大牛實現了, 可以參考這里:
http://blog.csdn.net/xtzmm1215/article/details/38407799
第3種實現方法, 控制打印順序, 完整實現代碼:
#include <stdio.h>
int main () {
int i;
int n;
int m;
char ch = ' '; // 打印控制變量, 默認空格
int arr[100]; // 下標0, 不存數據
scanf("%d", &n);
scanf("%d", &m);
m = m % n; // 確保 m < n
// 給數組賦值
for (i=1; i<=n; i++) {
scanf("%d", &arr[i]);
}
// 接下來分兩次, 分別控制打印順序
// 第1次打印, 從倒數第m個數開始
for (i=n-m+1; i<=n; i++) {
printf("%d ", arr[i]);
}
// // 第2次打印, 從1到倒數第m個數
for (i=1; i<=n-m; i++) {
if (i == (n - m)) {
ch = '\n';
}
printf("%d%c", arr[i], ch);
}
return 0;
}
鏈表實現
使用鏈表實現, 根據題意至少需要實現鏈表的插入, 刪除, 定位操作.
一般情況下, 大部分操作都是把鏈表后面的元素拿到前面, 如果真的追求最少的移動次數,
可以添加判斷條件, 從而有些情況需要把鏈表前面的元素移動到后面.
我們這里為了簡單, 插入只有2種情況一種是在頭部插入一種是在尾部插入.
由於我們結構體只有一個數據域data, 所以在執行刪除(在題目里面相當於移動)操作時, 只要
事先保存着數據域的值, 就可以把該節點徹底從鏈表中移除(free)
完整實現代碼:
#include <stdio.h>
struct list {
int data;
struct list *next;
};
typedef struct list s_list;
typedef struct list *p_list;
p_list insert (p_list p, int x);
void print (p_list head);
int del (p_list head, p_list *rear);
int main () {
int i;
int n; // 鏈表元素個數
int m; // 右移位數
int x; // 當前讀取的數
p_list head; // 頭指針
p_list rear; // 尾指針
head = (s_list*)malloc(sizeof(s_list));
head->next = NULL;
rear = head;
scanf("%d", &n);
scanf("%d", &m);
m = m % n;
// 為鏈表賦值
for (i=1; i<=n; i++) {
scanf("%d", &x);
rear = insert(rear, x); // 動態修改尾指針的指向
}
// 對鏈表進行移動
for (i=1; i<=m; i++) {
x = del(head, &rear);
insert(head, x);
}
print(head);
return 0;
}
// 在p元素的后面插入一個元素x
// 返回值: 返回一個指向被刪元素的指針
p_list insert (p_list p, int x) {
p_list temp = (s_list*)malloc(sizeof(s_list));
temp->data = x;
temp->next = p->next;
p->next = temp;
return temp;
}
// 刪除一個尾元素, 並返回其值
// 把鏈表元素的一個節點整個從程序中移除並無大礙, 只要被刪元素
// 的值, 被保存下來, 以后隨時可以重建被刪節點
// 該函數需要在內部改變rear的指向, 因此需要定義指針的指針
int del (p_list head, p_list *rear) {
p_list pre; // 最后一個元素的前一個元素
p_list tempRear; // 指向被刪元素, 也就是最后一個元素
int value; // 被刪除元素的值
value = (*rear)->data; // 又是運算符優先級的坑
tempRear = *rear;
pre = head->next;
while (pre->next != *rear) {
pre = pre->next;
}
*rear = pre;
(*rear)->next = NULL;
free(tempRear);
return value;
}
// 打印鏈表
void print (p_list head) {
p_list p;
char ch = ' '; // 打印控制變量
p = head->next;
while (p != NULL) {
if (p->next == NULL) {
ch = '\n';
}
printf("%d%c", p->data, ch);
p = p->next;
}
}