全排列算法【非递归活动数实现】

来源:互联网 发布:淘宝上代购药品 编辑:程序博客网 时间:2024/05/29 03:50

求解一个问题,有很多种算法/方法,一旦遇到比较有趣的思想/算法,就忍不住记录下来。

题:求n=4时的全排列(当n=4时,序列为:{1, 2, 3, 4})

算法的思想:
1. 给排列中的每个元素均赋予一个向左或向右的箭头。
2. 如果元素k的箭头指向的是与其相邻但小于k的元素,则称元素k是活动的。
3. 从排列 1 2 3 … n 开始,找其中的最大活动元素k,将该元素k与它所指向的相邻元素交换位置,并改变所有大于k的元素的方向。

import java.util.ArrayList;import java.util.List;import java.util.Scanner;/** * 生成全排列 * * 算法的思想: * 1. 给排列中的每个元素均赋予一个向左或向右的箭头。 * 2. 如果元素k的箭头指向的是与其相邻但小于k的元素,则称元素k是活动的。 * 3. 从排列 1  2  3  … n 开始,找其中的最大活动元素k,将该元素k与它 * 所指向的相邻元素交换位置,并改变所有大于k的元素的方向。 */public class Perm {    enum Direction {        LEFT, RIGHT     //方向有左右    }    /**     * 把每个数看成一个元素,有数值有方向     */    static class Element {        int data;               //数值        Direction direction;    //方向        public Element(int data, Direction direction) {            this.data = data;            this.direction = direction;        }    }    /**     * 生成全排列     * @param list 需要生成全排列的序列集合     */    private static void perm(List<Element> list) {        int count = 1;      //统计全排列的数目        if (list == null)            return;        //首先打印第一种情况        printAllElement(list, true);        int index;      //活动数的下标        while (true) {            index = findMaxActiveNum(list);     //找到最大活动数下标            if(index == -1) {                System.out.println("全排列总数为:" + count);                return;            }            //改变所有大于最大活动数的元素的方向            changeDirection(list, index);            //交换最大活动数与它所指向的相邻元素            if (list.get(index).direction == Direction.LEFT) {                swapElement(list, index-1, index);            } else {                swapElement(list, index, index+1);            }            count++;            printAllElement(list, true);        }    }    /**     * 找到最大活动数     * @param list 需要生成全排列的序列集合     */    private static int findMaxActiveNum(List<Element> list) {        if(list == null)            return -1;        int length = list.size();        int index = -1;        //找出最大活动数的下标        for (int i = 0; i < length; i++) {            int data = list.get(i).data;            boolean isLeft = list.get(i).direction == Direction.LEFT;            //当不是活动数时,跳出此次循环-            if (i == 0 && isLeft || i == length-1 && !isLeft ||     //这个数的箭头所指的下一个元素为空                    isLeft && data < list.get(i-1).data ||                     !isLeft && data < list.get(i+1).data) {         //这个数比箭头所指的下一个数小                continue;            } else {                if(index == -1) {                    index = i;                } else {                    index = list.get(i).data > list.get(index).data ? i : index;    //记录最大活动数的下标                }            }        }        return index;    }    /**     * 交换两个元素的值和箭头     * @param list 需要生成全排列的序列集合     * @param index1 下标1     * @param index2 下标2     */    private static void swapElement(List<Element> list, int index1, int index2) {        if(list == null)            return;        //交换两个对象的引用,达到交换值的目的        Element temp = list.get(index1);        list.set(index1, list.get(index2));        list.set(index2, temp);    }    /**     * 改变所有大于list.get(index)的元素的方向     * @param list 需要生成全排列的序列集合     * @param index 下标     */    private static void changeDirection(List<Element> list, int index) {        if (list == null)            return;        int data = list.get(index).data;        for (int i = 0; i < list.size(); i++) {            if (list.get(i).data > data) {                list.get(i).direction = list.get(i).direction == Direction.LEFT ?                        Direction.RIGHT :                        Direction.LEFT;            }        }    }    /**     * 打印全部元素     * @param list 需要生成全排列的序列集合     * @param arrowFlag 是否打印箭头     */    private static void printAllElement(List<Element> list, boolean arrowFlag) {        if (list == null)            return;        if(arrowFlag) {            for (Element element: list) {                switch (element.direction) {                    case LEFT:                        System.out.print("← ");                        break;                    case RIGHT:                        System.out.print("→ ");                        break;                }            }        }        System.out.println("");        for (Element element: list) {            System.out.print(element.data + " ");        }        System.out.println("");    }    /**     * 创建一个序列     * @param n 序列最大的数     * @return n为最大值的集合     */    private static List<Element> createDataList(int n) {        List<Element> list = new ArrayList<>();        for (int i = 1; i <= n; i++) {            list.add(new Element(i, Direction.LEFT));   //初始化一个最大元素为n的序列,并且每个元素拥有一个向左的箭头        }        return list;    }    public static void main(String[] args) {        List<Element> elementList;        Scanner scanner = new Scanner(System.in);        int n;      //读取n        System.out.println("【功能】非递归求全排列(活动数实现)");        System.out.println("请输入n:");        n = scanner.nextInt();        elementList = createDataList(n);        perm1(elementList, 0, elementList.size());    }}

运行结果:
这里写图片描述
这里写图片描述

原创粉丝点击