剑指offer之面试题38:字符串的排列

本文解析了如何通过递归算法解决字符串排列和组合问题,涉及字符串排列示例、组合问题的解决方案,以及8皇后问题的探讨。讨论了如何遍历所有可能性并应用在特定场景中,如8皇后问题的解法策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

面试题38:字符串的排列

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

思路:

  • 把整个字符串的排列,看成两步:一、求出所有可能出现在第一个位置的字符,即把第一个字符和后面所有的字符交换。二、固定第一个字符,求后面所有字符的排列。
  • 在求后面所有字符的排列的时候,也按照同样的方法来计算。

代码实现:

package Question38;

import java.util.HashSet;
import java.util.Set;

public class T01 {

    static Set<String> strSet = new HashSet<>();

    public static void main(String[] args) {
        String str = "abc";
        StringBuilder sb1 = new StringBuilder();
        StringBuilder sb2 = new StringBuilder(str);
        solve(sb1, sb2);
        String[] result = new String[strSet.size()];
        int index = 0;
        for(String data : strSet) {
            result[index++] = data;
            System.out.println(data);
        }
    }

    public static void solve(StringBuilder sb1, StringBuilder sb2) {
        if(sb2.length() == 0) {
            strSet.add(sb1.toString());
        } else {
            int len = sb2.length();
            for(int i = 0; i < len; i++) {
                char a = sb2.charAt(i);
                sb1.append(sb2.charAt(i));
                sb2.deleteCharAt(i);
                solve(sb1, sb2);
                sb1.deleteCharAt(sb1.length()-1);
                sb2.insert(i, a);
            }
        }
    }
}

本题扩展:

如果不是求字符的所有排列,而是求字符的所有组合,应该怎么办?还是三个字符,那他们的组合有 a、b、c、ab、ac、bc、abc。当交换字符串中的两个字符时,虽然能得到两个不同的排列,当却是同一个组合。比如 ab 和 ba 是不同的排列,但却是同一个组合。

思路:如果输入 n 个字符,则这 n 个字符能构成 1,2…n的组合。在求长度为 m 的组合的时候,可以先固定第一个字符,然后在剩下的字符中选取 m-1 个字符。这时又可以把它分为固定第一个字符,再剩下的字符中选取 m-2 个字符。因此,也可以通过递归实现。

代码实现:

package Question38;

import java.util.HashSet;
import java.util.Set;

public class T02 {

    static Set<String> strSet = new HashSet<>();

    public static void main(String[] args) {
        String str = "abc";
        int m = 1;
        solve(str, m);
        for(String data : strSet) {
            System.out.println(data);
        }
    }

    public static void solve(String data, int m) {
        if(data == null || data.length() == 0) return ;
        for(int i = 1; i <= m; i++) {
            StringBuilder sb = new StringBuilder();
            String str = data;
            help(sb, str, i);
        }
    }

    public static void help(StringBuilder sb, String str, int m) {
        if(sb.length() == m) {
            strSet.add(sb.toString());
        } else {
            int len = str.length();
            for(int i = 0; i < len; i++) {
                sb.append(str.charAt(i));
                help(sb, str.substring(i+1), m);
                sb.deleteCharAt(sb.length() - 1);
            }
        }
    }
}

相关题目

1、输入一个含有 8 个数字的数组,判断有没有可能把这 8 个数字分别放到正方体的 8 个顶点上,使得正方体上三组相对的面上的 4 个顶点的和都相等。

在这里插入图片描述
思路:找出所有的排列,然后看是否满足条件 a1+a2+a3+a4=a5+a6+a7+a8 && a1+a2+a5+a6=a3+a4+a7+a8 && a1+a3+a5+a7=a2+a4+a6+a8。

代码实现:

package Question38;

import java.util.ArrayList;
import java.util.List;

public class T03 {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
        System.out.println(solve(arr));
    }

    public static boolean solve(int[] arr) {
        if(arr == null || arr.length < 8) return false;
        List<Integer> list2 = new ArrayList<>();
        for(int data : arr) {
            list2.add(data);
        }
        List<Integer> list1 = new ArrayList<>();
        return help(list1, list2);
    }

    public static boolean help(List<Integer> list1, List<Integer> list2) {
        if(list1.size() == 8) {
            if(panduan(list1)) {
                System.out.println(list1);
                return true;
            }
        } else {
            int len = list2.size();
            for(int i = 0; i < len; i++) {
                int num = list2.get(i);
                list1.add(num);
                list2.remove(i);
                if(help(list1, list2)) return true;
                list1.remove(list1.size() - 1);
                list2.add(i, num);
            }
        }
        return false;
    }

    public static boolean panduan(List<Integer> list) {
        if(list.size() != 8) return false;
        int a1 = list.get(0) +list.get(1) + list.get(2) + list.get(3);
        int a2 = list.get(4) + list.get(5) + list.get(6) + list.get(7);
        int a3 = list.get(0) + list.get(1) + list.get(4) + list.get(5);
        int a4 = list.get(2) + list.get(3) + list.get(6) + list.get(7);
        int a5 = list.get(0) + list.get(2) + list.get(4) + list.get(6);
        int a6 = list.get(1) + list.get(3) + list.get(5) + list.get(7);
        return (a1 == a2) && (a3 == a4) && (a5 == a6);
    }
}

2、在 8×8 的国际象棋上摆放 8 个皇后,使其不能相互攻击,即任意两个皇后不得处于同一行、同一列、或者同一个对角线上。请问总共有多少种摆法?

思路:用一个数组表示八皇后拜访的位置。数组的下标代表皇后的行,值代表皇后拜访的列。所以,行肯定是不相同的,列取(1-8),所以也不会有两个及以上的皇后在同一列。在判断是否在同一斜线上时,可以用 arr[i] - arr[j] == i - j || arr[i] - arr[j] = j - i;

代码实现:

package Question38;

import java.util.ArrayList;
import java.util.List;

public class T04 {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
        System.out.println(solve(arr));
    }

    public static int solve(int[] arr) {
        if(arr == null || arr.length != 8) return 0;
        List<Integer> list1 = new ArrayList<>();
        List<Integer> list2 = new ArrayList<>();
        for(int data : arr) {
            list2.add(data);
        }
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        help(list1, list2, result);
        return result.size();
    }

    public static void help(List<Integer> list1, List<Integer> list2, List<List<Integer>> result) {
        if(list1.size() == 8) {
            result.add(list1);
        } else {
            int len = list2.size();
            for(int i = 0; i < len; i++) {
                int num = list2.get(i);
                if(panduan(list1, num)) {
                    list1.add(num);
                    list2.remove(i);
                    help(list1, list2, result);
                    list1.remove(list1.size() - 1);
                    list2.add(i, num);
                }
            }
        }
    }

    public static boolean panduan(List<Integer> list, int num) {
        if(list.size() == 0) return true;
        int len = list.size();
        for(int i = 0; i < len; i++) {
            if((num - list.get(i) == len - i) || (num - list.get(i) == i - len)) return false;
        }
        return true;
    }
}

举一反三

如果面试题是按照一定要求摆放若干个数字,则可以先求出这些数字的所有排列,然后一一判断每个排列是不是满足题目给定的要求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值