劍指offer刷題總結——字符串篇(一)

星級:1

1.字符串的排列

【題目】
輸入一個字符串,按字典序打印出該字符串中字符的所有排列。例如輸入字符串 abc, 則打印出由字符 a,b,c 所能排列出來的所有字符串 abc,acb,bac,bca,cab 和 cba。

輸入一個字符串,長度不超過9(可能有字符重複),字符只包括大小寫字母。

【代碼】

package swear2offer.strs;

import java.util.ArrayList;
import java.util.TreeSet;

public class StrRange {

    /**
     * 輸入一個字符串,按字典序打印出該字符串中字符的所有排列。
     * 例如輸入字符串 abc, 則打印出由字符 a,b,c
     * 所能排列出來的所有字符串 abc,acb,bac,bca,cab 和 cba。
     *
     * 輸入一個字符串,長度不超過9(可能有字符重複),字符只包括大小寫字母。
     * */

    StringBuilder path = new StringBuilder();
    // 使用HashSet是爲了保證存儲的stringBuilder不存在重複
    TreeSet<String> paths = new TreeSet<>();
    // 標記數組
    boolean[] flag;
    ArrayList<String> res = new ArrayList<>();

    public ArrayList<String> Permutation(String str) {

        if (str.equals("") || str == null) return res;

        char[] arr = str.toCharArray();
        flag = new boolean[arr.length];

        All(arr,0);

        res.addAll(paths);

        return res;
    }

    /**
     * 符合條件的時候返回,不符合條件的時候回溯
     * */
    public void All(char[] str, int len) {

        // 回溯跳出條件
        if (len == str.length) {
            paths.add(path.toString());
            return;
        }

        for (int i=0; i<str.length; i++) {
            if (!flag[i]) {
                flag[i] = true;
                path.append(str[i]);
                All(str,len+1);
                flag[i] = false;
                path.deleteCharAt(path.length()-1);
            }
        }

    }

    public static void main(String[] args) {
        String s = "abc";
        System.out.println(new StrRange().Permutation(s).toString());
    }
}

【思考】

  • 在沒有重複的情況下是全排列,有重複的情況下使用set去重,又因爲是需要字典序輸出,所以使用treeset保存結果。
  • 全排列這種情況通常是需要使用回溯的,回溯的幾個重點:
    • 算子for循環
    • 單次路徑和路徑總個數
    • 標記數組,用於回溯
    • 回溯終止的條件
    • 回溯的公式

2.把數組排成最小的數

【題目】

輸入一個正整數數組,把數組裏所有數字拼接起來排成一個數,打印能拼接出的所有數字中最小的一個。例如輸入數組 {3,32,321},則打印出這三個數字能排成的最小數字爲 321323。

【代碼】

	/**
     * 輸入一個正整數數組,把數組裏所有數字拼接起來排成一個數,
     * 打印能拼接出的所有數字中最小的一個。例如輸入數組 {3,32,321},
     * 則打印出這三個數字能排成的最小數字爲 321323。
     *
     * 利用回溯的思想,
     * 標記數組,總路徑,單路徑,算子循環,回溯
     * */
    TreeSet<String> paths;
    StringBuilder path;
    boolean[] flag;

    public String PrintMinNumber(int [] numbers) {

        int n;

        n = numbers.length;

        if (n == 0 || numbers == null) return "";

        paths = new TreeSet<>();
        path = new StringBuilder();
        flag = new boolean[n];

        Find(numbers,0);

        return paths.first();


    }

    /**
     * n相當於一個標記
     * 用來判斷什麼時候達到回溯的終點
     * */
    public void Find(int[] a, int n) {
        if (n == a.length) {
            paths.add(path.toString());
            return;
        }

        int i;

        for (i=0; i<a.length; i++) {
            if (!flag[i]) {
                flag[i] = true;
                path.append(a[i]);

                Find(a,n+1);

                flag[i] = false;
                // 因爲加進去的是字符串,所以需要指定範圍刪除
                int start,end;
                String add = a[i]+"";
                start = path.length() - add.length();
                end = path.length();
                path.delete(start, end);
            }
        }
    }

【思考】

使用回溯的方法複雜度要搞點,本題還可以使用排序的方法,先把數組變成字符串,然後給這些字符串排序,之後再把字符串連接起來即可

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章