STL next permutation排序演算法

2021-09-03 02:25:40 字數 3404 閱讀 6043

排列就是一次對物件序列或值序列的重新排列。例如,「abc」中字元可能的排列是:

"abc", "acb", "bac", "bca", "cab", "cba"
三個不同的字元有 6 種排列,這個數字是從 3*2*1 得到的。一般來說,n 個不同的字 符有 n! 種排列,n! 是 nx(n_1)x(n-2)...x2x1。很容易明白為什麼要這樣算。有 n 個物件 時,在序列的第乙個位置就有 n 種可能的選擇。對於第乙個物件的每一種選擇,序列的第 二個位置還剩下 n-1 種選擇,因此前兩個有 nx((n-1) 種可能選擇。在選擇了前兩個之後, 第三個位置還剩下 n-2 種選擇,因此前三個有 nx(n-1)x(n-2) 種可能選擇,以此類推。序列的末尾是 hobson 選擇,因為只剩下 1 種選擇。

對於包含相同元素的序列來說,只要乙個序列中的元素順序不同,就是一種排列。next_permutation() 會生成乙個序列的重排列,它是所有可能的字典序中的下乙個排列,預設使用 < 運算子來做這些事情。它的引數為定義序列的迭代器和乙個返回布林值的函式,這個函式在下乙個排列大於上乙個排列時返回 true,如果上乙個排列是序列中最大的,它返回 false,所以會生成字典序最小的排列。

下面展示了如何生成乙個包含 4 個整數的 vector 的排列:

std::vectorrange ;

do );

std::cout << std::endl;

}while(std::next_permutation(std::begin(range), std::end(range)));

當 next_permutation() 返回 false 時,迴圈結束,表明到達最小排列。這樣恰好可以生成 序列的全部排列,這只是因為序列的初始排列為 1、2、3、4,這是排列集合中的第乙個排列。有一種方法可以得到序列的全排列,就是使用 next_permutation() 得到的最小排列:

std::vectorwords ;

while(std::next_permutation(std::begin(words), std::end(words)));

do);

std::cout << std::endl;

} while(std::next_permutation(std::begin(words), std::end(words)));

words 中的初始序列不是最小的排列序列,迴圈會繼續進行,直到 words 包含最小排列。do-wliile 迴圈會輸出全部的排列。如果想執行這段**,需要記住它會生成 8! 種排列,從而輸出 40320 行,因此首先可能會減少 words 中元素的個數。

當排列中的每個元素都小於或等於它後面的元素時,它就是元素序列的最小排列,所以可以用 min_element() 來返回乙個指向序列中最小元素的迭代器,然後用 iter_swap() 演算法交換兩個迭代器指向的元素,從而生成最小的排列,例如:

std::vectorwords ;

for (auto iter = std::begin(words); iter != std::end(words)-1 ;++iter)

std::iter_swap(iter, std::min_element(iter, std::end(words)));

for 迴圈從序列的第乙個迭代器開始遍歷,直到倒數第二個迭代器。for 迴圈體中的語句會交換 iter 指向的元素和 min_element() 返回的迭代器所指向的元素。這樣最終會生成乙個最小排列,然後可以用它作為 next_permutation() 的起始點來生成全排列。

在開始生成全排列之前,可以先生成乙個原始容器的副本,然後在迴圈中改變它,從 而避免到達最小排列的全部開銷。

std::vectorwords ;

auto words_copy = words; // copy the original

do );

std::cout << std::endl;

std::next_permutation(std::begin(words), std::end(words));

}while(words != words_copy); // continue until back to the original

迴圈現在會繼續生成新的排列,直到到達原始排列。下面是乙個找出單詞中字母的全部排列的示例:

// finding rearrangements of the letters in a word

#include // for standard streams

#include // for iterators and begin() and end()

#include // for string class

#include // for vector container

#include // for next_permutation()

using std::string;

int main()

; do

while(word != word_copy);

size_t count{}, max;

for(const auto& wrd : words)

std::cout << wrd << ((++count % max == 0) ? '\n' : ' ');

std::cout << std::endl;

words.clear(); // remove previous permutations

}}

這段**會從標準輸入流讀取乙個單詞到 word 中,然後在 word_copy 中生成乙個副本,將 word 中字元的全排列儲存到 words 容器中。這個程式會繼續處理單詞直到按下 ctrl+z 組合鍵。用 word 的副本來判斷是否已經儲存了全排列。然後所有的排列會被寫入輸出流,8 個一行。像之前說的那樣,隨著被排列元素個數的增加,排列的個數增加也很快,所以這裡不要嘗試使用太長的單詞。

可以為 next_permutation() 提供乙個函式物件作為第三個引數,從而用這個函式物件定 義的比較函式來代替預設的比較函式。下面展示如何使用這個版本的函式,通過比較最後 乙個字母的方式來生成 words 序列的排列:

std::vectorwords ;

do );

std::cout << std::endl;

} while(std::next_permutation(std::begin(words), std::end(words),(const string& s1, const string& s2) ));

通過傳入乙個 lambda 表示式作為 next_permutation() 的最後乙個引數,這段**會生成 words 中元素的全部 24 種排列。

排序算發 計數排序

前面已經記錄過插入排序,歸併排序,快速排序,堆排序等四種排序。它們都有個共性,就是通過多次比較來得出前後順序,這種叫做比較排序,當然除此之外也有非比較排序。今天記錄的計數排序就是一種非比較排序。算發思想 有一串資料,如果我們知道每乙個元素公升序在陣列中的第幾個,那麼我們就知道了公升序的結果。而對於如...

快速排序算方法

快速排序演算法的原理 在待排序的n個記錄中任取乙個記錄 通常取第乙個記錄 為分割槽標準,把所有小於該排序碼的記錄移到左邊,把所有大於該排序碼的記錄移到右邊,中間放所選記錄,稱之為一趟排序 然後,對前後兩個子串行分別重複上述過程。繼續下去,知道所有記錄都排好序。演算法 標頭檔案定義結構體 1 ifnd...

c 練習 高算使用者排序

題目 高效能計算需要統計年計算量,按core 小時收費 要求 1.找出使用時間 24小時的使用者,刪除掉該記錄,輸出最終排序後結果。2.修改並補全demo.cpp 檔案 zhang 0.292778 huang 0.020000 xing 269970.744200 lisi 1142826.668...