日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

list转字符串_剑指offer 38——字符串的排列

發(fā)布時(shí)間:2024/7/23 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 list转字符串_剑指offer 38——字符串的排列 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本題主要在于對回溯的理解,優(yōu)化時(shí)可以結(jié)合 java 特性,以及排列的一些知識(shí)。

原題

輸入一個(gè)字符串,打印出該字符串中字符的所有排列。

你可以以任意順序返回這個(gè)字符串?dāng)?shù)組,但里面不能有重復(fù)元素。

示例:

輸入:s = "abc" 輸出:["abc","acb","bac","bca","cab","cba"]

限制:

1 <= s 的長度 <= 8

原題url:https://leetcode-cn.com/problems/zi-fu-chuan-de-pai-lie-lcof

解題

回溯

回溯算法的基本思想是:從一條路往前走,能進(jìn)則進(jìn),不能進(jìn)則退回來,換一條路再試。

大家在解決經(jīng)典的八皇后問題時(shí),大多都會(huì)采用回溯進(jìn)行解決。

本問題其實(shí)就是求所有字符的排列組合,針對這種問題,也可以利用回溯進(jìn)行解決,但要求不能重復(fù),因此需要進(jìn)行剪枝。

比如字符串 abc ,如果讓我們求所有排列,肯定是:

  • 先固定第 1 位,從 a 、b 、 c 中選一個(gè),比如 a。
  • 在以 a 為第 1 位的前提下,固定第 2 位,從 b 、 c 中選一個(gè),比如 b。
  • 此時(shí)第 3 位也沒有可以選擇的余地了,只剩下 c,這一步就走完了。
  • 退回第 2 步,依舊在第 2 位,這次選擇 c 。
  • 此時(shí)第 3 位也沒有可以選擇的余地了,只剩下 b,這一步也走完了。
  • 退回第 1 步。
  • 從上面,你可以總結(jié)出,正常的回溯,就是先走一條路,當(dāng)結(jié)束后,退回上一步繼續(xù)走,反復(fù)執(zhí)行,直至退無可退,結(jié)束流程。

    我們可以發(fā)現(xiàn),最終是沒有可以選擇的余地,這在程序里可以理解為,運(yùn)行到下一位時(shí),不能使用之前使用過的數(shù)據(jù),因此會(huì)涉及到字符交換。

    但因?yàn)闀?huì)進(jìn)行回溯,所以數(shù)字可以在回溯后再換回去,從而不影響下一次的回溯。

    那什么叫剪枝呢?就是要排除一些情況,針對本題,就是要排除重復(fù)的情況。

    也就是在同一位置,不能出現(xiàn)兩次相同的字符,因?yàn)榈?2 次出現(xiàn)時(shí),之前肯定已經(jīng)針對這種情況,所有路線都已經(jīng)走過了。

    因此可以聯(lián)想到使用集合,存儲(chǔ)當(dāng)前位置出現(xiàn)過的字符,如果重復(fù),就可以直接跳過。

    接下來我們看看代碼:

    class Solution {char[] array;List<String> result = new LinkedList<>();public String[] permutation(String s) {array = s.toCharArray();// 回溯backtrack(0);// 賦值給數(shù)組String[] resultArray = new String[result.size()];int index = 0;for (String str : result) {resultArray[index] = str;index++;}return resultArray;}private void backtrack(int index) {// 如果是最后一個(gè)位置,就可以添加進(jìn)result中if (index == array.length - 1) {StringBuilder sb = new StringBuilder();for (char temp : array) {sb.append(temp);}result.add(sb.toString());return;}Set<Character> set = new HashSet<>();for (int i = index; i < array.length; i++) {// 保證不會(huì)重復(fù)if (set.contains(array[i])) {continue;}set.add(array[i]);// 交換兩者的位置swap(index, i);// 固定下一個(gè)位置,繼續(xù)尋找backtrack(index + 1);// 還原兩者的位置swap(i, index);}}private void swap(int index, int newIndex) {char temp = array[index];array[index] = array[newIndex];array[newIndex] = temp;} }

    提交OK。

    分析一下復(fù)雜度:

    • 時(shí)間復(fù)雜度 O(N!) : 這個(gè)比較好理解,長度為 N 的字符串,需要計(jì)算的次數(shù)是: N * (N - 1) * (N - 2) * ... * 2 * 1,結(jié)果也就是 N! 。
    • 空間復(fù)雜度 O(N^2) : 需要借助的額外空間,也就是那個(gè)保證不會(huì)重復(fù)所使用到的set,它所存儲(chǔ)的總量,最差情況下,長度為 N 的字符串中,所有字符各不相同,也就需要 N + (N - 1) + (N - 2) * ... * 2 * 1,結(jié)果也就是 N^2。

    java 優(yōu)化

    針對上面代碼中出現(xiàn)的 char[] 轉(zhuǎn) String,可以使用String.valueOf(char[])方法進(jìn)行優(yōu)化,因?yàn)樵摲椒?#xff0c;最終會(huì)使用System.arrayCopy方法,該方法屬于native方法,更加高效。

    至于最終,將 list 轉(zhuǎn) array 的過程,可以用list.toArray(String[])做寫法上的簡化,性能上倒并沒有什么提升。

    優(yōu)化后的代碼為:

    class Solution {char[] array;List<String> result = new LinkedList<>();public String[] permutation(String s) {array = s.toCharArray();// 回溯backtrack(0);// 賦值給數(shù)組return result.toArray(new String[result.size()]);}private void backtrack(int index) {// 如果是最后一個(gè)位置,就可以添加進(jìn)result中if (index == array.length - 1) {result.add(String.valueOf(array));return;}Set<Character> set = new HashSet<>();for (int i = index; i < array.length; i++) {// 保證不會(huì)重復(fù)if (set.contains(array[i])) {continue;}set.add(array[i]);// 交換兩者的位置swap(index, i);// 固定下一個(gè)位置,繼續(xù)尋找backtrack(index + 1);// 還原兩者的位置swap(i, index);}}private void swap(int index, int newIndex) {char temp = array[index];array[index] = array[newIndex];array[newIndex] = temp;} }

    繼續(xù)優(yōu)化

    其實(shí)到了,如果想進(jìn)一步優(yōu)化的話,可以針對 list 轉(zhuǎn) array 這里。

    因?yàn)槲覀兪褂玫氖?LinkedList,內(nèi)部存儲(chǔ)的 String 對象在物理上是不連續(xù)的,在最后遍歷時(shí)會(huì)相對比較耗時(shí)。

    如果我們一開始就可以求出所有該字符串所能獲得的所有不重復(fù)字符串的總個(gè)數(shù)的話,就可以提前構(gòu)造一個(gè) array,不需要在最后又遍歷一次 list 了。

    那么如何求出有重復(fù)字符的所有排列呢?假設(shè)是字符串a(chǎn)abbc,其求法為:

  • 假設(shè)先排 a ,一共 5 個(gè)位置,選 2 個(gè)位置,C(5, 2) = (5 * 4) / (2 * 1) = 10。
  • 再排 b ,剩下 3 個(gè)位置里,選 2 個(gè)位置,C(3, 2) = (3 * 2) / (2 * 1) = 3。
  • 最后排 c ,剩下 1 個(gè)位置里,選 1 個(gè)位置,C(1, 1) = 1。
  • 綜上,一共有10 * 3 * 1 = 30種排列。
  • 接下來看看代碼:

    class Solution {char[] array;String[] result;int resultIndex = 0;public String[] permutation(String s) {array = s.toCharArray();// 求出一共有多少種可能int totalCount = calculate();result = new String[totalCount];// 回溯backtrack(0);// 賦值給數(shù)組return result;}private int calculate() {// 各字符出現(xiàn)的次數(shù),默認(rèn)只會(huì)出現(xiàn)26個(gè)英文字母int[] countArray = new int[26];for (char temp : array) {countArray[temp - 'a'] += 1;}// 統(tǒng)計(jì)總次數(shù)int length = array.length;int totalCount = 1;for (int count : countArray) {if (count == 0) {continue;}// 求排列totalCount *= cc(length, count);length -= count;}return totalCount;}private int cc(int total, int count) {// 如果count超過total的一半,則換成 (total - count),因?yàn)樵谂帕兄?#xff0c;C(5, 4) = C(5, 1)if (count > total / 2) {count = total - count;}// 分別求分子、分母int result = 1;int result1 = 1;for (int i = 0; i < count; i++) {result *= (total - i);result1 *= (count - i);}return result / result1;}private void backtrack(int index) {// 如果是最后一個(gè)位置,就可以添加進(jìn)result中if (index == array.length - 1) {result[resultIndex++] = String.valueOf(array);return;}// 默認(rèn)只會(huì)出現(xiàn)26個(gè)英文字母boolean[] exists = new boolean[26];for (int i = index; i < array.length; i++) {// 保證不會(huì)重復(fù)if (exists[array[i] - 'a']) {continue;}exists[array[i] - 'a'] = true;// 交換兩者的位置swap(index, i);// 固定下一個(gè)位置,繼續(xù)尋找backtrack(index + 1);// 還原兩者的位置swap(i, index);}}private void swap(int index, int newIndex) {char temp = array[index];array[index] = array[newIndex];array[newIndex] = temp;} }

    提交OK,其執(zhí)行時(shí)間最短,因此認(rèn)為優(yōu)化是有效的。

    總結(jié)

    以上就是這道題目我的解答過程了,不知道大家是否理解了。本題主要在于對回溯的理解,優(yōu)化時(shí)可以結(jié)合 java 特性,以及排列的一些知識(shí)。

    有興趣的話可以訪問我的博客或者關(guān)注我的公眾號(hào)、頭條號(hào),說不定會(huì)有意外的驚喜。

    https://death00.github.io/

    公眾號(hào):健程之道

    總結(jié)

    以上是生活随笔為你收集整理的list转字符串_剑指offer 38——字符串的排列的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。