全排列算法的全面解析

全排列算法的全面解析对数组进行全排列是一个比较常见问题 如果是一个比较喜欢考算法的公司 貌似一些大公司都比较喜欢考算法 那么估计就会考察应聘者这个全排列的问题了 就算不让你编写完整代码 也会让你描述大致的思路 这个问题也难也难 说易也易 下面我就来对这个问题进行一个比较全面的解析吧 如有遗漏 还望指正

概述

对数组进行全排列是一个比较常见问题,如果是一个比较喜欢考算法的公司(貌似一些大公司都比较喜欢考算法),那么估计就会考察应聘者这个全排列的问题了(就算不让你编写完整代码,也会让你描述大致的思路)。这个问题也难也难,说易也易,下面我就来对这个问题进行一个比较全面的解析吧。如有遗漏,还望指正。


版权说明

著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。
本文作者:Q-WHai
发表日期: 2016年3月27日
本文链接:https://qwhai.blog.csdn.net/article/details/
来源:CSDN
更多内容:分类 >> 算法与数学















描述

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

如果要按从小到大输出呢?算法又要怎么写?


基于递归的实现

思路分析

所有元素均无相同的情况

基于上面的分析,我们知道这个可以采用递归式实现,实现代码如下:

private static void core(int[] array) { 
       int length = array.length; fullArray(array, 0, length - 1); } private static void fullArray(int[] array, int cursor, int end) { 
       if (cursor == end) { 
       System.out.println(Arrays.toString(array)); } else { 
       for (int i = cursor; i <= end; i++) { 
       ArrayUtils.swap(array, cursor, i); fullArray(array, cursor + 1, end); } } } 

运行结果

[1, 2, 3] [1, 3, 2] [3, 1, 2] [3, 2, 1] [1, 2, 3] [1, 3, 2] 
private static void fullArray(int[] array, int cursor, int end) { 
       if (cursor == end) { 
       System.out.println(Arrays.toString(array)); } else { 
       for (int i = cursor; i <= end; i++) { 
       ArrayUtils.swap(array, cursor, i); fullArray(array, cursor + 1, end); ArrayUtils.swap(array, cursor, i); // 用于对之前交换过的数据进行还原 } } } 

修改后的运行结果

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

存在相同元素的情况

上面的程序乍一看没有任何问题了。可是,如果我们对序列进行一下修改 array = {1, 2, 2}.我们看看运行的结果会怎么样。

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

这里出现了好多的重复。重复的原因当然是因为我们列举了所有位置上的可能性,而没有太多地关注其真实的数值。
现在,我们这样来思考一下,如果有一个序列T = {a1, a2, a3, …, ai, … , aj, … , an}。其中,a[i] = a[j]。那么是不是就可以说,在a[i]上,只要进行一次交换就可以了,a[j]可以直接忽略不计了。好了,基于这样一个思路,我们对程序进行一些改进。我们每一次交换递归之前对元素进行检查,如果这个元素在后面还存在数值相同的元素,那么我们就可以跳过进行下一次循环递归(当然你也可以反着来检查某个元素之前是不是相同的元素)。
基于这个思路,不难写出改进的代码。如下:




private static void core(int[] array) { 
       int length = array.length; fullArray(array, 0, length - 1); } private static boolean swapAccepted(int[] array, int start, int end) { 
       for (int i = start; i < end; i++) { 
       if (array[i] == array[end]) { 
       return false; } } return true; } private static void fullArray(int[] array, int cursor, int end) { 
       if (cursor == end) { 
       System.out.println(Arrays.toString(array)); } else { 
       for (int i = cursor; i <= end; i++) { 
       if (!swapAccepted(array, cursor, i)) { 
       continue; } ArrayUtils.swap(array, cursor, i); fullArray(array, cursor + 1, end); ArrayUtils.swap(array, cursor, i); // 用于对之前交换过的数据进行还原 } } } 

基于非递归的实现

思路分析

由于非递归的方法是基于对元素大小关系进行比较而实现的,所以这里暂时不考虑存在相同数据的情况。
在没有相同元素的情况下,任何不同顺序的序列都不可能相同。不同的序列就一定会有大有小。也就是说,我们只要对序列按照一定的大小关系,找到某一个序列的下一个序列。那从最小的一个序列找起,直到找到最大的序列为止,那么就算找到了所有的元素了。
好了,现在整体思路是清晰了。可是,要怎么找到这里说的下一个序列呢?这个下一个序列有什么性质呢?
T[i]下一个序列T[i+1]是在所有序列中比T[i]大,且相邻的序列。关于怎么找到这个元素,我们还是从一个例子来入手吧。
现在假设序列T[i] = {6, 4, 2, 8, 3, 1},那么我们可以通过如下两步找到它的下一个序列。
这里写图片描述
看完上面的两个步骤,不知道大家有没有理解。如果不理解,那么不理解的点可能就在于替换点和被替点的寻找,以及之后为什么又要进行反转上。我们一个一个地解决问题吧。












  • 替换点和被替换点的寻找。替换点是从整个序列最后一个位置开始,找到一个连续的上升的两个元素。前一个元素的index就是替换点。再从替换点开始,向后搜寻找到一个只比替换点元素大的被替换点。(如果这里你不是很理解,可以结合图形多思考思考。)
  • 替换点后面子序列的反转。在上一步中,可以看到替换之后的子序列({8, 2, 1})是一个递减的序列,而替换点又从小元素换成 了大元素,那么与之前序列紧相邻的序列必定是{8, 2, 1}的反序列,即{1, 2, 8}。
    这样,思路已经完全梳理完了,现在就是对其的实现了。只是为了防止给定的序列不是最小的,那就需要对其进行按从小到大进行排序。

逻辑实现

public class DemoFullArray2 { 
        public static void main(String[] args) { 
        int[] array = { 
       2, 3, 1, 4}; core(array); } private static void core(int[] array) { 
        // 先排序 SortUtils sortUtils = new SortUtils(new QKSort()); sortUtils.sort(array); System.out.println(Arrays.toString(array)); // 最初始的序列 do { 
        nextArray(array); System.out.println(Arrays.toString(array)); } while (!isLast(array)); } private static int[] nextArray(int[] array) { 
        int length = array.length; // 寻找替换点 int cursor = 0; for (int i = length - 1; i >= 1; i--) { 
        // 找到第一个递增的元素对 if (array[i - 1] < array[i]) { 
        cursor = i - 1; // 找到替换点 break; } } // 寻找在替换点后面的次小元素 int biggerCursor = cursor + 1; for (int i = cursor + 1; i < length; i++) { 
        if (array[cursor] < array[i] && array[i] < array[biggerCursor]) { 
        biggerCursor = i; } } // 交换 ArrayUtils.swap(array, cursor, biggerCursor); // 对替换点之后的序列进行反转 reverse(array, cursor); return array; } private static void reverse(int[] array, int cursor) { 
        int end = array.length - 1; for (int i = cursor + 1; i <= end; i++, end--) { 
        ArrayUtils.swap(array, i, end); } } private static boolean isLast(int[] array) { 
        int length = array.length; for (int i = 1; i < length; i++) { 
        if (array[i - 1] < array[i]) { 
        return false; } } return true; } } 

Ref

  • http://blog.csdn.net/morewindows/article/details/

征集

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/198164.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月26日 下午3:38
下一篇 2026年3月26日 下午3:39


相关推荐

  • net.sf.json.JSONObject maven依赖

    net.sf.json.JSONObject maven依赖最后一行需要保留 有两个 jdk 版本的实现 json lib 2 1 jdk13 jar 和 json lib 2 1 jdk15 jar lt dependency gt lt groupId gt net sf json lib lt groupId gt lt artifactId gt json lib lt art

    2026年3月18日
    2
  • DDl,DML语句

    DDl,DML语句1 数据库 1 概述数据库 DataBase DB 指长期保存在计算机的存储设备上 按照一定规则组织起来 可以被各种用户或应用共享的数据集合 数据库管理系统 DataBaseMana DBMS 指一种操作和管理数据库的大型软件 用于建立 使用和维护数据库 对数据库进行统一管理和控制 以保证数据库的安全性和完整性 用户通过数据库管理系统访问数据库中的数据 数

    2026年3月26日
    2
  • 使用Cursor来辅助编写Junit5测试类

    使用Cursor来辅助编写Junit5测试类

    2026年3月15日
    2
  • [特殊字符]OpenClaw爆火背后的安全冷思 MEMORY.md与SKILL.md:安全架构与最佳实践

    [特殊字符]OpenClaw爆火背后的安全冷思 MEMORY.md与SKILL.md:安全架构与最佳实践

    2026年3月12日
    3
  • ssm框架过时了吗_spring实战

    ssm框架过时了吗_spring实战SpringSpring是一个开源的免费的框架Spring是一个轻量级的,非入侵式的框架控制反转(IOC),面向切面编程(AOP)支持事务的处理,对框架整合的支持IOC理论UserDaoUserDaoImpUserSeviceUserServiceImp在之前,用户的需求可能会影响原来的代码。使用一个set。public void setUserDao(UserDao userDao){ this.userDao = userDao;}之前是主动创建对象,控制

    2022年8月8日
    11
  • 1677个高频单词_3500高频词汇表

    1677个高频单词_3500高频词汇表给一非空的单词列表,返回前 k 个出现次数最多的单词。返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率,按字母顺序排序。示例 1:输入: [“i”, “love”, “leetcode”, “i”, “love”, “coding”], k = 2输出: [“i”, “love”]解析: “i” 和 “love” 为出现次数最多的两个单词,均为2次。 注意,按字母顺序 “i” 在 “love” 之前。 示例 2:输入: [“the”, “day”, “is

    2022年8月11日
    5

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号