黑马程序员技术交流社区

标题: 给定一个String字符串,输出所有排列组合。求高手赐教? [打印本页]

作者: 歌癫    时间: 2014-3-27 18:03
标题: 给定一个String字符串,输出所有排列组合。求高手赐教?
本帖最后由 歌癫 于 2014-3-29 14:16 编辑

提问:给定一个字符串,如String str =“abcd”,输出它所有可能的排列组合。举例:“abcd”排列组合为>>"a","b","c","d","ab","ba","ac","ca","ad","da","bc","cb","bd","db","cd","dc","abc","acb","bac","bca","cab","cba","abd","adb","bda","bad","dab","dba","acd","adc","cda","cad","dac","dca","bcd","bdc","cdb","cbd","dbc","dcb","dabc","dacb","dbac","dbca","dcab","dcba","cabd","cadb","cbda","cbad","cdab","cdba","bacd","badc","bcda","bcad","bdac","bdca","abcd","abdc","acdb","acbd","adbc","adcb" 有这么多种排列组合。
我的分析:
1,一个字符串String str = "abcd"; 按组合长度的划分,可以分为length=str.length,就是给定的字符串多长就有多少种。
    如长度为1的,"a","b"等,长度为2的,"ab","ac"等;长度为3的,"abc","acd"等;长度为4的,"abcd"等;
    这是第一个难点。
2,把不同长度的字符取出来后,还要按不同顺序排列;如:”abc“,”bac“,”cba“,”acb“,”bca“,”cab“等
    这是我分析的第二个难点。
解决思路:
第一个难点:
1,一个给定长度的字符串,假如长度为4,”abcd“;那么这个字符串可以取出四种不同长度的字符串;我按从长到短的顺序来取,则有
长度=4,则改字符就是它本身;
长度=3,可以理解为”abcd“,四个字符依次删除一个,然后存储到一个新的数组里;
长度=2,就是单个字符两两组合,这个可以用嵌套循环实现;
长度=1,依次取出存放到另一个数组即可;

疑问:我这是以四个长度的字符串举例,如果是长度为8呢?甚至更长呢?这里我没有找到规律。
第二个难点:还没想到怎么解决?

请教各位前辈高手,如何解决这一问题?希望可以贴出代码和必要的注释。
非常感谢~~





作者: 911趣购    时间: 2014-3-27 20:02
同需要啊   我也在这个问题上纠结了半天了。。。。。
作者: 严旭晟    时间: 2014-3-29 09:14
楼主,我已经写了一段代码
见效果图:
不知道是否满足你的要求!

排列组合string.jpg (1.19 MB, 下载次数: 460)

排列组合string.jpg

作者: 严旭晟    时间: 2014-3-29 09:33
应该说,看到这一个题目要求应该下意识想到嵌套循环或者递归的思想,可联想九九乘法表的打印过程
我的思路:
01:最笨的方法,以题中的例子,str=“abcd”来说,
      #1:一个字母:  a     b    c    d//  for遍历
      #2 :两个字母:   aa   ab   ac  ad // for-for遍历
                              ba   bb   bc  bd
                              ca    cb   cc   cd
                              da    db  dc   dd
     由此可以见,A:每增加一个字母,就需要多嵌套一个循环;
                       B :内外循环要进行字符串拼接,即外循环遍历结果要和内循环遍历结果同时打印
for-for嵌套:
  1. for(int i=0; i< len; i++)
  2. {
  3.     for(int j = 0; j<len; j++)
  4.    {
  5.        System.out.print(origin.charAt(i));
  6.        System.out.print(origin.charAt(j));
  7.        System.out.print("\t");
  8.    }
  9. }
复制代码

  #2:转换成通用的递归方法
  思考得问题:01.递归的初值条件是什么?结束条件是什么?
           02.如何通过递归实现循环嵌套?
           02.如何保证内外嵌套保证同步打印?函数递归调用时要传人什么参数?
           03.如何正确输出数据?字符串拼接和格式控制
你可以朝这几个方向想想
作者: 严旭晟    时间: 2014-3-29 09:37
这个问题网上有很多思路,有的借助List、有的借助辅助的char[]转存拼接
我认为如果只是打印结果的话,完全没必要借助char[]转存!
只是需要字符串拼接和控制打印就可以了,原理其实同九九乘法表相似
作者: 小周务商    时间: 2014-3-29 10:17
这么深的问题哪来的。
作者: osully    时间: 2014-3-29 10:27
以下代码是牛人写的, 我一直存着,

递归的这种思路,虽然原理懂,但是实际运用起来,有时候还是很难想出来
  1. public class Test13
  2. {
  3.     public static String str = "abcd";
  4.     public static void main(String[] args)
  5.     {
  6.         show(0, new String());
  7.     }
  8.     public static void show(int current_recur, String temp)
  9.     {
  10.         if(current_recur < str.length())
  11.         {
  12.             for(int i = 0; i < str.length(); i++)
  13.             {
  14.                 if( ! ( temp.contains(str.substring(i, i + 1)) ) )
  15.                 {
  16.                     System.out.println(temp + str.substring(i, i + 1));
  17.                     show(current_recur + 1, new String(temp + str.substring(i, i + 1)));
  18.                 }
  19.             }
  20.         }
  21.     }
  22. }
复制代码

作者: 歌癫    时间: 2014-3-29 11:29
严旭晟 发表于 2014-3-29 09:37
这个问题网上有很多思路,有的借助List、有的借助辅助的char[]转存拼接
我认为如果只是打印结果的话,完全 ...

感谢你的解答!看你的回答,说明你是有过思考的。但并没有解决这个问题,却提供了一点思路;
1,排列组合,一是字符不能有重复的;二是顺序不同;
2,按你的打印结果,明显重复了很多。
你可以修改一下,或许就解决了。期待与你交流


作者: 歌癫    时间: 2014-3-29 13:14
小周务商 发表于 2014-3-29 10:17
这么深的问题哪来的。

唉,基础测试中的一个题目,想了好久都没想出了、、
进黑马的起点,果然是很高的呀……
作者: 菜小徐    时间: 2014-3-29 13:41
本帖最后由 菜小徐 于 2014-3-29 13:42 编辑
  1. package com.itheima;

  2. import java.util.Scanner;
  3. import java.util.TreeSet;

  4. /*
  5.         编程列出一个字符串的全字符组合情况,原始字符串中没有重复字符
  6.         例如:
  7.         原始字符串是"abc",打印得到下列所有组合情况
  8.         "a" "b" "c"
  9.         "ab" "bc" "ca" "ba" "cb" "ac"
  10.         "abc" "acb" "bac" "bca" "cab" "cba"
  11. * */
  12. public class Test6 {
  13.         public static void main(String[] args){
  14.                 //输入一个字符串
  15.                 System.out.print("请输入一个字符串:");
  16.                 Scanner sc=new Scanner(System.in);
  17.                 String str=sc.nextLine();
  18.                 //判断输入的字符串中是否有重复字符
  19.                 if(judge(str)){
  20.                         //调用输出方法
  21.                         show(str);               
  22.                 }else{
  23.                         System.out.println("您输入的字符串有重复字符");
  24.                 }
  25.                
  26.         }
  27.         //从str中取出字符,如果取出的字符在TreeSet中存在说明有重复字符,如果不存在放入
  28.         public static boolean judge(String str) {
  29.                 TreeSet tst=new TreeSet();
  30.                 for(int i=0;i<str.length();i++){
  31.                         char ch=str.charAt(i);
  32.                         if(tst.contains(ch)){
  33.                                 return false;
  34.                         }else{
  35.                                 tst.add(ch);
  36.                         }
  37.                 }
  38.                 return true;
  39.         }

  40.         public static void show(String str) {
  41.                 //调用递归方法
  42.                 //参数有字符串,当前操作数的位置,临时存放字符串的buf,存放组合情况的TreeSet
  43.                 TreeSet<String> ts=getString(new StringBuffer(str),0,new StringBuffer(),new TreeSet<String>());
  44.                 //打印所有组合
  45.                 for(String s:ts){
  46.                         System.out.println(s);
  47.                 }
  48.                
  49.         }

  50.         public static TreeSet<String> getString(StringBuffer str, int count,StringBuffer buf, TreeSet<String> set) {
  51.                 //以每个字母为开头循环
  52.                 for(int i=0;i<str.length();i++){
  53.                         //取出字母
  54.                         char c=str.charAt(i);
  55.                         //先把字母删除,以免重复
  56.                         str.deleteCharAt(i);
  57.                         //将当前可能放入缓存
  58.                         buf.append(c);
  59.                         //把组合放入set
  60.                         set.add(buf.toString());
  61.                         //如果str长度不为零,表示还有字符可以取,继续递归
  62.                         if(str.length()!=0){
  63.                                 //count用于记录目前在进行排列组合的第count位
  64.                                 count++;
  65.                                 //递归
  66.                                 getString(str,count,buf,set);
  67.                                 //第n位递归结束后,需要继续对n-1位排列,位数-1
  68.                                 count--;
  69.                         }
  70.                         //删除添加的字符,还原到之前的字符情况
  71.                         buf.deleteCharAt(count);
  72.                         //把删除的字母再放回去,还原
  73.                         str.insert(i, c);
  74.                 }
  75.                 return set;
  76.         }

  77. }
复制代码

作者: 歌癫    时间: 2014-3-29 14:15
菜小徐 发表于 2014-3-29 13:41

感谢版主赐教,这里的视频我还没有看完,谢谢啦~~
得加快学习进度了……
作者: 小周务商    时间: 2014-3-29 18:30
歌癫 发表于 2014-3-29 13:14
唉,基础测试中的一个题目,想了好久都没想出了、、
进黑马的起点,果然是很高的呀…… ...

哇。这么坑啊。完全看不懂。
作者: 严旭晟    时间: 2014-3-29 19:01
歌癫 发表于 2014-3-29 11:29
感谢你的解答!看你的回答,说明你是有过思考的。但并没有解决这个问题,却提供了一点思路;
1,排列组合 ...

你好!实际上,如果能打印出全部的排列组合,题目的要求只需要再加上一个筛选----将不重复的输出并计数!
当初看你的问题只是看了标题,没有看你所列的例子,故只写了一个打印全部组合的代码。
现在我进行修改,关键只是添加一个叫isRepeat()判断字符串重复的函数:
  1. public static boolean isRepeat(String st)
  2. {
  3.     int len = st.length();
  4.     boolean bool = false;
  5.     for(int i=0; i<len; i++) // check repeat situation
  6.     {        for(int j=i+1; j<len; j++)
  7.         {if(st.charAt(i) == st.charAt(j))
  8.          {
  9.             bool = true;
  10.             break;
  11.           }
  12.         }                               
  13.      }
  14.      return bool;
  15. }
复制代码


作者: 严旭晟    时间: 2014-3-29 19:19
代码效果如图,应该能满足需求,另附有代码文件

测试效果.jpg (1.23 MB, 下载次数: 168)

测试图

测试图

CombineDemo2.zip

1.03 KB, 下载次数: 458

代码文件


作者: 歌癫    时间: 2014-3-29 23:16
严旭晟 发表于 2014-3-29 19:19
代码效果如图,应该能满足需求,另附有代码文件

非常感谢你,祝你生活愉快,学习进步。
作者: 郭晓晨    时间: 2014-3-30 11:22
Mark一个
作者: nishi5151    时间: 2016-5-5 23:42
看的纠结
作者: 张洪慊    时间: 2016-8-9 00:18
搜到这个题
作者: qinye    时间: 2016-9-5 22:16
osully 发表于 2014-3-29 10:27
以下代码是牛人写的, 我一直存着,

递归的这种思路,虽然原理懂,但是实际运用起来,有时候还是很难想出来

一脸懵逼。。
作者: 足球骑士szw    时间: 2016-10-9 00:18
卧槽,今天被卡主了,前来向前辈们学习
作者: 足球骑士szw    时间: 2016-10-9 00:38
菜小徐 发表于 2014-3-29 13:41

膜拜一下前辈,希望有一天能自己写出这么牛逼的代码
作者: lussen    时间: 2018-4-12 10:03
严旭晟 发表于 2014-3-29 19:19
代码效果如图,应该能满足需求,另附有代码文件

非常感谢大佬指教!




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2