Johnson-Trotter(JT)算法生成排列

对于生成{1,……,n}的所有n!个排列的问题,我们可以利用减治法,该问题的规模减一就是要生成所有(n-1)!个排列。假设这个小问题已经解决了,我们可以把n插入到n-1个元素的每一种排列中的n可能的位置中去,来得到较大规模大问题的一个解。按照这种方式生成的所有排列都是独一无二的,并且他们的总数应该是n(n-1)!=n!。这样,我们都得到了{1,……,n}的所有排列。

JohnsonTrotter算法实现形式。

JohnsonTrotter(n)

输入:一个正整数n

输出:{1,……,n}的素有排列的列表

将第一个排列初始化为方向向左的元素数组

while 存在一个移动元素k do

求最大的移动元素k

把k和它箭头指向的相邻元素互换

调转所有大于k的元素的方向

将新排列添加到列表

(摘自算法设计与分析基础)

下午自己实现了一下这个算法,将其改成可以把N个不重复的元素排列出来,程序中使用到的比较器提供接口需要自己去实现,程序运行需要把使用者自己实现的比较器注入程序。自我感觉程序灵活性还可以。

/**
 * 使用JT算法进行排列组合。
 * 注意:请务必保持范型和比较接口范型一致,否则可能产生不可预知的错误
 * @author LiuYeFeng<[email protected]>
 * @date 2015年4月9日 下午5:31:00
 * @CopyRight 2015 TopView Inc
 * @version V1.0
 * @param <E> 需要排列的元素的范型,请务必保持范型和比较接口范型一致,否则可能产生不可预知的错误
 */
public class JTAlgorithm<E>{

    //存放排列元素的数组
    protected E[] array;
    //元素的方向数组
    private Direction[] directions;
    //比较器,用于比较元素大小
    private Compare<E> compare;

    public JTAlgorithm(Class<? extends Compare<E>> clazz) {
        //获取比较方法的实例
        this.compare = (Compare) ReflectUtils.newInstance(clazz);
    }

    public JTAlgorithm(Compare<E> compare) {
        //获取比较方法的实例
        this.compare = compare;
    }
    public List<E[]> generate(E[] elements) {
        List<E[]> result = new ArrayList<E[]>();

        //初始化工作
        init(elements);

        //最大可移动元素的位置
        int biggestFlag = findBiggestMobileElement();
        //自身也为一种排列
        result.add(Arrays.copyOf(array, array.length));

        //存在可移动最大元素k
        while (biggestFlag != -1) {
            //将k和箭头指向的相邻元素互换
            biggestFlag = changeBiggestElementAndNeighbor(biggestFlag);
            //调转所有大于k的元素的方向
            changeDirection(biggestFlag);
            //将新排列添加到结果集
            result.add(Arrays.copyOf(array, array.length));
            //重新查找可移动最大元素
            biggestFlag = findBiggestMobileElement();
        }

        return result;
    }

    private void init(E[] elements) {
        //用快排把元素排序
        QuickSort<E> qk = new QuickSort<E>(compare);
        qk.sort(elements, 0, elements.length - 1);

        array = elements;
        directions = new Direction[array.length];

        //初始化方向
        for (int i = 0; i < directions.length; i++) {
            directions[i] = Direction.LEFT;
        }
    }

    /**
     * 把比loc位置大的元素的方向反转
     * @param loc
     */
    private void changeDirection(int loc) {
        for (int i = 0; i < array.length; i++) {
            if (compare.greaterThan(array[i], array[loc])) {
                directions[i] = (directions[i] == Direction.LEFT) ? Direction.RIGHT : Direction.LEFT;
            }
        }
    }

    /**
     * 把loc元素和它的邻居互换,并把互换后loc的新位置返回
     * @param loc
     * @return loc的新位置
     */
    private int changeBiggestElementAndNeighbor(int loc) {
        int neighbor = -1;

        if (directions[loc] == Direction.LEFT) {
            neighbor = loc - 1;
        } else {
            neighbor = loc + 1;
        }

        E temp = array[loc];
        array[loc] = array[neighbor];
        array[neighbor] = temp;

        Direction dTemp = directions[loc];
        directions[loc] = directions[neighbor];
        directions[neighbor] = dTemp;

        return neighbor;
    }

    /**
     * 查找最大可移动元素
     * @return 最大可移动元素的位置
     */
    private int findBiggestMobileElement() {
        int loc = -1;
        int biggestLoc = -1;

        for (int i = 0; i < array.length; i++) {
            //判断左右方向
            if (directions[i] == Direction.LEFT) {
                //如果是头元素,则无法向左比较,跳过
                if (i == 0) {
                    continue;
                }

                if (compare.greaterThan(array[i], array[i - 1])) {
                    loc = i;
                }
            } else {
                //如果是尾元素,则无法向右比较,跳过
                if (i == array.length - 1) {
                    continue;
                }

                if (compare.greaterThan(array[i], array[i + 1])) {
                    loc = i;
                }
            }

            //如果第一次找到可移动元素,则把最大可移动元素改变,之后把本次找到的可移动元素和最大可移动元素进行比较
            if (loc != -1 && biggestLoc == -1) {
                biggestLoc = loc;
            }else if (biggestLoc != -1 && compare.greaterThan(array[loc], array[biggestLoc])) {
                biggestLoc = loc;
            }
        }

        return biggestLoc;
    }
}
时间: 2024-07-28 16:22:28

Johnson-Trotter(JT)算法生成排列的相关文章

算法笔记03--归纳法之生成排列

生成排列 生成排列即对n个数的全排列,显然时间复杂度是n指数级的O(n^k) 假定可以生成n-1个数的所有排列,那么就可以扩展生成1,2,.....,n的排列. 例如1的生成排列即1 1,2的生成排列即1,2和2,1 1,2,3的生成排列在1,2的生成排列基础上可以这样得到: 1在第1位,2,3的生成排列 2在第1位,1,3的生成排列 3在第1位,2,3的生成排列 那么推广到1,2,...,n的生成排列即: 1在第1位,2,...,n的生成排列 2在第2位,1,3,...n的生成排列 ....

减一技术应用:生成排列与幂集(Java实现)

减一技术,与二分搜索一样,是一种通用算法设计技术.它是分治法的一种特殊形式,通过建立问题实例P(n) 与问题实例P(n-1)的递推求解关系式而实现:最经典的例子莫过于插入排序了.这里,给出减一技术在生成排列组合方面的应用. (一)  排列问题: 生成自然数 1,2,,,,,n 的所有排列. 算法描述: 使用减一技术,建立自然数12...n的排列与12...n-1的递推关系.假设 P(n-1) 是 自然数 12...n-1的所有排列 p1, p2,..., p(m)的集合,则P(n)通过如下方式得

算法生成五星红旗

这一篇将提供一套生成五星红旗的算法,代码中满满都是正能量.上一篇文章是生成N芒星,五角星是就芒星的一种,所以不难生成一个五角星的图像.中华人民共和国国旗是五星红旗,旗面为红色,长宽比例为3:2.左上方缀黄色五角星五颗,四颗小星环拱在一颗大星的右面,并各有一个角尖正对大星的中心点.1.4颗黄星对齐,2.3颗黄星对齐. 代码如下: h 1 class CPixel5StarsRedFlag : public IPixelEquation 2 { 3 public: 4 CPixel5StarsRed

恶魔的指纹---49幅由算法生成的邪恶七芒星图像

本文将带你领略算法之美,美得让你感到恐惧.前几天写了生成七芒星的算法,之后我在此算法的基础上做了些修改,从此一发不可收拾.我想我打开了潘多拉的盒子,只要修改下算法的任意一个输入参数,就能够生成一幅独一无二的七芒星图像.这些图像如同来自地狱一样,让我感到惊悚恐惧.有人曾将曼德勃罗集生成的分形图像称为上帝的指纹,那么这些饱含邪恶气息的七芒星图像应该算是恶魔的指纹. 有一次我对公司的一个策划开玩笑说:"你再给我提需求,我就写程序画圈圈诅咒你",并且我去年还真写了这么个程序,见:画圈圈诅咒你.

雪花六出---几幅算法生成的雪花图像,并祝大家平安夜和圣诞节快乐

今天和明天是平安夜和圣诞节,发几幅雪花的图像应下节日.本来是打算写个关于算法生成雪花的学术论文,但发现这东西很难写下去.主要是找不到一个提高论文逼格的数学公式,所以就发下博客吧.共有10幅PNG图像和3幅GIF动画图像,都是黑白二值化的图像. 雪花是种典型的混沌分形物体,在它身上能体现规则与随机的统一.说它规则是因为每一片雪花都是六边形的,至少有六边形的样子.而组成雪花的每一个分子都是随机混沌的.没有办法确定某一个水分子在生成雪花后位于雪花的哪个位置. 雪花是水分子的结晶,水分子又是由一个氧原子

算法生成青天白日满地红旗

“青天白日满地红”曾经代表中国,特别是在抗日战争的艰苦年代.那时候红军编入国民革命军,戴着青天白日帽徽,全国人民团结在“青天白日满地红”旗帜下,高唱<义勇军进行曲>,奋力抗战,打败了日本帝国主义.“青天白日满地红”象征着中国人的苦难和抗争,跟<义勇军进行曲>一起,鼓舞中国人团结起来取得胜利,凝结着中国人的感情. 青色代表光明纯洁.民族和自由:白色代表坦白无私.民权和平等:白日的十二道光芒,代表著一年十二个月,一天十二个时辰:也象征著国家的命脉,随著时间的前进永存于世界:更鼓舞国人与

HDU 4771 Stealing Harry Potter&#39;s Precious (生成排列+枚举 + BFS)

Stealing Harry Potter's Precious Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1982    Accepted Submission(s): 931 Problem Description Harry Potter has some precious. For example, his invisib

九尾之火---算法生成的动画图像

之前实现了算法生成火的图像,现在我要做的是让火动起来.似乎我在算法生成图像的路上越来越走火入魔了.最近总在想一些挺无聊的东西,如雪花何以六出,闪电的形态是如何形成,还有就是火焰烟花的生成.在无风的室内点燃一支蜡烛,其火苗是不停变化的,是什么造成它的变化? 火焰的本质是放热反应中反应区周边空气分子加热而高速运动,从而发光的现象. 火焰一般可分为三层:内层称内焰,带蓝色,因为供氧不足,燃烧不完全,具有还原作用,所以也称“还原焰”:中层明亮,温度比内层高:外层称外焰,为无色火焰,因为供氧充足,燃烧完全

太阳崇拜---64幅由算法生成的八芒星图像

八芒星与卐字符号是遍布于我国广大地域多个民族的文化符号.有学者认为神秘的卐字符号其实是八角星纹的简化变体,它们都代表了太阳在一个回归年的视循环运动,即一年四季的循环变化.八角星纹集中发现于长江中下游与黄河下游的中国东部地区, 在红山和良渚文化也有发现.与从赤峰到香港的中国东面沿海人面岩画分布带之间是否存在关联值得关注.如今,八芒星与卐字符号是遍布于我国广大地域多个民族的文化符号. 再发些从网上收集的八芒星相关资料,原址为:http://blog.sina.com.cn/s/blog_6a4e1c