<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          ?LeetCode刷題實(shí)戰(zhàn)46:全排列

          共 3925字,需瀏覽 8分鐘

           ·

          2020-09-24 07:54

          算法的重要性,我就不多說了吧,想去大廠,就必須要經(jīng)過基礎(chǔ)知識和業(yè)務(wù)邏輯面試+算法面試。所以,為了提高大家的算法能力,這個(gè)公眾號后續(xù)每天帶大家做一道算法題,題目就從LeetCode上面選 !

          今天和大家聊的問題叫做?全排列,我們先來看題面:

          https://leetcode-cn.com/problems/permutations/

          Given a collection of?distinct?integers, return all possible permutations.

          題意


          給定一個(gè) 沒有重復(fù) 數(shù)字的序列,返回其所有可能的全排列

          樣例

          輸入: [1,2,3]
          輸出:
          [
          ??[1,2,3]
          ,
          ??[1,3,2],
          ??[2,1,3],
          ??[2,3,1],
          ??[3,1,2],
          ??[3,2,1]
          ]

          解題

          來源:
          https://www.cnblogs.com/techflow/p/12636563.html

          回溯法

          我們在之前的文章當(dāng)中分析過,全排列問題,可以看成是搜索問題,從而近似成八皇后問題。在八皇后問題當(dāng)中,我們枚舉的是棋盤的每一行當(dāng)中的皇后放置的位置,而全排列其實(shí)也一樣,我們要枚舉每一個(gè)元素放置的位置。不過八皇后當(dāng)中要求皇后除了不能同行同列之外還不能同對角線,而我們排列元素可以忽略這個(gè)要求。也就是說我們把每一行皇后放置的列號看成是每個(gè)元素?cái)[放的位置,并且忽略同對角線的限制的話,那么八皇后問題和全排列問題就完全一樣了。
          如果還不理解,可以參考一下下圖,我們給皇后編號,把皇后同樣看成是序列當(dāng)中的元素,那么八皇后的擺放位置剛好可以映射成一種排列。映射的方式非常簡單,就是我們忽略行的信息,依次記錄下皇后擺放的列號。
          如果你能想通這兩個(gè)看似完全不同的問題當(dāng)中的相似之處,說明你對搜索問題的理解已經(jīng)有些入門了。
          思路清楚了,總之我們要枚舉皇后擺放的狀態(tài)。你可以按順序遍歷位置,然后枚舉各個(gè)位置上放置的皇后,也可以順序遍歷皇后,枚舉當(dāng)前皇后可以放置的位置。兩者是等價(jià)的,你可以根據(jù)自己的理解進(jìn)行操作。
          一般來說我喜歡遍歷位置,枚舉皇后。因?yàn)闀?huì)引起沖突的是皇后,而不是位置。我們往往要判斷皇后之間的關(guān)系以及皇后的狀態(tài),所以我們枚舉皇后會(huì)比較貼合思路
          所以我們把之前八皇后的代碼拿過來稍作修改即可,為了放置一個(gè)皇后重復(fù)放置在多個(gè)位置,我們需要存儲(chǔ)皇后的狀態(tài),即有沒有放置過。一般競賽當(dāng)中這種標(biāo)記的變量稱為flag,如果標(biāo)記多個(gè)那就是flag數(shù)組。更多細(xì)節(jié)我們來看代碼:
          class?Solution:
          ????def?dfs(self,?nums,?n,?i,?cur,?ret,?flag):
          ????????if?i?==?n:
          ????????????ret.append(cur.copy())
          ????????????return
          ????????for?p?in?range(n):
          ????????????#?遍歷所有元素
          ????????????#?如果p元素已經(jīng)放置過了,跳過
          ????????????if?flag[p]:
          ????????????????continue
          ????????????#?當(dāng)前位置放置p
          ????????????cur.append(nums[p])
          ????????????#?flag[p]置為True
          ????????????flag[p]?=?True
          ????????????#?遞歸
          ????????????self.dfs(nums,?n,?i+1,?cur,?ret,?flag)
          ????????????#?回溯
          ????????????cur.pop()
          ????????????flag[p]?=?False
          ????????
          ????def?permute(self,?nums:?List[int])?->?List[List[int]]:
          ????????ret?=?[]
          ????????n?=?len(nums)
          ????????#?記錄元素i有沒有放置過
          ????????flag?=?[False?for?_?in?range(n)]
          ????????self.dfs(nums,?n,?0,?[],?ret,?flag)
          ????????return?ret
          代碼很短,細(xì)節(jié)也不多,只要理解了我們是按照順序遍歷位置,然后對于每一個(gè)位置遍歷可以放置的元素,然后遞歸回溯即可。

          其他方法

          回溯法是這個(gè)問題的標(biāo)準(zhǔn)解法,那么這題還有沒有其他方法呢?
          其實(shí)是有的,也不難,在LeetCode31題的文章,也就是上面那個(gè)鏈接的文章當(dāng)中我們解決了一個(gè)叫做下一個(gè)排列的問題。在這道題當(dāng)中,我們給定一個(gè)序列,要求返回在它所有的全排列當(dāng)中剛好字典序比它大1的排列,這個(gè)方法稱為next_permutation。
          如果還記得這道題的話就好辦了,我們使用它很容易解出當(dāng)前的問題。因?yàn)槲覀冎恍枰@得給定序列的最小排列,然后不停地調(diào)用這個(gè)方法就好了,直到?jīng)]有更大的序列退出即可。從最小的序列一直獲取到最大的,當(dāng)然就是全排列了。
          在LeetCode31題當(dāng)中,這是一個(gè)inplace的方法,沒有返回值。并且當(dāng)序列達(dá)到最大的時(shí)候,會(huì)自動(dòng)再從最小的開始。我們需要稍稍修改一下,加上一個(gè)返回值,表示當(dāng)前的序列是否是最大的。如果序列達(dá)到最大,說明我們可以不用繼續(xù)往下尋找了,我們r(jià)eturn一個(gè)True,表示可以退出了,否則我們r(jià)eturn False,表示還有其他結(jié)果。
          本質(zhì)上我們是從最小的排列開始,不停地用一個(gè)叫做get_next的方法獲取比當(dāng)前序列大的下一個(gè)序列,當(dāng)沒有更大的序列的時(shí)候,說明我們已經(jīng)獲得了所有的排列,那么直接返回結(jié)果即可。如果忽略get_next當(dāng)中的邏輯,這個(gè)代碼其實(shí)只有幾行:
          其實(shí)這是一個(gè)取巧的辦法,利用之前的思路我們完全不用思考,幾乎可以無腦得到答案。但是從另外一個(gè)角度來說,這也是算法的魅力,畢竟通往終點(diǎn)的路往往不止一條。
          最后我們來看下代碼,如果你不懂怎么算next_permutation光看注釋是很難看懂的,劃到上面的鏈接看看吧。
          class?Solution:
          ????def?get_next(self,?nums:?List[int]):
          ????????"""
          ????????Do?not?return?anything,?modify?nums?in-place?instead.
          ????????"""

          ????????#?長度
          ????????n?=?len(nums)
          ????????#?記錄圖中i-1的位置
          ????????pos?=?n?-?1
          ????????for?i?in?range(n-1,?0,?-1):
          ????????????#?如果降序破壞,說明找到了i
          ????????????if?nums[i]?>?nums[i-1]:
          ????????????????pos?=?i-1
          ????????????????break
          ????????????????
          ????????for?i?in?range(n-1,?pos,?-1):
          ????????????#?從最后開始找大于pos位置的
          ????????????if?nums[i]?>?nums[pos]:
          ????????????????#?先交換元素,在進(jìn)行翻轉(zhuǎn)
          ????????????????nums[i],?nums[pos]?=?nums[pos],?nums[i]
          ????????????????#?翻轉(zhuǎn)[pos+1,?n]區(qū)間
          ????????????????nums[pos+1:]?=?nums[n:pos:-1]
          ????????????????return?False
          ????????return?True
          ????????
          ????????
          ????def?permute(self,?nums:?List[int])?->?List[List[int]]:
          ????????ret?=?[]
          ????????#?從小到大排序,獲得最小排列
          ????????nums?=?sorted(nums)
          ????????ret.append(nums.copy())
          ????????#?如果還有下一個(gè)排列則繼續(xù)調(diào)用
          ????????while?not?self.get_next(nums):
          ????????????#?要.copy()是因?yàn)镻ython中存儲(chǔ)的引用,如果不加copy
          ????????????#?會(huì)導(dǎo)致當(dāng)nums發(fā)生變化之后,ret中存儲(chǔ)的數(shù)據(jù)也會(huì)變化
          ????????????ret.append(nums.copy())
          ????????return?ret
          今天的問題并不難,只是Medium難度,并且題目的題意還是之前見過的,主要是給大家加深一下回溯算法的映像用的,沒什么太多的新內(nèi)容。

          好了,今天的文章就到這里,如果覺得有所收獲,請順手點(diǎn)個(gè)在看或者轉(zhuǎn)發(fā)吧,你們的支持是我最大的動(dòng)力。


          上期推文:


          LeetCode1-20題匯總,速度收藏!
          LeetCode21-40題匯總,速度收藏!
          LeetCode刷題實(shí)戰(zhàn)40:組合總和 II
          LeetCode刷題實(shí)戰(zhàn)41:缺失的第一個(gè)正數(shù)
          LeetCode刷題實(shí)戰(zhàn)42:接雨水
          LeetCode刷題實(shí)戰(zhàn)43:字符串相乘
          LeetCode刷題實(shí)戰(zhàn)44:通配符匹配
          LeetCode刷題實(shí)戰(zhàn)45:跳躍游戲 II


          瀏覽 47
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  午夜成人网在线 | 亚洲操逼AV | 超碰自拍 | 亚洲欧美久久翔田千里 | 俺也去网av |