1. 程式人生 > 實用技巧 >[探究] 陣列迴圈移動的一種方式

[探究] 陣列迴圈移動的一種方式

(筆者為普通高三學生,無競賽經驗,如果你有更好的方法,請忽略本文)

最近刷資訊科技做到一道題,設今天是星期k (k = 1, 2, 3 ,4, 5, 6, 7)。求昨天是星期幾?明天是星期幾? 這兩個問題看似簡單,實則隱含著深意,隱含著一種解決此類問題的思想。

答案是 (k + 5) mod 7 + 1。它是如何被推匯出來的呢?也許需要嚴格的數學證明,但是,對於一個追求效率的人來說,那些證明實在是繁冗拖沓,下面我提供一種很快的解決辦法:

我稱之為“啟發推導法”。大概記得這種方法老師之前上課講過,不過他研究得肯定沒我透徹。“啟發”的意思是,從一個猜測的答案開始,根據邏輯推理,逐漸達到正確的解。

  • PART1-求“明天是星期幾”

按照習慣性思維,我們應該很快能寫出 (k + 1) mod 7。這個公式在k1 ~ 57 時符合得很好,但是對 k = 6 時就無能為力了,為什麼呢?因為對 7 取模的結果最大隻能是 6 。從邏輯上這個公式就被推翻了——但它已經很接近正確的解了,確切地來說已經滿足了 6 / 7 的情況。怎麼讓結果能覆蓋到7呢?只需要加上一個1就行了,但是別忘了在括號內減去加上的 1。即:k mod 7 + 1

它的確是符合所有的情況的。

  • PART2-求“昨天是星期幾”

對於這類情況的處理稍顯複雜,同樣我們先寫出 (k - 1) mod 7 ,但它顯然不行,範圍裡就沒有 7

。怎麼做呢?還是括號外 + 1,括號內 - 1 嗎?這樣 k 代進去就是負數了。

我們知道對一個數取模時,加上它本身對取模結果沒有影響,不妨先 + 7 以防止負數的出現?即 (k + 6) mod 7,再按照原來的方法處理,就成了 (k + 5) mod 7 + 1,符合所有情況。

—————————————————————————————————————————————

上面只是一個引子,現在我們迴歸正題——實現陣列的迴圈移動。

無疑,這個操作需要額外的儲存空間,因為一個元素的移動必然引起一系列元素的連鎖移動,不能直接覆蓋否則資料會丟失。考慮一個數組 a ,它存放著待迴圈移動的資料,現在我們開一個數組b 來存放迴圈移動後的資料,此時就需要用到引子裡面的思想方法,不多說,直接呈上程式碼:

#include <vector>
#include <iostream>

void printArray(const std::vector<int> &v)
{
    for (const auto& object : v)
        std::cout << object << " ";
    std::cout << std::endl;
}

int main()
{
    std::vector<int> a{ 1,2,3,4,5,6,7,8 };
    int n = a.size(); // a陣列的元素個數
    int m = 1; // 迴圈向右移動m個元素
    std::vector<int> b(n, 0);

    std::cout << "Array A: ";
    printArray(a);
    std::cout << std::endl;

    for (; m < n; m++)
    {
        for (int i = 0; i < a.size(); i++)
            b[(i + m - 1) % n + 1] = a[i];
        std::cout << "Array B: ";
        printArray(b);
    }
    return 0;
}

執行程式碼,卻發現結果和預期不符,為什麼呢?因為我們前面的公式是針對 1~n 的。

但是,不說以不變應萬變,但只要稍作改動——0~n-1不就是 1~n嗎? 我們可以先 +1,將其代入公式,再 -1恢復原狀:

for (; m < n; m++)
    {
        for (int i = 0; i < a.size(); i++)
            b[(i + m) % n] = a[i];
        std::cout << "Array B: ";
        printArray(b);
    }

同樣地,左移的程式碼也就順理成章:

for (; m < n; m++)
    {
        for (int i = 0; i < a.size(); i++)
            b[(i - m % n + n) % n] = a[i]; // m % n 防止索引為負數
        std::cout << "Array B: ";
        printArray(b);
    }

現在我們來考慮一個變形,之前一直都是對 a[i] 賦值,能不能對 b[i] 賦值呢?其實原理完全一樣,此處不再贅述。

--END-- 距離第一次選考還有40天。