陣列移動演算法

2022-08-19 21:57:12 字數 1666 閱讀 9641

ash前段時間貼出了乙個問題:

對於有k個元素的陣列 int a[k]=;寫乙個高效演算法將陣列內容迴圈左移m位

比如:  int a[6] = ,迴圈左移3位得到結果,

要求:

1不允許另外申請陣列空間,但可以申請少許變數

2不允許採用每次左移

這是乙個有趣的問題,當時ash給出了乙個很簡單的解法:

1、將整個陣列倒排;

2、將前k-m個元素和剩下的m個元素分別倒排。

這個演算法需要對每個陣列元素做兩次寫操作,所以我當時在考慮,有沒有一種方法,只對陣列元素進行一次寫操作就完成移動?

最直觀的想法,就是從第乙個元素開始,把它一步移動到最終的目的位置,而該位置原有的元素的值取出,移動到它的新位置。遞迴進行這個步驟。

首先,我們在數學上很容易理解,這是乙個一一對應的對映,絕不會在乙個位置上出現兩次移動。所以不會出現移動的遞迴過程中途指向了已經移動過的元素。

那麼,這個遞迴過程唯一的終止條件就是,當前移動的元素,目的位置是移動過程的起始位置。

有興趣的朋友不妨在紙上推演一下這個過程,並不複雜。多試驗幾種組合,細心的朋友也許會發現,這個遞迴過程有時候可以遍歷整個陣列,有時候則會跳過若干元素。這其中有沒有什麼規律?

如果按照元素的索引下標標示元素,0到k-1中的任意元素i,會移動到什麼位置?

如果i小於m,它會移動到k-m+i,否則,它的新位置是i-m。在小小飛和ash的講解下,我理解了這個過程其實可以統一在乙個算式下:

對於任意的i,它的新位置i' = ((k - m) + i )%k

那麼,我們可以定義這個迴圈鏈:取整數i0,使得0=1呢?在這裡有乙個現象,任意兩個i,它們的差要麼是0,要麼是k和m的最大公因是q的整倍數。有興趣的朋友可以嘗試證明一下。因為我們知道整個過程一共a步,k=aq,那麼,i到ix的序列會形成乙個步長為q的等差序列。所以,我們要移動整個0到k-1區間,應該對0到q-1的元素應用這個遞迴演算法。

據此,我編寫了演算法帶**,並進行了泛化,使之可以適用於不同的型別,包括用不同整型變數表示k,m等引數的應用:

求最大公因數,輾轉相除,可以應用於任何求最大公因式/數的場合,只要引數型別支援求餘和賦值:

template

intt hcf(const intt & x, const intt & y)

return b;

}; 區間移動,支援任何型別的陣列:

template

void carry(t point, const u & len, const u & m)

; 用迭代器實現的區間移動,可以應用於stl容器等場合:

template

void carry(itert &iter, const u & len, const u & m)

while(p != start);

} };

演算法評估:表面上看,ash的兩步倒排需要對陣列進行兩次寫操作,我的演算法只需要一次。但是我要承認,因為需要乙個中間變數儲存當前要移動的元素,其實我要比ash的演算法多一倍的堆變數賦值。另外每一步迭代我要計算目的位置,所以我不敢肯定我的演算法就更快一些,對於我來說,它帶給我的是思考的樂趣:)。

**中使用了std::swap函式來交換變數值,這個函式在中。另外為了析取迭代器的值,我使用了中的iterator_traits操作。有興趣的朋友不妨編寫乙個適用於普通指標的偏特化版本。

演算法總結 陣列 陣列移動 左奇右偶

給定乙個整形陣列,要求對這個陣列進行操作,移動成左邊部分奇數,右邊部分偶數的形式。想法 快速排序的劃分方法,原本是根據與某乙個值進行比較進行劃分。更改這個演算法策略為根據每個數的奇偶性進行劃分。實現1 陣列移動,偶數移動到右邊,奇數在左邊 方式1 用類似與快速排序劃分的方式 用乙個指標掃瞄,遇到奇數...

演算法心得1 交換值 陣列移動

要交換兩個變數的值很簡單,只需要這麼寫 void swap int a,int b 當然可以不用中間變數交換值,好像能省一點記憶體空間,但是實際意義也不是很大,但在面試或筆試中常常出現這個題目,可能會一下想不出來,但是仔細一想,發現這就類似解乙個二元一次方程,如下 void swap int a,i...

LeetCode初級演算法之陣列 283 移動零

給定乙個陣列nums,編寫乙個函式將所有0移動到陣列的末尾,同時保持非零元素的相對順序。示例 輸入 0,1,0,3,12 輸出 1,3,12,0,0 說明 必須在原陣列上操作,不能拷貝額外的陣列。儘量減少操作次數。題目資訊 輸入 陣列 輸出 陣列 將原陣列的0都移動到後面 額外 空間o 1 時間儘量...