排列组合是算法竞赛、笔试面试的高频考点 ,核心区别:排列重顺序,组合不重顺序 。Python 自带标准库可快速解题,搭配回溯算法能覆盖所有变形场景。本文结合洛谷B2164组合数、洛谷P1706全排列、LeetCode77组合3道经典题,一站式搞定排列组合的内置函数用法与手写实现。
一、组合数计算:只求数量,不枚举(洛谷B2164)
题目核心
给定n、k,求C(n,k) mod 1e9+7(从n个元素选k个的方案数,顺序无关)。
数学公式
C(n,k)=n!/(k!*(n−k)!)
Python 极简解法(math.comb)
Python 3.10+ 提供math.comb直接计算组合数,搭配取模即可通过本题。
python
from math import comb
MOD = 10**9 + 7
n, m = map(int, input().split())
# 直接计算组合数并取模
ans = comb(n, m) % MOD
print(ans)
样例输入 :5 3 → 输出:10
手写组合数(兼容低版本)
python
def calc_comb(n, k):
if k < 0 or k > n:
return 0
if k == 0 or k == n:
return 1
k = min(k, n - k) # 减小计算量
res = 1
for i in range(1, k + 1):
res = res * (n - k + i) // i
return res
MOD = 10**9 + 7
n, m = map(int, input().split())
print(calc_comb(n, m) % MOD)
二、全排列生成:有序枚举所有序列(洛谷P1706)
题目核心
按字典序输出1~n的全排列,每个数字占5个字符宽度。
核心概念
排列 :元素顺序不同算不同结果,用itertools.permutations生成。
Python 标准库解法
python
from itertools import permutations
n = int(input())
# 生成1~n的列表
nums = list(range(1, n + 1))
# 生成全排列
perms = permutations(nums)
# 按格式拼接:每个数字占5位
result = [''.join(f'{num:5d}' for num in p) for p in perms]
print('\n'.join(result))
样例输入:3 → 输出按要求对齐的6行全排列。
回溯手写全排列(竞赛必备模板)
python
n = int(input())
res = []
used = [False] * (n + 1) # 标记数字是否被使用
def backtrack(path):
if len(path) == n:
res.append(''.join(f'{x:5d}' for x in path))
return
for i in range(1, n + 1):
if not used[i]:
used[i] = True
path.append(i)
backtrack(path)
path.pop()
used[i] = False
backtrack([])
print('\n'.join(res))
三、组合枚举:生成所有k元子集(LeetCode77)
题目核心
给定n、k,返回1,n中所有k个数的组合(无序,不重复)。
标准库解法(itertools.combinations)
python
from typing import List
from itertools import combinations
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
nums = list(range(1, n + 1))
# 生成所有k元组合
return [list(c) for c in combinations(nums, k)]
样例输入:n=4,k=2 → 输出6个组合。
回溯+剪枝(高效手写版)
组合无需顺序,用start避免重复,剪枝可大幅提速。
python
from typing import List
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
res = []
def backtrack(start, path):
# 剪枝:剩余元素不够凑k个,直接返回
if len(path) + (n - start + 1) < k:
return
if len(path) == k:
res.append(path.copy())
return
for i in range(start, n + 1):
path.append(i)
backtrack(i + 1, path) # 从下一个数开始,避免重复
path.pop()
backtrack(1, [])
return res
四、核心对比:排列 vs 组合
| 类型 | 顺序 | 函数 | 典型场景 |
|---|---|---|---|
| 排列 | 有 | permutations | 排队、排序、序列 |
| 组合 | 无 | combinations/comb | 选人、选子集、方案数 |
快速选择指南
- 只求方案数 → 用
math.comb(组合数) - 生成所有有序序列 → 用
permutations或全排列回溯 - 生成所有无序子集 → 用
combinations或组合回溯 - 算法竞赛/面试 → 必须掌握回溯模板(内置函数可能受限)
总结
- 组合数:
math.comb一步到位,搭配取模适配大数场景; - 全排列:
permutations快速生成,回溯可处理自定义约束; - 组合枚举:
combinations极简,回溯+剪枝更高效。
吃透这3道题与两套写法,Python 排列组合类题目直接秒杀。