剑指Offer:字符串的排列

题目描述

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述:输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。

递归法

(1) 遍历出所有可能出现在第一个位置的字符(即:依次将第一个字符同后面所有字符交换);在有重复值的情况下,每个数分别与它后面非重复出现的字符交换。
(2) 固定第一个字符,求后面字符的排列(即:在第1步的遍历过程中,插入递归进行实现)。从每个子串的第二个字符开始,依次与第一个字符交换,然后继续处理子串。
需要注意的几点:
(1) 递归的出口,就是只剩一个字符的时候。
(2) 输出的排列可能不是按字典顺序排列的,输出前需要排序。

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
public class Solution {
    public ArrayList<String> Permutation(String str) {
        ArrayList<String> list = new ArrayList<>();
        if(str!=null && str.length()>0){
            Permutation2(str.toCharArray(),0,list);
            Collections.sort(list);
        }
        return list;
    }
    //i表示i之前的字符固定不动,从i开始与后面的交换
    public void Permutation2(char[] ch,int i,ArrayList<String> list){
        if(i==ch.length-1){
            list.add(String.valueOf(ch));
        }else{
            HashSet<Character> hash = new HashSet<>();
            for(int j=i;j<ch.length;j++){ //j表示每次与i交换的位置,从i一直到末尾
                if(j==i || !hash.contains(ch[j])){ //遇到非重复的字符,才交换
                    hash.add(ch[j]);
                    swap(ch,i,j);
                    Permutation2(ch,i+1,list); //然后保持i之前的不变,从i+1开始交换
                    swap(ch,j,i);//再把i换回来,在下一次循环时,i与下下一位交换
                }
            }
        }
    }
    public void swap(char[] cs,int i,int j){
        char temp = cs[i];
        cs[i] = cs[j];
        cs[j] = temp;
    }
}

字典序排列算法

获得所有的字典序排列,例如 得到346987521的下一个排序:

  • 从尾部往前找第一个P(i-1) < P(i)的位置
    3 4 6 < 9 < 8 < 7 < 5 < 2 < 1 最终找到6是第一个变小的数字,记录下6的位置i-1
  • 从i位置往后找到最后一个大于6的数
    3 4 6 > 9 > 8 > 7 5 2 1 最终找到7的位置,记录位置为m
  • 交换位置i-1和m的值
    3 4 7 9 8 6 5 2 1
  • 倒序i位置后的所有数据
  • 3 4 7 1 2 5 6 8 9
  • 则347125689为346987521的下一个排列
import java.util.ArrayList;
import java.util.Arrays;
public class Solution {
    public static ArrayList<String> Permutation(String str) {
        ArrayList<String> list = new ArrayList<String>();
        if(str==null || str.length()==0){
            return list;
        }
        char[] ch = str.toCharArray();
        Arrays.sort(ch);
        list.add(String.valueOf(ch));
        while(true){
            int L = ch.length-1; //从后往前
            int R ; //从前往后最后一个大于的位置
            while(L>=1 && ch[L-1]>=ch[L]){
                L--;
            }
            // 此时L-1的位置就是从后往前,第一个变小的位置
            if(L==0){
                break;
            }
            R = L;
            while(R<ch.length && ch[R]>ch[L-1]){
                R++;
            }
            //此时R-1的位置就是最后一个大于L-1位置的数
            swap(ch,L-1,R-1);
            reverse(ch,L);//交换之后,倒序L-1后面的数
            list.add(String.valueOf(ch));
        }
        return list;
    }
    private static void reverse(char[] ch, int L) {
        int R = ch.length-1;
        while(L<=R){
            swap(ch,L++,R--);
        }
    }
    public static void swap(char[] cs,int i,int j){
        char temp = cs[i];
        cs[i] = cs[j];
        cs[j] = temp;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_43165002/article/details/89502002