高效的java版排列组合算法

高效的Java版排列组合算法

前言

排列组合是数学中的一种常见问题,例如给定数列[1,2,3],对其进行排列组合可以得到以下六种可能:

  • 1 2 3
  • 1 3 2
  • 2 1 3
  • 2 3 1
  • 3 1 2
  • 3 2 1

在Java中,我们可以使用递归和循环等方式来实现排列组合,但是如果数列过长,将会十分耗时,因此我们需要一种高效的实现方式。

算法基础

排列

排列的基本概念是:一组数据中,任意取出几个(也可以是全部),按照一定的顺序排列起来,叫做这组数据的一个排列。

在Java中,我们可以通过递归函数来生成排列。

public static void permutation(int[] nums, int start, List<List<Integer>> res) {
    if (start >= nums.length) {
        List<Integer> list = new ArrayList<>();
        for (int n : nums) {
            list.add(n);
        }
        res.add(list);
    } else {
        for (int i = start; i < nums.length; i++) {
            swap(nums, i, start);
            permutation(nums, start + 1, res);
            swap(nums, i, start);
        }
    }
}

private static void swap(int[] nums, int i, int j) {
    int temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
}

上面的递归函数permutation接收一个整数数组nums,一个整数start和一个List> res,表示从nums[start]开始生成一个排列,并将结果保存在res中。我们先将nums[start]与nums[start+1]到nums[nums.length-1]依次交换,然后递归调用函数permutation,直到start >= nums.length。

组合

组合的基本概念是:一组数据中,任意取出几个(不能重复取出),不考虑顺序排列起来,叫做这组数据的一个组合。

在Java中,我们可以通过递归函数来生成组合。

public static void combination(int[] nums, int start, int k, List<List<Integer>> res) {
    if (k == 0) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < nums.length; i++) {
            if ((start & (1 << i)) != 0) {
                list.add(nums[i]);
            }
        }
        res.add(list);
    } else {
        for (int i = start; i < (1 << nums.length); i++) {
            if (Integer.bitCount(i) != k) {
                continue;
            }
            combination(nums, i + 1, k - 1, res);
        }
    }
}

上面的递归函数combination接收一个整数数组nums,一个整数start,一个整数k和一个List> res,表示从nums[start]开始选择k个数,生成一个组合,并将结果保存在res中。我们通过循环枚举[0,2^nums.length)范围内的数,如果这个数中1的个数不等于k,说明选中的数个数不足k个,因此直接跳过。否则,递归调用combination函数,将选中的数添加到结果中。

算法优化

虽然上面的排列组合算法已经可以完成任务,但是在处理长数列的时候,时间开销仍然很大。我们可以通过剪枝和非递归方式等方式优化算法,以提高算法效率。

排列算法优化

剪枝:在对nums数组进行交换的时候,可以首先检查当前元素是否与之前交换过,避免重复计算。

非递归方式:使用while循环代替递归实现,只需要保存下标,因此实现简单,效率高。

public static List<List<Integer>> permute(int[] nums) {
    List<List<Integer>> res = new ArrayList<>();
    int n = nums.length;
    int[] indexes = new int[n];
    for (int i = 0; i < n; i++) {
        indexes[i] = 0;
    }
    int i = 0;
    while (i < n) {
        if (indexes[i] < i) {
            swap(nums, i % 2 == 0 ? 0 : indexes[i], i);
            List<Integer> list = new ArrayList<>();
            for (int num : nums) {
                list.add(num);
            }
            res.add(list);
            indexes[i]++;
            i = 0;
        } else {
            indexes[i] = 0;
            i++;
        }
    }
    return res;
}

组合算法优化

剪枝:组合算法本身就包含了去重的操作,因此不需要再进行剪枝。

非递归方式:使用while循环代替递归实现,只需要保存下标,因此实现简单,效率高。

public static List<List<Integer>> combine(int n, int k) {
    List<List<Integer>> res = new ArrayList<>();
    if (n < k || n == 0) {
        return res;
    }
    int[] indexes = new int[k];
    for (int i = 0; i < k; i++) {
        indexes[i] = i;
    }
    while (indexes[0] < n - k + 1) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < k; i++) {
            list.add(indexes[i] + 1);
        }
        res.add(list);
        int t = k - 1;
        while (t >= 0 && indexes[t] == n - k + t) {
            t--;
        }
        if (t < 0) {
            break;
        }
        indexes[t]++;
        for (int i = t + 1; i < k; i++) {
            indexes[i] = indexes[i - 1] + 1;
        }
    }
    return res;
}

示例说明

下面是一个排列的例子,[1,2,3,4,5,6,7,8,9,10]这样一个长度为10的数列,我们需要从中生成其全部的排列组合。可以使用上面所介绍的算法进行计算。

int[] nums = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
List<List<Integer>> permutations = permute(nums);
List<List<Integer>> combinations = combine(10, 5);

通过使用上面的代码,我们可以得到nums的所有排列和长度为5的所有组合。

结语

排列组合算法是程序设计中常见的问题,通过使用Java中的递归和非递归方式,可以实现高效的排列组合算法。本篇文章介绍了基础的排列组合算法,以及如何通过剪枝和非递归方式等方式优化算法。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:高效的java版排列组合算法 - Python技术站

(0)
上一篇 2023年5月19日
下一篇 2023年5月19日

相关文章

  • Java冒泡排序的定义与实例代码

    Java冒泡排序是一种简单的排序算法,其基本思想是通过交换相邻元素的位置来达到排序的目的。在本篇攻略中,我将详细讲解Java冒泡排序的定义与实例代码。 定义 冒泡排序是一种交换排序。它的工作原理就像把一堆泡泡按大小排序一样。具体来说,它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。重复进行直到没有任何一个数需要交换位置为止。…

    Java 2023年5月19日
    00
  • Mac系统中Apache Tomcat安装配置

    下面是 “Mac系统中Apache Tomcat安装配置” 的完整攻略: 准备工作 在开始安装和配置Apache Tomcat之前,需要确保你的Mac系统上已经安装了Java环境。同时,你需要知道以下几个信息: Apache Tomcat的版本号(例如8.5.65) Apache Tomcat的安装路径(例如/usr/local/tomcat) 安装Apac…

    Java 2023年5月19日
    00
  • Java的Struts框架报错“BaseException”的原因与解决办法

    当使用Java的Struts框架时,可能会遇到“BaseException”错误。这个错误通常由以下原因之一起: 配置错误:如果配置文件中没有正确配置Action,则可能会出现此。在这种情况下,需要检查配置文件以解决此问题。 代码错误:如果编写的代码中存在错误,则可能会出现此。在这种情况下,需要检查代码以解决此问题。 以下是两个实例: 例 1 如果配置文件中…

    Java 2023年5月5日
    00
  • 利用iText在JSP中生成PDF报表

    生成PDF报表可以利用Java中的iText库来实现,iText使用方便,具有灵活性和可定制性,支持多语言,功能强大,可以创建、读取和操作PDF文档、表单和模板,生成安全性高的PDF文档。 以下是在JSP中使用iText生成PDF报表的完整攻略: 步骤1:下载iText库 在iText官网(https://itextpdf.com/)下载最新版的iText库…

    Java 2023年6月15日
    00
  • Java 实战练手项目之校园超市管理系统的实现流程

    校园超市管理系统是一个相对综合的Java实战练手项目,涉及到多个模块和技术。下面将详细阐述实现该系统的完整攻略。 1. 需求分析 在实现任何一个应用程序之前,我们需要首先进行需求分析,确定该系统需要满足哪些需求。在校园超市管理系统中,常见的需求包括: 商品管理:实现商品的添加、编辑、删除、查询等功能; 库存管理:对库存进行统计、报表展示等操作; 订单管理:实…

    Java 2023年5月31日
    00
  • Springboot导出文件,前端下载文件方式

    下面是Spring Boot导出文件、前端下载文件的攻略。 问题 有时候我们需要从Spring Boot应用中导出一些文件,如Excel、PDF或者其他格式的文件。我们如何通过前端将这些文件下载到本地? 导出文件 在Spring Boot中,我们可以借助一些开源组件实现文件的导出,常见的包括Apache POI、iText等。这里以Apache POI导出E…

    Java 2023年5月20日
    00
  • 从JVM的内存管理角度分析Java的GC垃圾回收机制

    从JVM的内存管理角度分析Java的GC垃圾回收机制的完整攻略如下: 1. 垃圾回收机制的概念 Java垃圾回收机制是JVM一项非常重要的特性,主要用于自动管理Java程序运行时的内存分配与回收。Java程序在执行过程中会不断地动态分配内存,而程序员要考虑如何处理分配的内存,在不再需要使用时及时释放内存。Java的垃圾回收机制极大地方便了程序员的编程,不用考…

    Java 2023年5月20日
    00
  • 一篇文章带你了解常用的Maven命令

    一篇文章带你了解常用的Maven命令 Maven是一个流行的Java项目管理工具,它可以帮助我们管理Java项目的依赖库、构建工具、测试工具等,让Java项目开发变得更加高效和便捷。在使用Maven时,我们需要学习一些常用的命令,以便能够熟练地使用Maven来管理Java项目。本篇文章将带你了解常用的Maven命令。 1. mvn clean mvn cle…

    Java 2023年5月19日
    00
合作推广
合作推广
分享本页
返回顶部