全排列總結

接觸全排列已經好長時間了,一直沒有抽空總結一下全排列的相關問題,下面來說一下!

排列

  一般地,從n個不同元素中取出mmn)個元素,按照一定的順序排成一列,叫做從n個元素中取出m個元素的一個排列(Arrangement)。特別地,當m=n時,這個排列被稱作全排列(Permutation)。

排列數公式:

特別,當n==m時為全排列的公式!

下一個全排列算法

  lintcode鏈接:http://www.lintcode.com/zh-cn/problem/next-permutation/

樣例

  左邊是原始排列,右邊是對應的下一個排列。

  1,2,3?→?1,3,2

  3,2,1?→?1,2,3

  1,1,5?→?1,5,1

下一個全排列算法——first

class Solution {
public:/*** @param nums: a vector of integers* @return: return nothing (void), do not return anything, modify nums in-place instead*/void nextPermutation(vector<int> &nums) {//調用<algorithm>中的下一個排列算法以及排序算法if(!next_permutation(nums.begin(), nums.end()))sort(nums.begin(), nums.end(), less<int>());
}
}

下一個全排列算法——second

class Solution {
public:/*** @param nums: a vector of integers* @return: return nothing (void), do not return anything, modify nums in-place instead*/void nextPermutation(vector<int> &nums) {int ld = -1;if(nums.size() < 1) return ;for(int i=nums.size()-2; i>=0; --i)//找到從右邊開始第一個比它右邊相鄰小的數的位置if(nums[i] < nums[i+1]){ld = i;break;}if(ld != -1){//找到從右邊開始,第一個比位置為ld所在數 大的數的位置int rd;for(int i=nums.size()-1; i>=0; --i)if(nums[ld] < nums[i]){rd = i;break;}swap(nums[ld], nums[rd]);//在交換之前,ld位置后面的已經是從大到小排好序的,已經沒有下一個排列了sort(nums.begin()+ld+1, nums.end());//交換之后,將ld后面的數置為最小的排列,從小到大排序
        }if(ld == -1){sort(nums.begin(), nums.end());}}
};

下一個全排列算法——third

class Solution {
public:/*** @param nums: a vector of integers* @return: return nothing (void), do not return anything, modify nums in-place instead*/void nextPermutation(vector<int> &nums) {        //挑戰不使用額外的空間, 其實和方法二的思路是一樣的int n = nums.size();for(int i=n-1; i>=0; --i)for(int j=n-1; j>i; --j)if(nums[i] < nums[j]){swap(nums[i], nums[j]);sort(nums.begin()+i+1, nums.end());return ;}sort(nums.begin(), nums.end());}
};

下一個全排列的思想:

?

  如上圖所示,該全排列對應的下一個全排列是和綠色框里的數字以及紅色框里的數字有關系的。顯而易見的是紅色框里的數列已經沒有下一個排列了,因為已經是一個遞減的序列了。為了得到下一個排列,我們將綠色框里的數字和紅色框里右邊起第一個比綠色框中數字大的數字交換位置(也就是4 和 5交換位置)。這樣還不是下一個全排列,交換之后紅色框內的序列為7 4 3 2 1 0, 將它變成遞增序列 0 1 2 3 4 7,就得到了下一個全排列。

  因為,一個全排列,末尾的一段區間肯定是遞減的(如上圖的紅色框區間),如果這段區間一直延伸到首部,那么也就沒有下一個全排列了,否則找到和這段區間最近的一個數字(上圖中綠色框中的數字),然后經過上述的處理就可以得到下一個全排列了。

  the second method 就是先找到綠色框數字的位置(ld), 然后在尋找紅色框中右邊第一個比綠色框中數字大的數字的位置(rd);

  the third method的意思就是從右邊開始尋找第一對(i, j),滿足nums[i]<nums[j], 對應second method中(ld, rd)。該方法沒有用到額外的存儲空間。

上一個全排列算法

  注:算法思想和下一個全排列的思想正好相反,步驟一致!

 ? ?lintcode鏈接:http://www.lintcode.com/zh-cn/problem/previous-permutation/

樣例

  給出排列[1,3,2,3],其上一個排列是[1,2,3,3]

  給出排列[1,2,3,4],其上一個排列是[4,3,2,1]

上一個全排列算法——first

class Solution {
public:/*** @param nums: An array of integers* @return: An array of integers that's previous permuation*/vector<int> previousPermuation(vector<int> &nums) {        //直接調用<algrotihm>中的算法prev_permutation()if(!prev_permutation(nums.begin(), nums.end()))sort(nums.begin(), nums.end(), greater<int>());return nums;          }
};

上一個全排列算法——second

class Solution {
public:/*** @param nums: An array of integers* @return: An array of integers that's previous permuation*/vector<int> previousPermuation(vector<int> &nums) {      int ld = -1;if(nums.size() <= 1) return nums;for(int i=nums.size()-2; i>=0; --i)//找到從右邊開始第一個比它右邊相鄰大的數的位置if(nums[i] > nums[i+1]){ld = i;break;}if(ld != -1){//找到從右邊開始,第一個比位置為ld所在數 小的數的位置int rd;for(int i=nums.size()-1; i>=0; --i)if(nums[ld] > nums[i]){rd = i;break;}swap(nums[ld], nums[rd]);//在交換之前,ld位置后面的已經是從小到大排好序的,已經沒有上一個排列了//交換之后,將ld后面的數置為最大的排列,從大到小排序sort(nums.begin()+ld+1, nums.end(), greater<int>());}if(ld == -1){sort(nums.begin(), nums.end(), greater<int>());}return nums;      }
};

上一個全排列算法——third

class Solution {
public:/*** @param nums: An array of integers* @return: An array of integers that's previous permuation*/vector<int> previousPermuation(vector<int> &nums) {     int n = nums.size();for(int i=n-1; i>=0; --i)for(int j=n-1; j>i; --j)if(nums[i] > nums[j]){swap(nums[i], nums[j]);sort(nums.begin()+i+1, nums.end(), greater<int>());return nums;}sort(nums.begin(), nums.end(), greater<int>());return nums;}
};

得到所有全排列算法

  lintcode鏈接:http://www.lintcode.com/zh-cn/problem/permutations/

全排列算法——first

  注:非遞歸,由下一個全排列算法——third方法實現

class Solution {
public:/*** @param nums: A list of integers.* @return: A list of permutations.*/vector<vector<int> > vv;vector<vector<int> > permute(vector<int> nums) {if(nums.size() == 0) return vv;sort(nums.begin(), nums.end());//方法1: 非遞歸實現int n = nums.size();bool flag = true;vv.push_back(nums);while(flag){flag = false;for(int i=n-1; i>=0; --i){for(int j=n-1; j>i; --j)if(nums[i] < nums[j]){swap(nums[i], nums[j]);sort(nums.begin()+i+1, nums.end());vv.push_back(nums);flag = true;break;}if(flag) break;}}       return vv;}
};

全排列算法——second

class Solution {
public:/*** @param nums: A list of integers.* @return: A list of permutations.*/vector<vector<int> > vv;vector<vector<int> > permute(vector<int> nums) {if(nums.size() == 0) return vv;sort(nums.begin(), nums.end());        //方法2:調用<algorithm>中的next_permutation()do{vv.push_back(nums);}while(next_permutation(nums.begin(), nums.end()));            return vv;}
};

全排列算法——third

  注:遞歸思路:一共有nn個位置,然后每個位置枚舉可能出現的數字(注意處理重復數字的情況)

class Solution {
public:/*** @param nums: A list of integers.* @return: A list of permutations.*/vector<vector<int> > vv;    ///
    int nn;//沒有 unique 之前的數組的大小map<int, int> mp;    void dfs_1(int cur, vector<int> &v, vector<int> nums){if(cur >= nn){vv.push_back(v);return;}for(int i=0; i<nums.size(); ++i)if(mp[nums[i]]){//如果nums[i]這個數字沒有枚舉完--mp[nums[i]];v.push_back(nums[i]);dfs_1(cur+1, v, nums);v.pop_back();++mp[nums[i]];}}    ////        vector<vector<int> > permute(vector<int> nums) {if(nums.size() == 0) return vv;sort(nums.begin(), nums.end());        //方法3:遞歸for(int i=0; i<nums.size(); ++i)//統計每個重復元素的個數++mp[nums[i]];nn = nums.size();unique(nums.begin(), nums.end());//對數組進行去重vector<int> v;dfs_1(0, v, nums);       return vv;}
};

全排列算法——forth

  注:遞歸思路:每一個數,不斷的和后面的數交換位置,每交換一次就會得到一個新的排列

class Solution {
public:/*** @param nums: A list of integers.* @return: A list of permutations.*/vector<vector<int> > vv;void dfs_2(int ld, int rd, vector<int> nums){if(ld == rd){vv.push_back(nums);return ;}for(int i=ld; i<=rd; ++i){swap(nums[ld], nums[i]);dfs_2(ld+1, rd, nums);swap(nums[ld], nums[i]);}}vector<vector<int> > permute(vector<int> nums) {if(nums.size() == 0) return vv;sort(nums.begin(), nums.end());        dfs_2(0, nums.size()-1, nums);return vv;}
};

排列序號(按字典序排序屬于第幾個全排列)

  注:不含重復數字的排列!

  lintcode鏈接:http://www.lintcode.com/zh-cn/problem/permutation-index/

樣例

  例如,排列[1,4,2]是第2個全排列。

排列序號算法

  算法思想請參考:http://www.cnblogs.com/hujunzheng/p/5020211.html

class Solution {
public:/*** @param A an integer array* @return a long integer*/long long permutationIndex(vector<int>& A) {//一個一個來肯定會超時// vector<int> permu(A.begin(), A.end());// sort(permu.begin(), permu.end());// int cnt = 0;// do{//     int i;//     for(i=0; i<A.size(); ++i)//         if(A[i]!=permu[i])//             break;//     ++cnt;//     if(i>=A.size()) break;// }while(next_permutation(permu.begin(), permu.end()));// return cnt;
                vector<int> a;int len = A.size();int cnt[len];cnt[len-1] = 0;a.push_back(A[len-1]);for(int i=len-2; i>=0; --i){//統計每個數后面有多少個比它小的數的個數vector<int>::iterator it = lower_bound(a.begin(), a.end(), A[i]);cnt[i] = it-a.begin();a.insert(it, A[i]);}long long ans=1, fac=1, c=1;for(int i=len-2; i>=0; --i)ans += (fac*=c++)*cnt[i];return ans;}
};

第k個排列

  注:給定?n?和?k,求123..n組成的排列中的第?k?個排列。

  lintcode鏈接:http://www.lintcode.com/zh-cn/problem/permutation-sequence/

樣例

  對于?n = 3, 所有的排列是:123, 132, 213, 231, 312, 321.

  如果?k = 4, 第4個排列為,231.

康托展開的公式?:

  X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0!,ai為整數,并且0<=ai<i(1<=i<=n)

  適用范圍:沒有重復元素的全排列

例題:

  找出第16個n = 5的序列(12345)。康托展開只要O(n)就行?,下面來說說具體怎么做:

  根據第一行的那個全排列公式,15 / 4! = 0 …15 ?=> ?有0個數比它小的數是1,所以第一位是1

  拿走剛才的余數15,用15 / 3! = 2 …3 ? => ?剩下的數里有兩個數比它小的是4(1已經沒了),所以第二位是4

  拿走余數3, 用 3 / 2! = 1 …1 ? => ?剩下的數里有一個數比它小的是3,所以第三位是3

  拿走余數1, 用 1/ ?1! = 1 …0 ? ?=> ?剩下的數里有一個數比它小的是 5(只剩2和5了),所以第四位是5

  所以排列是 1,4,3,5,2

第k個排列算法

?

class Solution {
public:/*** @param n: n* @param k: the kth permutation* @return: return the k-th permutation*/string getPermutation(int n, int k) {if(n==1) return "1";int f[n+1];bool use[n+1];f[1] = 0;f[2] = 1;memset(use, false, sizeof(use));for(int i=3; i<=n; ++i)f[i] = f[i-1]*(i-1);string ans = "";--k;//要計算的排列之前有多少個排列for(int i=n; i>=2; --i){int cnt = 0;int c = k/f[i];//假設該排列的這位數是x,c就是比x小的數(之前沒有用過)的個數k%=f[i];for(int j=1; j<=n; ++j){//尋找符合要求的x的值if(!use[j]){if(cnt == c){ans += j+'0';use[j] = true;break;}++cnt;}}}for(int j=1; j<=n; ++j)if(!use[j]){ans += j+'0';break;}return ans;}
};

?

帶重復元素的排列

  題目鏈接:http://www.lintcode.com/zh-cn/problem/permutations-ii/

帶重復元素的排列——first

  思路:next_permutation()本身支持帶重復元素的全排列

class Solution {
public:/*** @param nums: A list of integers.* @return: A list of unique permutations.*/vector<vector<int> > ans;vector<vector<int> > permuteUnique(vector<int> &nums) {// write your code here
        sort(nums.begin(), nums.end());do{ans.push_back(nums);}while(next_permutation(nums.begin(), nums.end()));

return ans;} };

?

帶重復元素的排列——second

  思路:枚舉每個位置肯能出現的數字。

class Solution {
public:/*** @param nums: A list of integers.* @return: A list of unique permutations.*/vector<vector<int> > ans;map<int, int> cnt;//記錄每個數字在nums中出現的次數 void dfs(int n, vector<int> &nums, vector<int> &v){if(v.size() >= n){ans.push_back(v);return ;}for(int i=0; i<nums.size(); ++i)if(cnt[nums[i]]){--cnt[nums[i]];v.push_back(nums[i]);dfs(n, nums, v);v.pop_back();++cnt[nums[i]];}}vector<vector<int> > permuteUnique(vector<int> &nums) {// write your code here
        sort(nums.begin(), nums.end());for(int i=0; i<nums.size(); ++i)++cnt[nums[i]];int n = nums.size();nums.erase(unique(nums.begin(), nums.end()), nums.end());//清除重復的元素vector<int> v;dfs(n, nums, v);return ans;}
};

?

帶重復元素的排列——third

  思路:和 “全排列算法——first”方法類似,唯一不同的是下面代碼中紅色部分

class Solution {
public:/*** @param nums: A list of integers.* @return: A list of unique permutations.*/vector<vector<int> > ans;vector<vector<int> > permuteUnique(vector<int> &nums) {// write your code here
        sort(nums.begin(), nums.end());       bool flag = true;while(flag){flag = false;ans.push_back(nums);for(int i=nums.size()-1; i>=0; --i){for(int j=nums.size()-1; j>i; --j)if(nums[i] < nums[j]){flag = true;while(j-1>i && nums[j-1]==nums[j]) --j;//如果前面有相同的數字,找到最左邊的數字
                        swap(nums[i], nums[j]);sort(nums.begin()+i+1, nums.end());break;}if(flag) break;}}return ans;}
};

?

轉載于:https://www.cnblogs.com/hujunzheng/p/5037121.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/531374.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/531374.shtml
英文地址,請注明出處:http://en.pswp.cn/news/531374.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

大小端問題傻傻分不清?

先來熟悉一下概念&#xff1a; 大端&#xff1a;數據的高位數據保存在低位地址&#xff0c;數據的低位數據保存在高地址 小端&#xff1a;數據的高位數據保存在高位地址&#xff0c;數據的低位數據保存在低地址為什么會存在大小端的問題&#xff1f; 這是因為在計算機系統中&a…

n個結點,不同形態的二叉樹(數目+生成)

題目鏈接&#xff1a; 不同的二叉查找樹&#xff1a;http://www.lintcode.com/zh-cn/problem/unique-binary-search-trees/ 不同的二叉查找樹 II&#xff1a;http://www.lintcode.com/zh-cn/problem/unique-binary-search-trees-ii/ 不同形態二叉樹的數目&#xff1a; 樣例 給出…

c++ stringstream(老好用了)

前言&#xff1a; 以前沒有接觸過stringstream這個類的時候&#xff0c;常用的字符串和數字轉換函數就是sscanf和sprintf函數。開始的時候就覺得這兩個函數應經很叼了&#xff0c;但是畢竟是屬于c的。c中引入了流的概念&#xff0c;通過流來實現字符串和數字的轉換方便多了。在…

mount --bind的用處

&#xff08;一&#xff09;mount --bind介紹 mount --bind的作用是將兩個目錄連接起來&#xff0c;例如&#xff1a;mount ---bind /dir1 /dir2 是將dir1目錄掛載到dir2目錄上&#xff0c;下面來實際演示一下&#xff1a; 上面的操作中首先創建了dir1 dir2兩個目錄&#xf…

gcc -strip編譯選項的作用

從字面上來看strip的意思是脫衣服、拆卸&#xff0c;那么gcc --strip的作用大概能猜錯來了。 沒錯就是有選擇地除去行號信息、重定位信息、調試段、typchk 段、注釋段、文件頭以及所有或部分符號表。 一旦使用該命令&#xff0c;則很難調試文件的符號&#xff0c;因此&#x…

lintcode 落單的數(位操作)

題目1 落單的數 給出2*n 1 個的數字&#xff0c;除其中一個數字之外其他每個數字均出現兩次&#xff0c;找到這個數字。 鏈接&#xff1a;http://www.lintcode.com/zh-cn/problem/single-number/ 樣例 給出 [1,2,2,1,3,4,3]&#xff0c;返回 4 挑戰 一次遍歷&#xff0c;常數級…

旋轉圖像

旋轉圖像 給定一個NN的二維矩陣表示圖像&#xff0c;90度順時針旋轉圖像。 看個例子 算法1&#xff1a; 如上圖所示&#xff0c;設一個N階二維矩陣&#xff0c;則將矩陣從外向里可以分成N/2個圈&#xff0c;例如&#xff08;1 2 3 4 8 12 16 15 14 13 9 5&#xff09;這是最外邊…

嵌入式開發板模擬器:QEMU

前兩天看微信公眾號時發現了一個嵌入式模擬器&#xff0c;感覺很不錯&#xff0c;自己動手安裝了一個&#xff0c;折騰了幾天&#xff0c;下載一直是個問題&#xff0c;特此記錄如下 模擬器大家應該都聽說過&#xff0c;有的小伙伴打游戲也會安裝模擬器&#xff0c;今天我們介紹…

gcc: weak_alias如何使用

本文主要說明weak和alias是什么和如何使用它 __attribute__是用來說明函數的屬性&#xff0c;weak和alias分別是兩個屬性。 &#xff08;一&#xff09;強符號和弱符號&#xff1a; 強符號&#xff1a;已經初始化的全局變量和未被weak修飾的函數弱符號&#xff1a;未初始化的全…

靜態Include和動態Include測試并總結

主要代碼 hjzgg.css .center-div{width:auto;margin-left: 40%;margin-right: 40%;display: block;position: absolute;top:0px;left:0px; }.text-div{margin-top: 80px; }.hjzgg-div{color:transparent;font-size:20px;font-weight: bold;letter-spacing:2px;-webkit-animatio…

linux終端常用快捷鍵

CTRLALTT 打開終端 CTRLD 關閉終端 CTRL SHIFT "" 放大終端字體 CTRL “-” 縮小終端字體 CTRL r 查找歷史命令 CTRLu 刪除光標前面所有內容 CTRLw 刪除光標左邊的單詞 CTRL k 刪除光標后面的所有內容 CTRLL 清除當前屏幕內容 CTRLa 光標移到開始位置 CTRLe 光標移到…

ueditor的配置和使用

ueditor下載好之后直接復制到項目的WebContent目錄下&#xff0c;并將ueditor\jsp\lib下的jar包復制或者剪切到項目的lib目錄下。先看一下效果&#xff0c;如下&#xff1a; 1.文件的上傳 首先在ueditor/jsp目錄下找到config.json文件&#xff0c;就拿Image上傳來說吧。 "…

windows上搭建NFS服務器

在進行嵌入式開發的時候&#xff0c;我們常用的做法是搭建NFS服務器&#xff0c;然后使把文件系統、調試程序放在NFS服務器上&#xff0c;這樣可以方便調試&#xff0c;以前都是在linux里面開啟NFS服務器&#xff0c;今天來說下window里的nfs服務器–haneWin 一、軟件安裝和使…

計算機是如何啟動的?從未上電到操作系統啟動

計算機是如何啟動的&#xff0c;網絡上很多博文1都從 BIOS 程序的加載開始說起&#xff0c;有的也跳到 BIOS 程序加載 Bootloader 階段。個人認為把這個過程稱為操作系統是如何被加載并啟動應該更加貼切一點。同時&#xff0c;也有計算機硬件大神的文章[1][5]詳細分析計算機加電…

Hibernate注解

前言&#xff1a; 最近正在學習Hibernate通過注解&#xff08;annotation&#xff09;來管理映射關系&#xff0c;以前都是通過XML映射文件。下面拿個小例子說一下。 數據庫物理模型&#xff1a; 數據庫的描述&#xff1a; 一篇博客隨筆可以分到不同的類中&#xff0c;一個類中…

js表單動態添加數據并提交

情景1&#xff1a;已經存在form對象了&#xff0c;動態為form增加對象并提交 function formAppendSubmit(){var myform$(#newArticleForm); //得到form對象var tmpInput$("<input typetext nameblogArticleForm.articleContent/>");tmpInput.attr("value&…

*++p和*p++的區別

首先你應該明白* 和 的優先級是相同的&#xff0c;而且他們的結合性是從又往左的 #include <stdio.h>int main(int argc ,char * argv[]) {int str[]{1,2,3,4,5,6,7,8,9,10};int *p str;int a *p;//a*p ,pp1即a1&#xff0c;p&str[1]int b *p;//pp1,b*p即p&s…

zyUpload+struct2完成文件上傳

前言&#xff1a; 最近在寫自己的博客網站&#xff0c;算是強化一下自己對s2sh框架的理解。期間遇到了很多問題&#xff0c;這些問題在寫之前都考慮過&#xff0c;感覺也就是那樣吧。但正真遇到了&#xff0c;也挺讓人難受的。就利用zyUpload這個js插件實現文件的上傳&#xff…

gbd的簡單使用(一)

這篇文章將gdb的簡單使用&#xff0c;通過此篇文章你能學習到使用gdb進行調試程序 在Linux中編寫程序時&#xff0c;如何進行程序的debug工作呢&#xff1f;今天來介紹下gdb這個工具&#xff0c;可以在Linux下直接man gdb查看幫助信息 &#xff08;一&#xff09;gdb命令介紹 …

java發送內嵌圖片郵件

前言&#xff1a; 博客系統中需要郵件服務的功能&#xff0c;以前寫過類似的功能&#xff0c;不過功能太簡單了&#xff0c;僅僅是發送文本內容&#xff0c;現在嘗試一下發送內嵌圖片郵件&#xff01; 準備工作&#xff1a; 請參考&#xff1a;http://www.cnblogs.com/hujunzhe…