Python入门习题(74)——OpenJudge百练习题:全排列

OpenJudge百练第4070号习题:全排列

  • 题目描述
  • 解题思路
  • 参考答案
  • 测试用例
  • 小结

题目描述

来源
OpenJudge网站 – 百练习题集-第4070号习题

要求
总时间限制: 1000ms 内存限制: 65536kB

对于数组[1, 2, 3],他们按照从小到大的全排列是

1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

现在给你一个正整数n,n小于8,输出数组[1, 2, …,n]的从小到大的全排列。

输入
输入有多行,每行一个整数。当输入0时结束输入。
输出
对于每组输入,输出该组的全排列。每一行是一种可能的排列,共n个整数,每个整数用一个空格隔开,每行末尾没有空格。
样例输入
2
3
0
样例输出
1 2
2 1
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

解题思路

  1. 问题可以抽象为求出数字列表num_list中各个数字的全排列。结果是各个排列组成的列表。

  2. 用函数quanpailie(num_list)来实现求出数字列表num_list的全排列的算法。这一算法的思路是:
    (2-1) 对于num_list的第 i 个数字,求出以第 i 个数字打头的所有排列。
      (2-1-1)从num_list中取走第 i 个数字,得到num_list_pop_i列表。
      (2-1-2)调用函数quanpailie(num_list_pop_i) 得出num_list_pop_i列表的全排列。
      (2-1-3)um_list_pop_i列表的全排列中的每一个排列之前冠以第 i 个数字,即得到以第 i 个数字打头的排列。
    (2-2)i 依次取1, 2, …, n,这里n是num_list的长度,重复执行(2-1), 把得到的以第 i 个数字打头的所有排列加入到pailie_list中。重复执行结束的时候,pailie_list存储的就是num_list列表的全排列。

  3. 第2点中,quanpailie函数会调用自己,具体来讲就是quanpailie(num_list)执行期间会调用quanpailie(num_list_pop_i)。相比于quanpailie(num_list),quanpailie(num_list_pop_i)的列表少了一个元素——问题规模变小了。

  4. quanpailie函数会调用自己,因此这个函数是递归函数。什么时候递归调用会结束?答案是,当列表的长度为1的时候。

参考答案

#求出num_list中各个数字的全排列,结果是各个排列组成的列表。
def quanpailie(num_list):
    if len(num_list) == 0:
        return []
    if len(num_list) == 1:  
        return [num_list]  #只有1个数字的情形。注意:结果是列表的列表

    pailie_list = []
    for i in range(len(num_list)):  #对于num_list中的每个数字
    	#得到以第 i 个数字打头的排列
        num_list_pop_i = num_list[:]  #[:]不能省略。作用是复制一份列表。
        num = num_list_pop_i.pop(i)  #从num_list中取出第i个数字
        #得到第i个数字打头的所有排列 = 打头数字 + num_list_pop_i的全排列
        for qpl in quanpailie(num_list_pop_i):  #对于num_list_pop_i的全排列中的每一个排列
            qpl.insert(0, num)  #该排列的头部加上打头数字
            pailie_list.append(qpl)  #得到num_list的一个排列
    return pailie_list

while True:
    n = int(input())

    if n == 0:
        break

    for qpl in quanpailie(list(range(1, n + 1))):
        print(' '.join([str(num) for num in qpl]))

测试用例

  1. 题目描述给出的测试用例覆盖了一般情形。有两组测试数据。

  2. 一组测试数据的情形。
    样例输入
    4
    0
    样例输出
    1 2 3 4
    1 2 4 3
    1 3 2 4
    1 3 4 2
    1 4 2 3
    1 4 3 2
    2 1 3 4
    2 1 4 3
    2 3 1 4
    2 3 4 1
    2 4 1 3
    2 4 3 1
    3 1 2 4
    3 1 4 2
    3 2 1 4
    3 2 4 1
    3 4 1 2
    3 4 2 1
    4 1 2 3
    4 1 3 2
    4 2 1 3
    4 2 3 1
    4 3 1 2
    4 3 2 1

  3. n=1的边界情形。
    样例输入
    1
    0
    样例输出
    1

  4. n=8的边界情形。
    样例输入
    8
    0
    样例输出
    1 2 3 4 5 6 7 8

    8 7 6 5 4 3 2 1
    输出太长了,这里省略。因为输出数据有规律,所以可以看一看规律是否满足要求。此外,这一测试用例可以验证程序的时间开销是否可以满足要求。

小结

  1. 解答本题使用了递归函数。构造算法的过程中发现了递归规律,然后采用递归函数。
  2. 数字列表num_list的全排列 = 第1个数字打头的所有排列 + 第2个数字打头的所有排列 + … + 第n个数字打头的所有排列。第 i 个数字打头的排列= 第 i 个数字 (打头 )+ 其余数字的全排列。这里存在递归规律。
  3. 对于本题,问题可以抽象为求出数字列表num_list中各个数字的全排列。问题不应该抽象成求出数字1, 2, …, n的全排列。这只是起点,在算法执行过程中,会求数字2, 3, 5, 7或1, 5, 6等数字列表的全排列。
  4. 递归函数必须有终止条件。

你可能感兴趣的:(Python入门100道习题,Python编程)