python 组合排列,python列表排序算法

  python 组合排列,python列表排序算法

  array的问题是从指定数量的元素中检索指定数量的元素并排序。组合的问题是从N个不同的数中选出M个数,输出所有的组合方法。这两个问题是经典回溯算法的问题。先解决排列问题,再解决组合问题。

  假设你需要排列四个数[1,3,4,5]。我们知道第一个数字有四个选择,第二个数字有三个选择,第三个数字有两个选择,第四个数字有一个选择。你其实可以不用回溯算法写代码。伪代码如下。

  nums=[ 1,3,4,5 ]

  对于数字中的:

  nums1=不带的nums

  解决方案1=[ a]

  对于nums1中的b:

  nums2=不带b的nums1

  解决方案2=解决方案1。追加(b))

  对于nums2中的c:

  nums3=不带c的nums2

  方案3=方案2。追加(c)

  对于nums3中的d:

  解决方案4=解决方案3。追加(c)

  打印(解决方案4)。

  上述伪代码的思想是从列表中排除已经使用的选择,并使用for循环继续遍历下一个位置的数量。这样做的缺点是代码很麻烦,写代码前必须知道nums的长度。用回溯算法来解决这个问题就没有这个缺点了。

  定义置换(nums)方法。输入nums数组。permute(nums)方法输出nums中的所有数字数组。例如,permute ([1,2,3])的输出:

  [ 1,2,3 ],[ 1,3,2 ],[ 2,1,3,1 ],[ 3,1,2 ],[ 3,2,2 ],[ 3,2,2,1 ] ]

  置换(nums)方法的思想如下。

  选择第一个位置的数字,称为X;

  使用permute(nums)方法得到剩余数字的所有排列;

  将x放在这些数组的第一个位置。保存答案;

  回到第一步,改X;

  试完所有的X,输出答案。

  permute(nums)方法的边界条件是,如果nums数组为空,则将当前数组添加到结果集中并返回。

  以[1,2,3,4]为例。

  一开始,我只关心初始位置的数量。总共有四个选择。我们在这四个选项之间来回切换,第一次选择1。

  我们知道从1开始的序列组合等于1“[2,3,4]的序列组合”。此时,如果再次调用permute(nums)方法并传递[2,3,4],就会得到“[2,3,4]数组组合”。

  获得6个“[2,3,4]排列组合”后,在数组中加1,放在第一个位置。

  回到第一步,将第一个位置的编号替换为2。

  递归步骤如图1所示。最初递归到底部时保存的组合是[1,2,3,4]。看看接下来的步骤。

  图1:第一个结果

  如图,我们回到三楼。第三层次,三选四。我们试过3次。现在是4。将4放在第三个位置,并将剩余的数字3作为参数传递给第四级。至此,获得了第二数组[1,2,4,3]的组合。

  图2:第二个结果

  如图3所示,我们再次回到三楼。三楼的两个选项,33543和4,因为试过了,会继续退到二楼。二楼的选择是234。尝试从3开始继续。把3放在第三个位置。把剩下的号码——2和4传到三楼。然后,把2放在第三个位置,把4给第四层。第三个结果是[1,3,2,4]。

  图3:第三个结果

  重复以上步骤得到顺序:[1,3,4,2],[1,4,2,3,2],[2,1,3,4],…,[4,3,2,1]。

  序列问题代码:

  对num排序并返回所有组合

  defpermute(nums,solution=[]):

  If nums: #边界条件

  结果追加(解决方案)

  Return #返回

  forIinrange(Len ) nums):

  New solution=solution [nums [I]] #将nums中的数字按顺序排列,放在下一个组合位置。

  新列表=nums [0: I] nums [I 1:] #新列表包含除nums[i]以外的所有号码

  继续使用permute(newlist,newSolution) #数组

  新列表中的数字

  res=[]

  置换([1,2,3,4])

  解就是当前的排列组合。当初,解=[]。放置第一个数字后,解=[1],放置第二个数字后,解=[1,2]。

  注意,变量解只存在于当前层中。比如我们从第一层传到第二层,第二层的解和第一层的解同名,但不是同一个变量。当第二层的解等于[1,2]时,第一层的解仍然是[1]。主动改变的是newSolution,也就是传入下一层的数组。

  如图4所示,每一层的新解就是下一层的解。

  图4:解决方案和新解决方案之间的关系

  比如我们从第四层回到第三层,第三层的解不变,还是[1,2],但是第三层的For循环把newSolution改成了[1,2,4]。

  虽然图4中没有显示,但是当我们再次递归到第四层时,第四层会放弃原来的解和newSolution,在内存中重新声明一个名为solution的变量和一个名为newSolution的变量,然后将解赋给[1,2,4]。每一次进步,我们都重新创造变量,赋予它们新的值。但是每次回去,因为方法不变,变量不变。

  因为解是“一次性”变量,所以我们不必深度复制解并添加到结果列表中,只需使用append()方法即可。同理,每层的num只存在于当前层。当我们从一层递归到另一层时,每一层的num都是独立存在的,而newList是变化的。新列表决定了下一层的num是什么,只有当新列表改变并且方法被重新传递到下一层时,num才会改变。

  接下来,解决组合问题。假设我们需要从[1,2,3,4]中选择2个数字。我们知道有六种选择:[1,2]、[1,3]、[1,4]、[2,3]、[2,4]、[3,4]。

  我们将定义组合(nums)方法。输入nums数组,combination()方法输出nums中数字的所有排列。例如,permate ([1,2,3,4])输出[[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]。

  排列问题和组合问题的性质非常相似。在组合问题中,我们也可以只关心第一个数,然后让递归的方法得到其余数的组合。

  步骤如下:

  选择第一个位置的数字,称之为x。

  使用combination()方法获得剩余数字的组合。

  将X放在第二步中获得的数组的第一个位置。保存答案。

  回到第一步,改x。

  试完所有的X,输出答案。

  如果传递给combination()方法的nums数组为空,则停止递归,并将当前排列添加到结果集中。

  比如第一步,我们只关心在[1,2,3,4]中选一个数。有四个选择。当我们选择1时,我们知道答案等于1加上“在[2,3,4]中选择1个数字的组合”。同样,当我们选择第一个数字为2时,答案等于2加上“[1,4]

  combination()方法和permute()方法的区别在于第二步“余数”的定义。在permute()方法中,“剩余数”是除x之外的所有数,但是在combination()方法中,“剩余数”是x之后的所有数,比如nums是[1,2,3,4],当前数是2,那么permute()方法的“剩余数”是[1,3,4],而combination()方法的“剩余数”是[3,4]。

  这是因为用过的数在组合题中是不能用的。在排列问题中,数字的位置是有意义的,但在组合问题中,数字的位置是没有意义的。比如[2,1]和[1,2]是两种不同的排列,但却是同一种组合。

  代码如下:

  #nums是要组合的数字列表,solution是当前组合,n是所选数字的个数。

  #输出所有组合

  定义组合(数字,解决方案,n):

  If==0: #边界条件

  结果追加(解决方案)

  返回

  对于范围内的I(len(nums)):

  NewSolution=solution [nums[i]] #将nums中的数字依次放入新组合NewSolution中。

  List=nums [I 1:] #剩余号码

  组合(新列表,新解决方案,n-1) #从剩余的数字中选择n-1个数字。

  res=[]

  组合([1,2,3,4,5],[],3)

  运行代码,结果如下:

  [[1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 3, 4], [1, 3, 5], [1, 4, 5], [2, 3, 4], [2, 3, 5], [2, 4, 5], [3, 4, 5]]

  组合问题代码和排列问题代码有两个区别。

  combination()方法有一个额外的参数N,用来表示我们需要选择几个数字。combination()方法每调用一次自身,n就减1。

  newList的定义不再是nums[0:i] nums[i 1:],而是nums[i 1:]。这是因为我们不能重复使用已经使用过的号码。让我们浏览一下代码,理解为什么要这样定义newList。

  我们第一次进入组合()方法。组合()的参数为nums=[1,2,3,4],solution=[],n=2。

  对于范围(n)中的I:# n=2,i=0

  new solution=solution[nums[I]]# new solution=[][1]=[1]

  newList=nums[i 1:]#newList=[2,3,4]

  combination(newList,newSolution,n-1) #combination([2,3,4],[1],1)

  目前选择了一个号码——1。接下来,在[2,3,4]中选择一个数字。

  第二次输入组合()方法。组合()的参数为:nums=[2,3,4],solution=[1],n=1。

  对于范围(n)中的I:# n=1,i=0

  新解决方案=解决方案[nums[I]]#新解决方案=[1] [2]

  List=nums [i 1:] # newlist=[3,4]而不是[1,3,4]

  combination(newList,newSolution,n-1) #combination([3,4],[1,2],0)

  目前的数字是2。当前选择的两个数字是1和2。新列表是[3,4]而不是[1,3,4]。如果是[1,3,4],逻辑上就说不通了,因为1已经在组合里了,所以不再可选。

  第三次输入组合()方法。combination()方法得到的参数是nums=[3,4],solution=[1,2],n=0。

  如果n==0: #n=0

  res.append(solution) #res=[[1,2]]

  返回

  满足边界条件并将当前组合添加到res列表。回到上次调用combination()方法的地方。

  combination()方法的当前参数为nums=[2,3,4],solution=[1],n=1。

  继续未完成的主循环。

  对于范围(n)中的I:# n=1,i=1

  新解决方案=解决方案[nums[i]] #新解决方案=[1] [3]

  new list=nums[I 1:]# new list=[4]=[4]而不是[1,2,4]

  combination(newList,newSolution,n-1) # combination([4],[1,3],0)

  目前的数字是3。当前选择的两个数字是1和3。NewList是[4]而不是[1,2,4]。因为组合中已经有4,所以不再可选。

  如你所见,newList只选择当前号码之后的号码,因为之前的号码都已经被使用过了,一个号码在组合问题中是不能重复使用的。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: