【试题描述】

输入一个字符串,打印出该字符串中字符的所有排列。你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。

例如输入字符串abc,则打印出a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab,cba。

剑指 Offer 38. 字符串的排列 https://leetcode-cn.com/problems/zi-fu-chuan-de-pai-lie-lcof/

我们以三个字符abc为例来分析一下求字符串排列的过程。首先我们固定第一个字符a,求后面两个字符bc的排列。当两个字符bc的排列求好之后,我们把第一个字符a和后面的b交换,得到bac,接着我们固定第一个字符b,求后面两个字符ac的排列。现在是把c放到第一位置的时候了。记住前面我们已经把原先的第一个字符a和后面的b做了交换,为了保证这次c仍然是和原先处在第一位置的a交换,我们在拿c和第一个字符交换之前,先要把b和a交换回来。在交换b和a之后,再拿c和处在第一位置的a进行交换,得到cba。我们再次固定第一个字符c,求后面两个字符b、a的排列。

既然我们已经知道怎么求三个字符的排列,那么固定第一个字符之后求后面两个字符的排列,就是典型的递归思路了。

为方便起见,用123来示例下。123的全排列有123、132、213、231、312、321这六种。首先考虑213和321这二个数是如何得出的。显然这两个都是123中的1与后面两数交换得到的。然后可以将123的第二个数和每三个数交换得到132。同理可以根据213和321来得231和312。因此可以知道——全排列就是从第一个数字起每个数分别与它后面的数字交换。找到这个规律后,递归的代码就很容易写出来了:

对于一个n 位的字符串来讲,它是n-1位字符串的排列 加上 没有在 n -1 位字符串里那个字符 的排列。

有点难理解,用例子说明:对于字符串ABC来讲,它所有的排列就是 A + BC 的排列 加上 B + AC 的排列,再加上 C + AB的排列。而BC的排列是 B + C 的排列 加上 C + B 的排列。所以,对一个字符串,我们从中去一个值,然后求剩余部分的排列,然后把它们再组合在一起。所有,代码如下

【参考代码】

如果字符串中有重复字符的话,这个方法肯定不会符合要求的

public static void permutation(char[] arr, int begin)
{// if pBegin points to the end of string,// this round of permutation is finished,// print the permuted stringif (begin == arr.length)System.out.println(Arrays.toString(arr));else{for (int i = begin; i < arr.length; i++){swap(arr, i, begin);permutation(arr, begin + 1);// restore pCh and pBegin// PS:改变字符串顺序后必须还原回来!swap(arr, i, begin);}}
}public static void swap(char[] arr, int i, int j)
{char temp = arr[i];arr[i] = arr[j];arr[j] = temp;
}

二、去掉重复的全排列的递归实现

由于全排列就是从第一个数字起每个数分别与它后面的数字交换。我们先尝试加个这样的判断——如果一个数与后面的数字相同那么这二个数就不交换了。如122,第一个数与后面交换得212、221。然后122中第二数就不用与第三个数交换了,但对212,它第二个数与第三个数是不相同的,交换之后得到221。与由122中第一个数与第三个数交换所得的221重复了。所以这个方法不行。

换种思维,对122,第一个数1与第二个数2交换得到212,然后考虑第一个数1与第三个数2交换,此时由于第三个数等于第二个数,所以第一个数不再与第三个数交换。再考虑212,它的第二个数与第三个数交换可以得到解决221。此时全排列生成完毕。
这样我们也得到了在全排列中去掉重复的规则——去重的全排列就是从第一个数字起每个数分别与它后面非重复出现的数字交换。

 1 // 在[nBegin,nEnd)区间中是否有字符与下标为pEnd的字符相等2     static boolean isSwap(char[] arr ,int pBegin, int pEnd)3     {4         int p;5         for (p = pBegin; p < pEnd; p++)6         {7             if (arr[p] == arr[pEnd])8                 return false;9         }
10         return true;
11     }
12
13     public static void permutation2(char[] arr, int begin)
14     {
15         // if pBegin points to the end of string,
16         // this round of permutation is finished,
17         // print the permuted string
18         if (begin == arr.length)
19             System.out.println(Arrays.toString(arr));
20
21         else
22         {
23             for (int i = begin; i < arr.length; i++)
24             {
25                 if (isSwap(arr,begin, i))
26                 {
27                     swap(arr, begin, i);
28                     permutation(arr, begin + 1);
29                     // restore pCh and pBegin
30                     // PS:改变字符串顺序后必须还原回来!
31                      swap(arr, begin, i);
32                 }
33             }
34         }
35     }

三、全排列的非递归实现
    要考虑全排列的非递归实现,先来考虑如何计算字符串的下一个排列。如"1234"的下一个排列就是"1243"。只要对字符串反复求出下一个排列,全排列的也就迎刃而解了。
如何计算字符串的下一个排列了?来考虑"926520"这个字符串,我们从后向前找第一双相邻的递增数字,"20"、"52"都是非递增的,"26 "即满足要求,称前一个数字2为替换数,替换数的下标称为替换点,再从后面找一个比替换数大的最小数(这个数必然存在),0、2都不行,5可以,将5和2交换得到"956220",然后再将替换点后的字符串"6220"颠倒即得到"950226"。
对于像“4321”这种已经是最“大”的排列,采用STL中的处理方法,将字符串整个颠倒得到最“小”的排列"1234"并返回false。

这样,只要一个循环再加上计算字符串下一个排列的函数就可以轻松的实现非递归的全排列算法。按上面思路并参考STL中的实现源码,不难写成一份质量较高的代码。值得注意的是在循环前要对字符串排序下,可以自己写快速排序的代码

转:http://blog.csdn.net/zz198808/article/details/7657168

总结:
1、全排列就是从第一个数字起每个数分别与它后面的数字交换。
2、去重的全排列就是从第一个数字起每个数分别与它后面非重复出现的数字交换。
3、全排列的非递归就是由后向前找替换数和替换点,然后由后向前找第一个比替换数大的数与替换数交换,最后颠倒替换点后的所有数据。

[字符串] --- 字符串的排列(剑指 Offer 38)相关推荐

  1. 【LeetCode】剑指 Offer 38. 字符串的排列

    [LeetCode]剑指 Offer 38. 字符串的排列 文章目录 [LeetCode]剑指 Offer 38. 字符串的排列 package offer;import java.util.Hash ...

  2. ++递归 字符串全排列_剑指 Offer 38. 字符串的排列

    剑指 Offer 38. 字符串的排列 输入一个字符串,打印出该字符串中字符的所有排列. 你可以以任意顺序返回这个字符串数组,但里面不能有重复元素. 示例: 输入:s = "abc" ...

  3. LeetCode——剑指 Offer 38. 字符串的排列

    剑指 Offer 38. 字符串的排列 题目 输入一个字符串,打印出该字符串中字符的所有排列.你可以以任意顺序返回这个字符串数组,但里面不能有重复元素.示例:输入:s = "abc" ...

  4. 剑指 Offer 38. 字符串的排列

    import java.util.ArrayList; import java.util.HashSet; import java.util.List;/*** 剑指 Offer 38. 字符串的排列 ...

  5. 字符串专题-LeetCode:剑指 Offer 58 - II. 左旋转字符串、LeetCode 459.重复的子字符串、 代码思路和注意点

    文章目录 一.剑指 Offer 58 - II. 左旋转字符串 二.LeetCode 459.重复的子字符串 一.剑指 Offer 58 - II. 左旋转字符串 思路: 预留出n个字符空间s.res ...

  6. 字符串(一) | 剑指 Offer 58 - II. 左旋转字符串、541. 反转字符串 II、剑指 Offer 05. 替换空格、151. 反转字符串中的单词

    剑指 Offer 58 - II. 左旋转字符串 把前k个字符移动到结尾 翻转前k个字符,翻转剩余字符 翻转整个字符串 class Solution { public:void reverse(str ...

  7. 【击败时间100%】剑指 Offer 38. 字符串的排列

    立志用最少的代码做最高效的表达 输入一个字符串,打印出该字符串中字符的所有排列. 你可以以任意顺序返回这个字符串数组,但里面不能有重复元素. 示例: 输入:s = "abc" 输出 ...

  8. list转字符串_剑指offer 38——字符串的排列

    本题主要在于对回溯的理解,优化时可以结合 java 特性,以及排列的一些知识. 原题 输入一个字符串,打印出该字符串中字符的所有排列. 你可以以任意顺序返回这个字符串数组,但里面不能有重复元素. 示例 ...

  9. 【算法】剑指 Offer 38. 字符串的排列 【重刷】

    1.概述 输入一个字符串,打印出该字符串中字符的所有排列. 你可以以任意顺序返回这个字符串数组,但里面不能有重复元素. 示例: 输入:s = "abc" 输出:["abc ...

最新文章

  1. MySQL8.0索引新特性:颠覆了我对索引的认知
  2. jmeter获取时间_time 函数
  3. mysql的错误号大全
  4. MINIGUI常见错误集及解决方法
  5. 三菱plc编程实例3000_三菱入门PLC编程PLC系统程序包括哪些
  6. Struts2笔记——15.Spring的事务
  7. PHP之旅4 php 超全局变量
  8. 2017电大c语言考试时间,2017年电大 《c语言程序设计》a课程考核说明.doc
  9. java高级语言特性,Java高级语言特性之注解
  10. 案例 月工作列表 c# 1614192274
  11. python代码错误有哪些_在编写python代码时,小白最容易犯的十几个错误 !
  12. C#.Net工作笔记018---葡萄城控件FlexGrid自定义单元格_以及给自定义控件添加自定义事件
  13. MyBatis调用存储过程,含有返回结果集、return参数和output参数
  14. [转载] 用 C++ 和 Java 写算法,差别大吗?
  15. eclipse进度条从后台还原到前台
  16. Linux基础开发工具
  17. uniapp iOS打包
  18. 形容谣言的四字词语_四字词语加解释大全
  19. 服务器电脑的作用,什么是wins服务器及其作用 -电脑资料
  20. 职称英语计算机考试取消,职称英语考试取消了吗

热门文章

  1. linux c 获取网关ip,linux sh 如何根据出口网关来获取本机出口ip
  2. SpringBoot集成Thymeleaf
  3. redis3.0.7_sds.c_sdsnewlen()
  4. 努力,做个淡定的女子
  5. 基于RBAC的权限设计
  6. 递归算法 流程图_什么是算法?如何学习算法?算法入门
  7. JAVA和遮掩_JAVA 你不知道的秘密 覆写,重载,隐藏,遮蔽,遮掩
  8. 广西大学计算机类开设课程,操作系统教学大纲-广西大学计算机与电子信息学院.DOC...
  9. u大侠pe系统桌面计算机,替换WinPE桌面背景的详细教程
  10. Binary String Reconstruction CodeForces - 1352F(思维+构造)