1008. 數組元素循環右移問題 (20)


原題: 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;
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM