剑指offer面试题38(java版):字符串的排列

welcome to my blog

剑指offer面试题38(java版):字符串的排列

题目描述

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

输入描述

输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。

第四次做; 核心: 1)回溯; 2)使用HashSet在递归中去重, HashSet没有get方法, 得通过迭代器获取HashSet中的元素 3) 递归函数的循环主体中的sb.append(chs[index]); //选取的是索引index处的元素, 开始错写成i了
/*
排列用递归很好实现

回溯算法
*/
class Solution {
    public String[] permutation(String s) {
        if(s==null||s.length()==0)
            return new String[]{};
        char[] chs = s.toCharArray();
        List<String> list = new ArrayList<>();
        StringBuilder sb = new StringBuilder();
        core(list, sb, chs, 0);
        String[] strs = new String[list.size()];
        Iterator<String> ite = list.iterator();
        int i=0;
        while(ite.hasNext()){
            strs[i++] = ite.next();
        }
        return strs;
    }
    
    //递归函数逻辑: 获取所有的排列可能, 选择当前位置的元素, 处理完剩下所有位置的可能
    private void core(List<String> list, StringBuilder sb, char[] chs, int index){
        //base case
        if(index==chs.length){
            list.add(sb.toString());
            return;
        }
        //用于去重
        HashSet<Character> set = new HashSet<>();
        for(int i=index; i<chs.length; i++){
            //去重, 出现过的元素不再选取
            if(set.contains(chs[i]))
                continue;
            set.add(chs[i]);
            //改变现场
            swap(chs, index, i);
            sb.append(chs[index]); //确定索引index处的元素, 开始错写成i了
            //新条件新递归
            core(list, sb, chs, index+1);
            //恢复现场
            sb.deleteCharAt(sb.length()-1);
            swap(chs, index, i);
        }
    }
    
    private void swap(char[] arr, int i, int j){
        char tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
}
第四次做; 回溯; 使用HashSet在base case中去重, HashSet没有get方法, 得通过迭代器获取HashSet中的元素
/*
排列用递归很好实现

回溯算法
*/
class Solution {
    public String[] permutation(String s) {
        if(s==null||s.length()==0)
            return new String[]{};
        char[] chs = s.toCharArray();
        HashSet<String> set = new HashSet<>();
        StringBuilder sb = new StringBuilder();
        core(set, sb, chs, 0);
        String[] strs = new String[set.size()];
        Iterator<String> ite = set.iterator();
        int i=0;
        while(ite.hasNext()){
            strs[i++] = ite.next();
        }
        return strs;
    }
    
    //递归函数逻辑: 获取所有的排列可能, 选择当前位置的元素, 处理完剩下所有位置的可能
    private void core(HashSet<String> set, StringBuilder sb, char[] chs, int index){
        //base case
        if(index==chs.length){
            String s = sb.toString();
            if(!set.contains(s))
                set.add(s);
            return;
        }
        //
        for(int i=index; i<chs.length; i++){
            //改变现场
            swap(chs, index, i);
            sb.append(chs[index]); //确定索引index处的元素, 开始错写成i了
            //新条件新递归
            core(set, sb, chs, index+1);
            //恢复现场
            sb.deleteCharAt(sb.length()-1);
            swap(chs, index, i);
        }
    }
    
    private void swap(char[] arr, int i, int j){
        char tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
}

笔记

  • 将char[]转成String不能用ch.toString(), 而是用String.valueOf(ch)
  • ch.toString()是调用ch这个对象的toString()方法, 并不是将ch的内容转成String
  • 具体见Object.toString()方法
  • str1.compareTo(str2) 比较两个字符串, 按照字典顺序进行排序, st1>str2返回一个大于0的整数
  • ArrayList al al.set(index, element)
  • ArrayList al al.get(index)
  • 使用HashSet处理重复问题, hs.add(element),如果返回true表示添加元素成功, 如果返回false表示添加元素失败, 说明hs中已经存在该元素
  • 其实List类中包含了contains方法,可以判断集合中是否已经存在某个元素, ArrayList当然也能用这个方法
  • 可以使用Collections.sort(al)对al中的元素进行排序(根据元素的自然顺序 对指定列表按升序进行排序)

思路

  • 使用递归函数每次处理一个位置
  • 第一个位置有n种选择, 第二个位置有n-1种选择…
  • 假设当前位置是index, 需要把其他位置的元素放到index上, 则可以将该元素和index位置上的元素交换. 这样原来index位置上的元素可以作为下一轮递归函数的index的候选之一
  • 在一次循环中,交换完元素,调用递归函数,最后还需要再交换刚才的两个元素,相当于复原了当前递归函数中的str,在下一轮循环中考虑该index位置上的其他可能的选项
  • 处理完所有位置后, 将结果添加到al中
第三次做
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Collections;
//
public class Solution {
    public ArrayList<String> Permutation(String str) {
        ArrayList<String> res = new ArrayList<>();
        if(str==null || str.length()==0)
            return res;
        char[] ch = str.toCharArray();
        Core(res, ch, 0);
        Collections.sort(res);
        return res;
    }
    public void Core(ArrayList<String> res, char[] ch, int index){
        if(index==ch.length){
            res.add(String.valueOf(ch));
            return;
        }
        HashSet<Character> hs = new HashSet<>();
        for(int i=index; i<ch.length; i++){
            if(hs.contains(ch[i]))
                continue;
            hs.add(ch[i]);
            //为index位置选择字符
            swap(ch, i, index);
            //递归处理剩下的位置
            Core(res, ch, index+1);
            //恢复
            swap(ch, i, index);
        }
    }
    public void swap(char[] ch, int i, int j){
        char temp = ch[i];
        ch[i] = ch[j];
        ch[j] = temp;
    }
}
第二遍做,精简了一下代码。 碰到一个问题:char[]转成String
  • base case中将char[]转成String不能用Arrays.toString(ch). 可以用new String(ch)或者String.valueOf(ch)
import java.util.ArrayList;
import java.util.Collections;

public class Solution {
    public ArrayList<String> Permutation(String str) {
       ArrayList<String> res = new ArrayList<>();
        if(str==null || str.length()==0)
            return res;
        char[] ch = str.toCharArray();
        Core(res,ch,0);
        Collections.sort(res);
        return res;
    }
    public void Core(ArrayList<String> res, char[] ch, int i){
        //base case
        if(i==ch.length && !res.contains(String.valueOf(ch))){
            res.add(String.valueOf(ch));
            return;
        }
        //
        //for循环的作用:尝试ch[i]所有可能的选择
        for(int k=i; k < ch.length; k++){
            swap(ch,i,k);
            Core(res, ch, i+1);
            swap(ch,i,k);
        }
    }
    public void swap(char[] ch, int i, int j){
        char temp = ch[i];
        ch[i] = ch[j];
        ch[j] = temp;
    }
}
第二遍做,思考过程比较慢; 避免重复的代码占用了较多额外空间,可以将最终的结果作为判断的基本单元,例如将最终结果abc加入HashSet,再次碰到abc时就不加入res了
  • 暂时没有改成动态规划版本
  • 关键的地方不仅仅是两次交换,还有就是当前位置的可选元素是从i到末尾,而不是0到末尾,因为i之前的位置的元素已经选好了,剩下可用的元素就是i到末尾的了
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Collections;

public class Solution {
    public ArrayList<String> Permutation(String str) {
        ArrayList<String> res = new ArrayList<>();
        if(str==null||str.length()==0)
            return res;
        StringBuilder sb = new StringBuilder();
        char[] ch = str.toCharArray();
        //可以先对字符数组排序,可能就不用对结果进行排序了, 试了试,并不行
        //Arrays.sort(ch);
        Core(res, ch, 0, sb);
        Collections.sort(res);
        return res;
    }
    public void Core(ArrayList<String> res, char[] ch, int i, StringBuilder sb){
        //base case
        if(i==ch.length){
            res.add(sb.toString());
            return;
        }
        //
        HashSet<Character> hs = new HashSet<>();
        for(int k=i; k<ch.length; k++){
            //重复的元素不能在同一位置出现
            if(hs.contains(ch[k]))
                continue;
            hs.add(ch[k]);
            //当前位置用哪个字符,就把当前位置的字符和相应位置的字符交换,这样就不用单独开辟空间记录可用的char  //
            swap(ch, i, k);
            sb.append(ch[i]);
            
            Core(res,ch,i+1,sb);
            //恢复现场
            sb.deleteCharAt(sb.length()-1);//和昨天做的一道题很像
            swap(ch,i,k);
        }
    }
    public void swap(char[] ch, int i, int j){
        char temp = ch[i];
        ch[i] = ch[j];
        ch[j] = temp;
    }
}
import java.util.ArrayList;
import java.util.HashSet;
public class Solution {
    public ArrayList<String> Permutation(String str) {
        ArrayList<String> al = new ArrayList<String>();
        if(str.length() == 0)
            return al;
        char[] chArr = str.toCharArray();
        HashSet<String> hs = new HashSet<String>();
        PermutationCore(chArr, 0, al, hs);
        bubble(al);
        return al;
    }
    public void PermutationCore(char[] chArr, int index, ArrayList<String> al, HashSet<String> hs){
        if(index == chArr.length && hs.add(String.valueOf(chArr)))
            al.add(String.valueOf(chArr));
            
        if(index < chArr.length){
            for(int i=index; i < chArr.length; i++){
                swap(chArr, index, i);
                PermutationCore(chArr, index+1, al, hs);
                swap(chArr, index, i);
            }
        }
    }
    public void swap(char[] chArr, int index, int i){
        char temp = chArr[index];
        chArr[index] = chArr[i];
        chArr[i] = temp;
    }
    public void bubble(ArrayList<String> al){
        for(int i=0; i<al.size(); i++){
            for(int j=0; j<al.size()-i-1; j++){
                if(al.get(j).compareTo(al.get(j+1)) > 0){
                    String temp = al.get(j);
                    al.set(j, al.get(j+1));
                    al.set(j+1, temp);
                }
            }
        }
    }
}
稍稍精简了下代码
  • 使用了Collections.sort()
  • 使用了al.contains()
import java.util.ArrayList;
import java.util.Collections;
public class Solution {
    public ArrayList<String> Permutation(String str) {
        ArrayList<String> al = new ArrayList<String>();
        if(str.length() == 0)
            return al;
        char[] chArr = str.toCharArray();
        PermutationCore(chArr, 0, al);
        Collections.sort(al);
        return al;
    }
    public void PermutationCore(char[] chArr, int index, ArrayList<String> al){
        if(index == chArr.length && !al.contains(String.valueOf(chArr)))
            al.add(String.valueOf(chArr));
            
        if(index < chArr.length){
            for(int i=index; i < chArr.length; i++){
                swap(chArr, index, i);
                PermutationCore(chArr, index+1, al);
                swap(chArr, index, i);
            }
        }
    }
    public void swap(char[] chArr, int index, int i){
        char temp = chArr[index];
        chArr[index] = chArr[i];
        chArr[i] = temp;
    }
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值