资讯专栏INFORMATION COLUMN

July 算法习题 - 字符串4(全排列和全组合)

tuniutech / 2794人阅读

摘要:求字符串的全排列字符串的全排列设计一个算法,输出一个字符串字符的全排列。的做法没有结果的,都是在一个字符串上进行的操作。字符串的全组合输入三个字符,则它们的组合有。因此可以循环字符串长度,然后输出对应代表的组合即可。

  

求字符串的全排列

字符串的全排列

设计一个算法,输出一个字符串字符的全排列。
比如,String = "abc"
输出是"abc","bac","cab","bca","cba","acb"

算法思想

从集合依次选出每一个元素,作为排列的第一个元素,然后对剩余的元素进行全排列,如此递归处理;

比如:首先我要打印abc的全排列,就是第一步把a 和bc交换(得到bac,cab),这需要一个for循环,循环里面有一个swap,交换之后就相当于不管第一步了,进入下一步递归,所以跟一个递归函数, 完成递归之后把交换的换回来,变成原来的字串
递归方法1(July 方法):

abc 为例子:
1. 固定a, 求后面bc的全排列: abc, acb。 求完后,a 和 b交换; 得到bac,开始第二轮
2. 固定b, 求后面ac的全排列: bac, bca。 求完后,b 和 c交换; 得到cab,开始第三轮
3. 固定c, 求后面ba的全排列: cab, cba
 即递归树: 
     str:   a         b         c
         ab ac       ba bc          ca cb
     result:     abc acb      bac bca          cab cba
  

    public static void Permutation(char[] s, int from, int to) {
        if(to<=1)
            return;
        if(from == to){
            System.out.println(s);
        }
        else{
            for(int i=from;i<=to;i++){
                swap(s,i,from);
                Permutation(s,from+1,to);
                swap(s,from,i);
                }
        }
    }

    public static void swap(char[] s, int i, int j) {
        char temp = s[i];
        s[i] = s[j];
        s[j] = temp;
    }

递归方法2:
与上面算法区别:
本算法需要一个额外的存储空间存放结果(buffer),固定第一个位置是哪个元素的时候,是通过一个循环,然后看原始字符串上,每一个位置是什么元素。July的做法没有结果的buffer,都是在一个字符串上进行的操作。第一个swap的作用就是,依次拿起始字符和后面的每一个字符交换,这样就能遍历第一个位置上的所有可能字符

推荐一个youtube讲解的视频

n个数的全排列,一共有n!种情况. (n个位置,第一个位置有n种,当第一个位置固定下来之后,第二个位置有n-1种情况...)

全排列的过程:

选择第一个字符

获得第一个字符固定下来之后的所有的全排列

选择第二个字符

获得第一+ 二个字符固定下来之后的所有的全排列

从这个过程可见,这是一个递归的过程。

还有一点需要注意是:
之前递归过程选择的字符,下一次不能再被选: 第一个位置选了a, 其他位置就不能选a了
解决方法是1. 扫描之前选择的字符 或者 2.创建一个与字符串等长的boolean数组,标记该位置对于的字符是否已经选择。若选择,则标记true; 若未选择,则标记false.

public class Permutation {
    public static void permute(String str){
        int length = str.length();
        boolean[] used = new boolean[length];
        StringBuffer output = new StringBuffer(length);

        permutation(str,length,output,used,0);

    }

    // @para
    // position : 下一个放置的元素位置,所以调入时候是0
    // 
    static void permutation(String str, int length, StringBuffer output, boolean[] used, int position){
        // end of the recursion
        if(position == length){
            System.out.println(output.toString());
            return;
        }
        else{
            for(int i=0;i

个人认为这个算法不如第一个递归方法,因为需要额外的空间;但是二者的时间复杂度是相同的,都是O(n!)

字符串的全组合

输入三个字符 a、b、c,则它们的组合有a b c ab ac bc abc。当然我们还是可以借鉴全排列的思路,利用问题分解的思路,最终用递归解决。不过这里介绍一种比较巧妙的思路 —— 基于位图。
假设原有元素n个,最终的组合结果有2^n - 1. 可以使用2^n - 1个位,1表示取该元素,0表示不取。 所以a表示001,取ab是011。
001,010,011,100,101,110,111。对应输出组合结果为:a,b,ab,c,ac,bc,abc
因此可以循环 1~2^n-1(字符串长度),然后输出对应代表的组合即可。

    public static void Combination(char [] s){
        if(s.length == 0){
            return;
        }
        int len = s.length;
        int n = 1<

    for(int j=0;j

j = 0, 1< j = 1, 1< j = 2, 1< 有限制的组合

Leetcode

  

Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.
For example,
If n = 4 and k = 2, a solution is:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]

解题思路

基于位操作,这里我们主要借助一个二进制操作 “ 求最小的、比 x 大的整数 M,使得 M 与 x 的二进制表示中有相同数目的 1”,如果这个操作已知,那么我们可以设置一个初始整数 bit,bit 的低位第 1~k 个二进制位为 1,其余二进制位为 0,bit 的二进制表示一种组合,然后调用上述操作求得下一个 bit,bit 的最大值为:bit 从低位起第 n-k+1~n 位等于 1,其余位等于 0,即 (1<

    public static List> combine(int n, int k) {
        if(n == 0 | k>n){
            return null;
        }
        int len = n;
        int nbit = 1<> result = new ArrayList>();
        //从1循环到2^len-1
        for(int i=kbit-1; i<= inbit; i = nextn(i)){
            List list = new ArrayList();
            for(int j=0;j>2;
    }
附录: 位操作
  

求整数的二进制表示中有多少个 1

方法1

应用了n&=(n-1)能将 n 的二进制表示中的最右边的 1 翻转为 0 的事实。只需要不停地执行 n&=(n-1),直到 n 变成 0 为止,那么翻转的次数就是原来的 n 的二进制表示中 1 的个数,其代码如下:

    public int count1Bits(int n){
        int count = 0;
        while(n!=0){
            count++;
            n = n & (n-1);
        }
        return count;
    }
NextN
  

给定一个正整数 N,求最小的、比 N 大的正整数 M,使得 M 与 N 的二进制表示中有相同数目的 1

方法1: 简单枚举
从 N+1 开始枚举,对每个数都测试其二进制表示中的 1 的个数是否与 N 的二进制表示中 1 的个数相等,遇到第一次相等时就停止

    public int GetNextN(int n){
        int k = count1Bits(n);
        do{
            n++;
        }while(count1Bits(n) != k);
        return n;
    }

方法2: O(1)时间高效方法
参考

public int NextN(int n){
    int x = n&(-n);
    int t = n + x;
    int ans = t | ((n^t)/x)>>2;
    return ans;
}

想更一进步的支持我,请扫描下方的二维码,你懂的~

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/64349.html

相关文章

  • 符串排列

    摘要:问题输入一个字符串按字典序打印出该字符串中字符的所有排列。如此递归处理,从而得到所有字符的全排列。记斐波那契数列的第位这件事为,则有。其中,表示去掉那个开头字符的剩余字符串的全排列。 问题 输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。 地址:https://...

    sunny5541 评论0 收藏0
  • July算法习题 - 符串1

    摘要:反转上述步骤得到的结果字符串,即反转字符串的两部分和给予反转,得到,形式化表示为,这就实现了整个反转。例如,原字符串为,,输出结果为。同单词翻转输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变,句子中单词以空格符隔开。 July 程序员编程艺术:面试和算法心得题目及习题 旋转字符串 题目描述 给定一个字符串,要求把字符串前面的若干个字符移动到字符串的尾部,如...

    Betta 评论0 收藏0
  • July 算法习题 - 符串2 + Leetcode 8,9

    摘要:判断一条单向链表是不是回文解法可以借助栈,将遍历到的前半段链表节点放入栈,后半段每当遍历到一个,都要与出栈的节点相比较。如果中间出现不相等的情况,则不是回文。 [July 程序员编程艺术:面试和算法心得题目及习题][1] 字符串转换成整数 also Leetcode 8 String to Integer (atoi) 题目描述 输入一个由数字组成的字符串,把它转换成整...

    timger 评论0 收藏0
  • 符串处理文章outline

    摘要:遇到问题查查,看看,大神的讲解问问岛胖君下面是我最近整理出来的关于字符串的文章的怎么翻译汇集目录非常希望强化博客的功能,比如分类,置顶。 虽是读书笔记,但是如转载请注明出处 http://segmentfault.com/blog/exploring/ .. 拒绝伸手复制党 最近在看算法和语言,基本属于看知识 --> java实现 --> 整理blog 这个路线。 遇到问题查查st...

    Karuru 评论0 收藏0
  • 原生js练习题---第二课(下)

    摘要:最后,我们只要在事件处理程序中获得这个布尔值传给这几个函数就可以了,其中,全选框反选链接可以从全选框的属性获得这个值,而全体的复选框要获得就得靠遍历了。 0x1播放列表收缩展开 实现效果 值得注意的一个地方是那个箭头,我这里只是用了简单的字符串替换,而原题用了背景图片移动来实现切换箭头,但是似乎那样做会导致整个容器的左右偏移、效果不是很好。 0x2鼠标移过显示车标 实现效果 这题看起来...

    Little_XM 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<