#数据结构与算法学习笔记#剑指Offer26:字符串的排列升级版 + 去重全排列问题3种解法 + 字典序算法 + 测试用例(Java、C/C++)

博客探讨了《剑指Offer》中的一道复杂全排列问题,涉及去重和字典序的处理。介绍了三种解法:交换递归法、字典序排列算法和深度遍历搜索(回溯法),并针对字符重复和字典序要求提出了改进策略。还提供了Java和C++的实现示例。
摘要由CSDN通过智能技术生成

2018.9.3     《剑指Offer》从零单刷个人笔记整理(66题全)目录传送门​​​​​​​

这道题比书上的题目还要复杂一些,解法总体思路差不多。花的时间比较长,因为是第一次接触全排列问题,初步实现+调试+改进花了不少时间,做完参考其他优秀的解法,又花时间进行了理解和学习,同时又从网上了解了其他很多有意思的全排列问题的解决方法,算法真是博大精深啊。题目主要复杂在哪里呢?1.字符重复,2.字典排序。因此这题可以简称为去重字典序全排列问题。

顾名思义,全排列就是一串字符的所有有序组合。生成全排列的算法有很多,这里给出三种比较常用的实现方法。解题思路:

方法一:交换递归法

将每次递归分为序列的第一个结点与后面其他结点,每次递归将第一个结点依次与其他结点交换,交换至末尾时对本次递归结果进行打印,每次执行算法后需要对交换进行复位以便进行下一次交换。

例如对于序列abc,第一层第一次交换为a与a交换,第二层第一次为b与b交换,第三层第一次为c与c交换,交换结束,打印abc;倒退一层,第二层第二次为b与c交换,第三层第一次为b与b交换,交换结束,打印acb;倒退两层,第一层第二次为a与b交换……最后打印出来的序列为abc acb bac bca cba cab。这就是原书题目的标准解答,也是一次全排列的实现过程了。

全排列是打印出来了,可是注意到后两次打印并不满足字典序,事实上因为是隔位交换,也不可能保证字典序。同时也无法保证字符重复造成的排列重复。因此还需要进行改进。

改进方案1:

对于字典序:可在排列之后利用Collections.sort对容器进行排序,字符串默认字典序。

对于字符重复:可在每次打印前对容器用contains进行字符重复的判断,如重复则不打印。

改进方案2:

先用TreeSet临时保存,可以保证排序和去重。(TreeSet可以理解成数组实现的二叉搜索树的容器,可以直接用addAll添加到ArrayList中)

方法二:字典序排列算法

一个全排列可看做一个字符串,字符串可有前缀、后缀。 生成给定全排列的下一个排列。所谓一个的下一个就是这一个与下一个之间没有其他的。这就要求这一个与下一个有尽可能长的共同前缀,也即变化限制在尽可能短的后缀上。例如839647521是1—9的排列。1—9的排列最前面的是123456789,最后面的987654321,从右向左扫描若都是增的,就到了987654321,也就没有下一个了。否则找出第一次出现下降的位置。

字典序排列算法分步如下,例如:

1.先对字符数组进行初始排序并打印初始序列

2.从尾部向前找第一个由大变小的数字(较大者)的位置lIndex

3.从i开始向后找到第一个小于lIndex-1的值的位置rIndex

4.将lIndex-1与rIndex-1位置对应的值进行交换,也即将从后往前第一个由大变小的数字(较小者)与从前往后最后一个大于前者的值

5.将lIndex-1后的序列进行翻转

6.打印这个序列,继续进行循环

如果直接理解起来晦涩难懂,可以参考一个例子——如何得到346987521的下一个 :

1.从尾部往前找第一个P(i-1) < P(i)的位置 3 4 6 <- 9 <- 8 <- 7 <-5 <- 2 <- 1 最终找到6是第一个变小的数字&

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值