名稱空間
變體
操作

std::ranges::rotate

來自 cppreference.com
< cpp‎ | 演算法‎ | 範圍
 
 
演算法庫
有約束演算法與針對範圍的演算法 (C++20)
有約束的演算法,例如 ranges::copyranges::sort 等……
執行策略 (C++17)
排序及相關操作
劃分操作
排序操作
二分搜尋操作
(於已劃分範圍上)
集合操作(於已排序範圍上)
歸併操作(於已排序範圍上)
堆操作
最小/最大值操作
(C++11)
(C++17)
字典序比較操作
排列操作
C 庫
數值操作
未初始化記憶體上的操作
 
受約束演算法
此選單中的所有名稱均屬於名稱空間 std::ranges
非修改序列操作
修改序列操作
劃分操作
排序操作
二分查詢操作(在已排序的範圍內)
       
       
集合操作(於已排序範圍上)
堆操作
最小/最大值操作
       
       
排列操作
摺疊操作
數值操作
(C++23)            
對未初始化儲存的操作
返回型別
 
定義於標頭檔案 <algorithm>
呼叫簽名 (Call signature)
template< std::permutable I, std::sentinel_for<I> S >

constexpr ranges::subrange<I>

    rotate( I first, I middle, S last );
(1) (C++20 起)
template< ranges::forward_range R >

requires std::permutable<ranges::iterator_t<R>>
constexpr ranges::borrowed_subrange_t<R>

    rotate( R&& r, ranges::iterator_t<R> middle );
(2) (C++20 起)
1) 對元素範圍執行*左旋轉*。具體來說,`ranges::rotate` 會交換範圍 `[`first`, `last`)` 中的元素,使得元素 *middle 成為新範圍的第一個元素,而 *(middle - 1) 成為最後一個元素。
如果 `[`first`, `last`)` 不是有效範圍,或者 middle 不在 `[`first`, `last`)` 中,則行為未定義。
2)(1),但使用 r 作為範圍,如同使用 ranges::begin(r) 作為 firstranges::end(r) 作為 last

本頁描述的類函式實體是 演算法函式物件(非正式地稱為 niebloids),即

目錄

[編輯] 引數

first, last - 定義要旋轉的元素範圍的迭代器-哨兵對
r - 要旋轉的元素範圍
middle - 指向旋轉後範圍開頭應出現的元素的迭代器

[編輯] 返回值

{new_first, last},其中 `new_first` 與 ranges::next(first, ranges::distance(middle, last)) 比較相等,並指向 `first` 所指向元素的新位置。

[編輯] 複雜度

最壞情況下為*線性*:ranges::distance(first, last) 次交換。

[編輯] 注意

如果 `I` 建模 `bidirectional_iterator` 或(更好)`random_access_iterator`,`ranges::rotate` 在常見實現中具有更好的效率。

當迭代器型別建模 `contiguous_iterator` 且交換其值型別不呼叫非平凡特殊成員函式或ADL查詢的 `swap` 時,實現(例如 MSVC STL)可能會啟用向量化。

[編輯] 可能的實現

另請參閱 libstdc++MSVC STL 中的實現。

struct rotate_fn
{
    template<std::permutable I, std::sentinel_for<I> S>
    constexpr ranges::subrange<I>
        operator()(I first, I middle, S last) const
    {
        if (first == middle)
        {
            auto last_it = ranges::next(first, last);
            return {last_it, last_it};
        }
        if (middle == last)
            return {std::move(first), std::move(middle)};
 
        if constexpr (std::bidirectional_iterator<I>)
        {
            ranges::reverse(first, middle);
            auto last_it = ranges::next(first, last);
            ranges::reverse(middle, last_it);
 
            if constexpr (std::random_access_iterator<I>)
            {
                ranges::reverse(first, last_it);
                return {first + (last_it - middle), std::move(last_it)};
            }
            else
            {
                auto mid_last = last_it;
                do
                {
                    ranges::iter_swap(first, --mid_last);
                    ++first;
                }
                while (first != middle && mid_last != middle);
                ranges::reverse(first, mid_last);
 
                if (first == middle)
                    return {std::move(mid_last), std::move(last_it)};
                else
                    return {std::move(first), std::move(last_it)};
            }
        }
        else
        { // I is merely a forward_iterator
            auto next_it = middle;
            do
            { // rotate the first cycle
                ranges::iter_swap(first, next_it);
                ++first;
                ++next_it;
                if (first == middle)
                    middle = next_it;
            }
            while (next_it != last);
 
            auto new_first = first;
            while (middle != last)
            { // rotate subsequent cycles
                next_it = middle;
                do
                {
                    ranges::iter_swap(first, next_it);
                    ++first;
                    ++next_it;
                    if (first == middle)
                        middle = next_it;
                }
                while (next_it != last);
            }
 
            return {std::move(new_first), std::move(middle)};
        }
    }
 
    template<ranges::forward_range R>
    requires std::permutable<ranges::iterator_t<R>>
    constexpr ranges::borrowed_subrange_t<R>
        operator()(R&& r, ranges::iterator_t<R> middle) const
    {
        return (*this)(ranges::begin(r), std::move(middle), ranges::end(r));
    }
};
 
inline constexpr rotate_fn rotate {};

[編輯] 示例

`ranges::rotate` 是許多演算法中常見的構建塊。此示例演示了插入排序

#include <algorithm>
#include <iostream>
#include <numeric>
#include <string>
#include <vector>
 
int main()
{
    std::string s(16, ' ');
 
    for (int k {}; k != 5; ++k)
    {
        std::iota(s.begin(), s.end(), 'A');
        std::ranges::rotate(s, s.begin() + k);
        std::cout << "Rotate left (" << k << "): " << s << '\n';
    }
    std::cout << '\n';
 
    for (int k {}; k != 5; ++k)
    {
        std::iota(s.begin(), s.end(), 'A');
        std::ranges::rotate(s, s.end() - k);
        std::cout << "Rotate right (" << k << "): " << s << '\n';
    }
 
    std::cout << "\nInsertion sort using `rotate`, step-by-step:\n";
 
    s = {'2', '4', '2', '0', '5', '9', '7', '3', '7', '1'};
 
    for (auto i = s.begin(); i != s.end(); ++i)
    {
        std::cout << "i = " << std::ranges::distance(s.begin(), i) << ": ";
        std::ranges::rotate(std::ranges::upper_bound(s.begin(), i, *i), i, i + 1);
        std::cout << s << '\n';
    }
    std::cout << (std::ranges::is_sorted(s) ? "Sorted!" : "Not sorted.") << '\n';
}

輸出

Rotate left (0): ABCDEFGHIJKLMNOP
Rotate left (1): BCDEFGHIJKLMNOPA
Rotate left (2): CDEFGHIJKLMNOPAB
Rotate left (3): DEFGHIJKLMNOPABC
Rotate left (4): EFGHIJKLMNOPABCD
 
Rotate right (0): ABCDEFGHIJKLMNOP
Rotate right (1): PABCDEFGHIJKLMNO
Rotate right (2): OPABCDEFGHIJKLMN
Rotate right (3): NOPABCDEFGHIJKLM
Rotate right (4): MNOPABCDEFGHIJKL
 
Insertion sort using `rotate`, step-by-step:
i = 0: 2420597371
i = 1: 2420597371
i = 2: 2240597371
i = 3: 0224597371
i = 4: 0224597371
i = 5: 0224597371
i = 6: 0224579371
i = 7: 0223457971
i = 8: 0223457791
i = 9: 0122345779
Sorted!

[編輯] 另請參閱

複製並旋轉一個範圍的元素
(演算法函式物件)[編輯]
反轉一個範圍中元素的順序
(演算法函式物件)[編輯]
旋轉一個範圍中元素的順序
(函式模板) [編輯]