std rotate的幾種實現方法

2021-09-29 23:21:01 字數 3815 閱讀 8093

《程式設計珠璣》2.3節提出了向量旋轉問題,並給出幾種解決方案。c++標準庫中的

template 

void

rotate

(forwarditerator first, forwarditerator middle, forwarditerator last)

就解決了該問題,而且一般來說使用了其中效率最高的方法。接下來就分別看一下這幾個解決方案,並分別實現rotate函式。

問題:將乙個n元向量x向左旋轉i個位置。

首先是最樸素的方法,將x的前i個元素複製到乙個臨時陣列中,然後將餘下的n-i個元素向左移動i個位置,最後將最初的i個元素從臨時陣列中複製到x中餘下的位置。

template 

void

rotate1

(forwardit first, forwardit middle, forwardit last)

但是這個方法消耗了較多的儲存空間。

考慮將向量首尾相接,我們要做的相當於迴圈左移i位。詳細的步驟是:移動x[0]到臨時變數t,然後移動x[i]至x[0],x[2i]至x[i],依此類推(將x的所有下標對n取模),直至返回到x[0],此時從t取值。如果該過程沒有移動全部元素,比如6個元素的陣列左移2位,就從x[1]再次移動,直到所有的元素都已經移動為止。

由於std::rotate接受的是前向迭代器,該方法實現起來有些複雜:

template 

void

rotate2

(forwardit first, forwardit middle, forwardit last)

; forwardit write = first;

forwardit read = middle;

forwardit hole = write;

auto tmp =

*hole;

auto counter = std:

:distance

(first, last);if

(counter <=0)

return

;while

(counter)

*write = tmp;

--counter;

write =

++read;

hole = write;

tmp =

*hole;

circle_advance

(read);}

}

第三個方法比較巧妙了,將x分為ab兩段,選擇x其實就是交換ab使之變為ba。考慮兩種情況:

a比b短,將ab表示為ab1b2,其中a與b2長度相同,最終我們需要的是b1b2a。先交換a與b2,向量變為b2b1a,接下來只要交換b2b1即可。

a比b長,將ab表示為a1a2b,其中a1與b長度相同,最終我們需要的是ba1a2。先交換a1與b,向量變為ba2a1,接下來只要交換a2a1即可。

以上行為可以用遞迴實現:

template 

void

rotate3

(forwardit first, forwardit middle, forwardit last)

rotate3

(first, first == oldmid ? middle : oldmid, last)

;}

很多實現就用這個方法實現的std::rotate,但可能沒有使用遞迴:

template 

void

rotate4

(forwardit first, forwardit middle, forwardit last)

}

這個方法足夠高效,但實現起來還是要小心,最後乙個方法即簡單效率又不差:先對a求逆得到arb,然後對b求逆,得到arbr,最後對整體求逆,得到(arbr)r,此時恰好就是ba。

template 

void

rotate5

(forwardit first, forwardit middle, forwardit last)

ken thompson主張把該方法當做乙個常識,在2023年。。。

《程式設計珠璣》作者測試了後三種方法,後兩種方法明顯更優,求逆演算法花費的時間很穩定,但比塊交換演算法稍慢一些。

附上正確性測試**:

#include

#include

#include

#include

template

<

class

forwardit

>

void

myrotate

(forwardit first, forwardit middle, forwardit last)

intmain()

;myrotate

(v.begin()

, v.

begin()

+3, v.

end())

; std::cout <<

"a direct test : "

;for

(int n: v)

std::cout << n <<

' ';

std::cout <<

'\n'

; v =

; std::cout <<

"before sort : "

;for

(int n: v)

std::cout << n <<

' ';

std::cout <<

'\n'

;// insertion sort

for(

auto i = v.

begin()

; i != v.

end();

++i)

std::cout <<

"after sort : "

;for

(int n: v)

std::cout << n <<

' ';

std::cout <<

'\n'

;// ****** rotation to the left

myrotate

(v.begin()

, v.

begin()

+1, v.

end())

; std::cout <<

"****** rotate left : "

;for

(int n: v)

std::cout << n <<

' ';

std::cout <<

'\n'

;// ****** rotation to the right

myrotate

(v.rbegin()

, v.

rbegin()

+1, v.

rend()

);std::cout <<

"****** rotate right : "

;for

(int n: v)

std::cout << n <<

' ';

std::cout <<

'\n'

;return0;

}

參考:

程式設計珠璣

cppreference.com

cplusplus.com

javascript實現繼承的幾種主要方法

1.原型鏈繼承 var supclass function name,supclass.prototype var sonclass function name,console.log sonclass.prototype sonclass.prototype new supclass 核心 son...

javascript實現繼承的幾種主要方法

1.原型鏈繼承 var supclass function name,supclass.prototype var sonclass function name,console.log sonclass.prototype sonclass.prototype new supclass 核心 son...

javascript實現繼承的幾種主要方法

1.原型鏈繼承 var supclass function name,supclass.prototype var sonclass function name,console.log sonclass.prototype sonclass.prototype new supclass 核心 son...