IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 算法 - 组合 -> 正文阅读

[数据结构与算法]算法 - 组合

目录

题目来源

题目描述

示例

提示

题目解析

算法源码

剪枝优化?


题目来源

77. 组合 - 力扣(LeetCode)

题目描述

给定两个整数?n?和?k,返回范围?[1, n]?中所有可能的?k?个数的组合。

你可以按?任何顺序?返回答案。

示例

输入:n = 4, k = 2
输出:
[
? [2,4],
? [3,4],
? [2,3],
? [1,2],
? [1,3],
? [1,4],
]

输入:n = 1, k = 1
输出:[[1]]

提示

  • 1 <= n <= 20
  • 1 <= k <= n

题目解析

这题就是求组合的问题。

组合和排列的区别在于,组合是无顺序性的,而排列是有顺序性的。

比如在[1,2,3]中任选两个数字的全排列有:

  • 12
  • 21
  • 13
  • 31
  • 23
  • 32

而全组合有:

  • 12
  • 13
  • 23

也就是说,对于组合来说12和21是同一个。

组合的求解也可以模拟为一个树结构

黄色部分是第一层选择完后,剩余可选数。

有人可能有疑问,为什么第一层选完2后,剩余可选数不是13,而是只有3呢?

我们假设第一层选完2后,剩余可选数是13,则

?最终会产生12和21这种重复组合。

因此组合的树结构,单层节点选择时,如果采用for顺序遍历的话,则后面层级节点选择起始位置 要比 前面层级节点选择起始位置 + 1。

比如第一层选择了1,则第二层从2开始选

比如第一层选择了2,则第二层从3开始选

只有这样才能避免重复。

组合由于和排列问题一样也能用树结构模拟,因此也可以用回溯算法,基于递归模型进行实现。

假设,从n个数中选择k个数,每层得到节点保存进path中,则当path.length === k 时,说明已经选择了path中已经保存了k个数,符合结束要求,结束递归。

每层节点选择没有特殊条件的话,即只要保证后面层级节点选择的起始位置,是上一层选择的节点的位置i的后一个,即i+1,这样才能保证无重复的组合。

算法源码

/**
 * @param {number} n
 * @param {number} k
 * @return {number[][]}
 */
var combine = function(n, k) {
    let result = []
    dfs(n, k, 1, [], result)
    return result
};

function dfs(n, k, index, path, result) {
    if(path.length === k) {
        return result.push([...path])
    }

    for(let i=index; i<=n; i++) {
        path.push(i)
        dfs(n, k, i+1, path, result)
        path.pop()
    }
}

剪枝优化?

题目中提示

1 <= k <= n

也就是说存在k===n的情况,比如我从4个数里面取4个,此时很显然只有一种组合,就是1234。但是按照上面算法源码逻辑,我们会出现如下图所示的递归过程

?当第一层取2时,第二层可选数字就只有3了,因此这个分叉的结果为23,由于不满足k=4,因此最终会丢弃。

剪枝优化,指的是,我们可以提前判断某分支是否符合最终要求,若不符合要求,则再其走递归流程前就结束它。

?比如当第一层选取2后,理论上还需要选择k-1=2个元素,但是实际上,第一层选完2后,只能选3了,即只剩余1个元素可选,因此我们不需要走完递归流程,也知道此分支的结果是不符合要求的,因此可以提前结束。

/**
 * @param {number} n
 * @param {number} k
 * @return {number[][]}
 */
var combine = function(n, k) {
    let result = []
    dfs(n, k, 1, [], result)
    return result
};

function dfs(n, k, index, path, result) {
    if(path.length === k) {
        return result.push([...path])
    }

    for(let i=index; i<=n; i++) {
        if(n - i + 1 < k - path.length) break // 剪枝
        path.push(i)
        dfs(n, k, i+1, path, result)
        path.pop()
    }
}

但是上面代码中的剪枝策略,依旧存在无意义的for循环遍历,循环的结束条件都放到了循环体中,因此可以继续优化

/**
 * @param {number} n
 * @param {number} k
 * @return {number[][]}
 */
var combine = function(n, k) {
    let result = []
    dfs(n, k, 1, [], result)
    return result
};

function dfs(n, k, index, path, result) {
    if(path.length === k) {
        return result.push([...path])
    }

    for(let i=index; i<=n-(k-path.length)+1; i++) { // 将剪枝条件整合到循环条件中,减少循环次数
        path.push(i)
        dfs(n, k, i+1, path, result)
        path.pop()
    }
}

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-10-22 21:39:02  更:2022-10-22 21:42:47 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 20:38:52-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码