阿俊帶你用Kotlin刷算法(二)

本系列通過(guò)JavaKotlin這兩種語(yǔ)言來(lái)解決力扣上面的算法題撼泛,由于本人算法菜鳥(niǎo)一枚冷离,可能部分題目并不是最優(yōu)題解吵冒,希望能和各位大神共同討論~

阿俊帶你用Kotlin刷算法(一)

阿俊帶你用Kotlin刷算法(二)

項(xiàng)目的GitHub:Algorithm

尋找兩個(gè)正序數(shù)組的中位數(shù)(Median of Two Sorted Arrays)

難度:困難

鏈接:Median of Two Sorted Arrays

代碼

Java

/**
 * Created by TanJiaJun on 2021/6/9.
 * 4. 尋找兩個(gè)正序數(shù)組的中位數(shù)(Median of Two Sorted Arrays)
 * 難度:困難
 *
 * @see <a >Median of Two Sorted Arrays</a>
 */
class MedianOfTwoSortedArrays {

    public static void main(String[] args) {
        // 示例一
        System.out.println("示例一:");

        int[] firstNumbers = {1, 3};
        int[] secondNumbers = {2};
        System.out.println(findMedianSortedArrays(firstNumbers, secondNumbers));

        System.out.print("\n");

        // 示例二
        System.out.println("示例二:");

        int[] thirdNumbers = {1, 2};
        int[] fourthNumbers = {3, 4};
        System.out.println(findMedianSortedArrays(thirdNumbers, fourthNumbers));

        System.out.print("\n");

        // 示例三
        System.out.println("示例三:");

        int[] fifthNumbers = {0, 0};
        int[] sixthNumbers = {0, 0};
        System.out.println(findMedianSortedArrays(fifthNumbers, sixthNumbers));

        System.out.print("\n");

        // 示例四
        System.out.println("示例四:");

        int[] seventhNumbers = {};
        int[] eightNumbers = {1};
        System.out.println(findMedianSortedArrays(seventhNumbers, eightNumbers));

        System.out.print("\n");

        // 示例五
        System.out.println("示例五:");

        int[] ninthNumbers = {2};
        int[] tenthNumbers = {};
        System.out.println(findMedianSortedArrays(ninthNumbers, tenthNumbers));
    }

    /**
     * 雙指針
     * 時(shí)間復(fù)雜度:O(m+n),其中m是數(shù)組num1的長(zhǎng)度西剥,n是數(shù)組num2的長(zhǎng)度
     * 空間復(fù)雜度:O(m+n)痹栖,其中m是數(shù)組num1的長(zhǎng)度,n是數(shù)組num2的長(zhǎng)度
     *
     * @param firstNumbers  第一個(gè)數(shù)組
     * @param secondNumbers 第二個(gè)數(shù)組
     * @return 結(jié)果
     */
    public static double findMedianSortedArrays(int[] firstNumbers, int[] secondNumbers) {
        int len1 = firstNumbers.length;
        int len2 = secondNumbers.length;
        // 合并后的數(shù)組的索引
        int index = 0;
        // 第一個(gè)數(shù)組的指針瞭空,下面用i指針描述
        int i = 0;
        // 第二個(gè)數(shù)組的指針揪阿,下面用j指針描述
        int j = 0;
        // 合并兩個(gè)數(shù)組
        // 創(chuàng)建一個(gè)大小是兩個(gè)數(shù)組長(zhǎng)度之和的數(shù)組
        int[] arrays = new int[len1 + len2];
        while (i < len1 || j < len2) {
            if (i == len1) {
                // 如果第一個(gè)數(shù)組遍歷完畢后,就繼續(xù)遍歷第二個(gè)數(shù)組
                arrays[index++] = secondNumbers[j++];
            } else if (j == len2) {
                // 如果第二個(gè)數(shù)組遍歷完畢后咆畏,就繼續(xù)遍歷第一個(gè)數(shù)組
                arrays[index++] = firstNumbers[i++];
            } else if (firstNumbers[i] < secondNumbers[j]) {
                // 如果第一個(gè)數(shù)組的元素小于第二個(gè)數(shù)組的元素南捂,就將第一個(gè)數(shù)組中的該元素添加到合并后的新數(shù)組,同時(shí)將i指針向右移動(dòng)一格
                arrays[index++] = firstNumbers[i++];
            } else {
                // 如果第一個(gè)數(shù)組的元素大于第二個(gè)數(shù)組的元素旧找,就將第二個(gè)數(shù)組中的該元素添加到合并后的新數(shù)組溺健,同時(shí)將j指針向右移動(dòng)一格
                arrays[index++] = secondNumbers[j++];
            }
        }
        // 找出數(shù)組的中位數(shù)
        double median;
        int length = arrays.length;
        if (length % 2 == 0) {
            // 如果數(shù)組長(zhǎng)度是偶數(shù),就找出這條中線旁邊的兩個(gè)元素钮蛛,然后相加之后除以2得到結(jié)果
            median = (arrays[length / 2] + arrays[length / 2 - 1]) / 2.0D;
        } else {
            // 如果數(shù)組長(zhǎng)度是奇數(shù)鞭缭,就找出這條中線對(duì)應(yīng)的元素剖膳,該元素就是結(jié)果
            median = arrays[length / 2];
        }
        return median;
    }

}

Kotlin

/**
 * Created by TanJiaJun on 2021/6/13.
 * 4. 尋找兩個(gè)正序數(shù)組的中位數(shù)(Median of Two Sorted Arrays)
 * 難度:困難
 *
 * @see <a >Median of Two Sorted Arrays</a>
 */
object MedianOfTwoSortedArraysKotlin {

    @JvmStatic
    fun main(args: Array<String>) {
        // 示例一
        println("示例一:")

        val firstNumbers = intArrayOf(1, 3)
        val secondNumbers = intArrayOf(2)
        println(findMedianSortedArrays(firstNumbers, secondNumbers))

        print("\n")

        // 示例二
        println("示例二:")

        val thirdNumbers = intArrayOf(1, 2)
        val fourthNumbers = intArrayOf(3, 4)
        println(findMedianSortedArrays(thirdNumbers, fourthNumbers))

        print("\n")

        // 示例三
        println("示例三:")

        val fifthNumbers = intArrayOf(0, 0)
        val sixthNumbers = intArrayOf(0, 0)
        println(findMedianSortedArrays(fifthNumbers, sixthNumbers))

        print("\n")

        // 示例四
        println("示例四:")

        val seventhNumbers = intArrayOf()
        val eightNumbers = intArrayOf(1)
        println(findMedianSortedArrays(seventhNumbers, eightNumbers))

        print("\n")

        // 示例五
        println("示例五:")

        val ninthNumbers = intArrayOf(2)
        val tenthNumbers = intArrayOf()
        println(findMedianSortedArrays(ninthNumbers, tenthNumbers))
    }

    /**
     * 雙指針
     * 時(shí)間復(fù)雜度:O(m+n),其中m是數(shù)組num1的長(zhǎng)度岭辣,n是數(shù)組num2的長(zhǎng)度
     * 空間復(fù)雜度:O(m+n)潮秘,其中m是數(shù)組num1的長(zhǎng)度,n是數(shù)組num2的長(zhǎng)度
     *
     * @param nums1 第一個(gè)數(shù)組
     * @param nums2 第二個(gè)數(shù)組
     * @return 結(jié)果
     */
    private fun findMedianSortedArrays(nums1: IntArray, nums2: IntArray): Double {
        val size1 = nums1.size
        val size2 = nums2.size
        // 合并后的數(shù)組的索引
        var index = 0
        // 第一個(gè)數(shù)組的指針易结,下面用i指針描述
        var i = 0
        // 第二個(gè)數(shù)組的指針枕荞,下面用j指針描述
        var j = 0
        // 合并兩個(gè)數(shù)組
        // 創(chuàng)建一個(gè)大小是兩個(gè)數(shù)組長(zhǎng)度之和的數(shù)組
        val arrays = IntArray(size1 + size2)
        while (i < size1 || j < size2) {
            when {
                // 如果第一個(gè)數(shù)組遍歷完畢后,就繼續(xù)遍歷第二個(gè)數(shù)組
                i == size1 -> arrays[index++] = nums2[j++]
                // 如果第二個(gè)數(shù)組遍歷完畢后搞动,就繼續(xù)遍歷第一個(gè)數(shù)組
                j == size2 -> arrays[index++] = nums1[i++]
                // 如果第一個(gè)數(shù)組的元素小于第二個(gè)數(shù)組的元素躏精,就將第一個(gè)數(shù)組中的該元素添加到合并后的新數(shù)組,同時(shí)將i指針向右移動(dòng)一格
                nums1[i] < nums2[j] -> arrays[index++] = nums1[i++]
                // 如果第一個(gè)數(shù)組的元素大于第二個(gè)數(shù)組的元素鹦肿,就將第二個(gè)數(shù)組中的該元素添加到合并后的新數(shù)組矗烛,同時(shí)將j指針向右移動(dòng)一格
                else -> arrays[index++] = nums2[j++]
            }
        }
        // 找出數(shù)組的中位數(shù)
        val size = arrays.size
        return if (size % 2.0 == 0.0) {
            // 如果數(shù)組長(zhǎng)度是偶數(shù),就找出這條中線旁邊的兩個(gè)元素箩溃,然后相加之后除以2得到結(jié)果
            (arrays[size / 2] + arrays[size / 2 - 1]) / 2.0
        } else {
            // 如果數(shù)組長(zhǎng)度是奇數(shù)瞭吃,就找出這條中線對(duì)應(yīng)的元素,該元素就是結(jié)果
            arrays[size / 2].toDouble()
        }
    }
}

時(shí)間復(fù)雜度:O(m+n)涣旨,其中m是數(shù)組num1的長(zhǎng)度歪架,n是數(shù)組num2的長(zhǎng)度。

空間復(fù)雜度:O(m+n)霹陡,其中m是數(shù)組num1的長(zhǎng)度和蚪,n是數(shù)組num2的長(zhǎng)度。

題解

根據(jù)題目可知烹棉,這兩個(gè)數(shù)組都是正序(從小到大)的攒霹,所以我們可以先使用雙指針將這兩個(gè)數(shù)組合并成一個(gè)數(shù)組,注釋寫(xiě)得挺詳細(xì)了浆洗,這里就不在贅述了催束,合并后得到一個(gè)大小是兩個(gè)數(shù)組長(zhǎng)度之和的新數(shù)組,然后我們從這個(gè)數(shù)組找出中位數(shù)伏社,有兩種情況:

  • 如果數(shù)組長(zhǎng)度是偶數(shù)抠刺,就找出這條中線旁邊的兩個(gè)元素,然后相加之后除以2得到結(jié)果洛口。
  • 如果數(shù)組長(zhǎng)度是奇數(shù)矫付,就找出這條中線對(duì)應(yīng)的元素凯沪,該元素就是結(jié)果第焰。

最長(zhǎng)回文子串(Longest Palindromic Substring)

難度:中等

鏈接:Longest Palindromic Substring

代碼

Java

/**
 * Created by TanJiaJun on 2021/6/10.
 * 5. 最長(zhǎng)回文子串(Longest Palindromic Substring)
 * 難度:中等
 *
 * @see <a >Longest Palindromic Substring</a>
 */
class LongestPalindromicSubstring {

    public static void main(String[] args) {
        // 示例一
        System.out.print("示例一:");

        String firstStr = "babad";
        System.out.println(expandAroundCenterLongestPalindrome(firstStr));

        System.out.print("\n");

        // 示例二
        System.out.print("示例二:");

        String secondStr = "cbbd";
        System.out.println(expandAroundCenterLongestPalindrome(secondStr));

        System.out.print("\n");

        // 示例三
        System.out.print("示例三:");

        String thirdStr = "a";
        System.out.println(expandAroundCenterLongestPalindrome(thirdStr));

        System.out.print("\n");

        // 示例四
        System.out.print("示例四:");

        String fourthStr = "ac";
        System.out.println(expandAroundCenterLongestPalindrome(fourthStr));
    }

    /**
     * 方法一:枚舉算法
     * 時(shí)間復(fù)雜度:O(N^3),其中N是字符串的長(zhǎng)度
     * 空間復(fù)雜度:O(1)
     *
     * @param str 字符串
     * @return 結(jié)果
     */
    private static String longestPalindrome(String str) {
        int maxLength = 0;
        String result = "";
        // 枚舉所有的元素
        for (int i = 0, iLen = str.length(); i < iLen; i++) {
            for (int j = i + 1, jLen = str.length(); j <= jLen; j++) {
                String substring = str.substring(i, j);
                if (isPalindromeSubstring(substring) && substring.length() > maxLength) {
                    maxLength = substring.length();
                    result = substring;
                }
            }
        }
        return result;
    }

    /**
     * 判斷字符串是否為回文串
     *
     * @param str 字符串
     * @return 結(jié)果
     */
    private static boolean isPalindromeSubstring(String str) {
        int length = str.length();
        for (int i = 0; i < length / 2; i++) {
            // 找出該元素作為回文子串的起始位置還有結(jié)束位置
            if (str.charAt(i) != str.charAt(length - i - 1)) {
                // 如果其中一個(gè)不相同妨马,證明該字符串不是回文串
                return false;
            }
        }
        // 如果字符都相同挺举,證明該字符串是回文串
        return true;
    }

    /**
     * 方法二:中心擴(kuò)展法(雙指針)
     * 時(shí)間復(fù)雜度:O(N^2)杀赢,其中N是字符串的長(zhǎng)度
     * 空間復(fù)雜度:O(1)
     *
     * @param str 字符串
     * @return 結(jié)果
     */
    private static String expandAroundCenterLongestPalindrome(String str) {
        int start = 0;
        int end = 0;
        for (int i = 0, length = str.length(); i < length; i++) {
            // 長(zhǎng)度是奇數(shù)
            int oddLength = getExpandAroundCenterLength(str, i, i);
            // 長(zhǎng)度是偶數(shù)
            int evenLength = getExpandAroundCenterLength(str, i, i + 1);
            // 得到最大長(zhǎng)度
            int maxLength = Math.max(oddLength, evenLength);
            if (maxLength > end - start) {
                // 得到起始位置
                start = i - (maxLength - 1) / 2;
                // 得到結(jié)束位置
                end = i + maxLength / 2;
            }
        }
        // 截取對(duì)應(yīng)的字符
        return str.substring(start, end + 1);
    }

    /**
     * 得到中心往兩邊擴(kuò)展的長(zhǎng)度
     *
     * @param str   字符串
     * @param left  左指針
     * @param right 右指針
     * @return 長(zhǎng)度
     */
    private static int getExpandAroundCenterLength(String str, int left, int right) {
        // 找出該元素作為回文子串的起始位置還有結(jié)束位置
        while (left >= 0 && right < str.length() && str.charAt(left) == str.charAt(right)) {
            // 如果符合條件,左指針向左移動(dòng)一格湘纵,右指針向右移動(dòng)一格
            left--;
            right++;
        }
        // 得到長(zhǎng)度
        return right - left - 1;
    }
}

Kotlin

import kotlin.math.max

/**
 * Created by TanJiaJun on 2021/6/14.
 * 5. 最長(zhǎng)回文子串(Longest Palindromic Substring)
 * 難度:中等
 *
 * @see <a >Longest Palindromic Substring</a>
 */
object LongestPalindromicSubstringKotlin {

    @JvmStatic
    fun main(args: Array<String>) {
        // 示例一
        print("示例一:")

        val firstStr = "babad"
        println(expandAroundCenterLongestPalindrome(firstStr))

        print("\n")

        // 示例二
        print("示例二:")

        val secondStr = "cbbd"
        println(expandAroundCenterLongestPalindrome(secondStr))

        print("\n")

        // 示例三
        print("示例三:")

        val thirdStr = "a"
        println(expandAroundCenterLongestPalindrome(thirdStr))

        print("\n")

        // 示例四
        print("示例四:")

        val fourthStr = "ac"
        println(expandAroundCenterLongestPalindrome(fourthStr))
    }

    /**
     * 方法一:枚舉算法
     * 時(shí)間復(fù)雜度:O(N^3)脂崔,其中N是字符串的長(zhǎng)度
     * 空間復(fù)雜度:O(1)
     *
     * @param str 字符串
     * @return 結(jié)果
     */
    private fun longestPalindrome(str: String): String {
        var maxLength = 0
        var result = ""
        // 枚舉所有的元素
        str.forEachIndexed { index, _ ->
            for (i in index + 1..str.length) {
                val substring = str.substring(index, i)
                if (isPalindromicSubstring(substring) && substring.length > maxLength) {
                    maxLength = substring.length
                    result = substring
                }
            }
        }
        return result
    }

    /**
     * 判斷字符串是否為回文串
     *
     * @param str 字符串
     * @return 結(jié)果
     */
    private fun isPalindromicSubstring(str: String): Boolean {
        val length = str.length
        for (i in 0 until length / 2) {
            // 找出該元素作為回文子串的起始位置還有結(jié)束位置
            if (str[i] != str[length - i - 1]) {
                // 如果其中一個(gè)不相同,證明該字符串不是回文串
                return false
            }
        }
        // 如果字符都相同梧喷,證明該字符串是回文串
        return true
    }

    /**
     * 方法二:中心擴(kuò)展法(雙指針)
     * 時(shí)間復(fù)雜度:O(N^2)砌左,其中N是字符串的長(zhǎng)度
     * 空間復(fù)雜度:O(1)
     *
     * @param str 字符串
     * @return 結(jié)果
     */
    private fun expandAroundCenterLongestPalindrome(str: String): String {
        var start = 0
        var end = 0
        str.forEachIndexed { index, _ ->
            // 長(zhǎng)度是奇數(shù)
            val oddLength = getExpandAroundCenterLength(str = str, left = index, right = index)
            // 長(zhǎng)度是偶數(shù)
            val evenLength = getExpandAroundCenterLength(str = str, left = index, right = index + 1)
            // 得到最大長(zhǎng)度
            val maxLength = max(oddLength, evenLength)
            if (maxLength > end - start) {
                // 得到起始位置
                start = index - (maxLength - 1) / 2
                // 得到結(jié)束位置
                end = index + maxLength / 2
            }
        }
        return str.substring(start, end + 1)
    }

    /**
     * 得到中心往兩邊擴(kuò)展的長(zhǎng)度
     *
     * @param str   字符串
     * @param left  左指針
     * @param right 右指針
     * @return 長(zhǎng)度
     */
    private fun getExpandAroundCenterLength(str: String, left: Int, right: Int): Int {
        var l = left
        var r = right
        // 找出該元素作為回文子串的起始位置還有結(jié)束位置
        while ((l >= 0 && r < str.length && str[l] == str[r])) {
            // 如果符合條件,左指針向左移動(dòng)一格铺敌,右指針向右移動(dòng)一格
            l--
            r++
        }
        // 得到長(zhǎng)度
        return r - l - 1
    }

}

題解

枚舉

// LongestPalindromicSubstringKotlin.kt
/**
 * 方法一:枚舉
 * 時(shí)間復(fù)雜度:O(N^3)汇歹,其中N是字符串的長(zhǎng)度
 * 空間復(fù)雜度:O(1)
 *
 * @param str 字符串
 * @return 結(jié)果
 */
private fun longestPalindrome(str: String): String {
    var maxLength = 0
    var result = ""
    // 枚舉所有的元素
    str.forEachIndexed { index, _ ->
        for (i in index + 1..str.length) {
            val substring = str.substring(index, i)
            if (isPalindromicSubstring(substring) && substring.length > maxLength) {
                maxLength = substring.length
                result = substring
            }
        }
    }
    return result
}

/**
 * 判斷字符串是否為回文串
 *
 * @param str 字符串
 * @return 結(jié)果
 */
private fun isPalindromicSubstring(str: String): Boolean {
    val length = str.length
    for (i in 0 until length / 2) {
        // 找出該元素作為回文子串的起始位置還有結(jié)束位置
        if (str[i] != str[length - i - 1]) {
            // 如果其中一個(gè)不相同,證明該字符串不是回文串
            return false
        }
    }
    // 如果字符都相同偿凭,證明該字符串是回文串
    return true
}

時(shí)間復(fù)雜度:O(N^3)产弹,其中N是字符串的長(zhǎng)度。

空間復(fù)雜度:O(1)弯囊。

暴力解法痰哨,枚舉所有元素,要注意的是匾嘱,由于回文串的特征是正讀和反讀都一樣斤斧,例如:abba就是回文串abda就不是回文串了霎烙,所以我們只要找到某個(gè)字符折欠,并且找到該字符對(duì)應(yīng)索引的字符,只需要遍歷該數(shù)組長(zhǎng)度一半就可以判斷該字符串是否為回文串吼过,注釋寫(xiě)得挺詳細(xì)了锐秦,這里就不再贅述了。

中心擴(kuò)展法(雙指針)

// LongestPalindromicSubstringKotlin.kt
/**
 * 方法二:中心擴(kuò)展法(雙指針)
 * 時(shí)間復(fù)雜度:O(N^2)盗忱,其中N是字符串的長(zhǎng)度
 * 空間復(fù)雜度:O(1)
 *
 * @param str 字符串
 * @return 結(jié)果
 */
private fun expandAroundCenterLongestPalindrome(str: String): String {
    var start = 0
    var end = 0
    str.forEachIndexed { index, _ ->
        // 長(zhǎng)度是奇數(shù)
        val oddLength = getExpandAroundCenterLength(str = str, left = index, right = index)
        // 長(zhǎng)度是偶數(shù)
        val evenLength = getExpandAroundCenterLength(str = str, left = index, right = index + 1)
        // 得到最大長(zhǎng)度
        val maxLength = max(oddLength, evenLength)
        if (maxLength > end - start) {
            // 得到起始位置
            start = index - (maxLength - 1) / 2
            // 得到結(jié)束位置
            end = index + maxLength / 2
        }
    }
    return str.substring(start, end + 1)
}

/**
 * 得到中心往兩邊擴(kuò)展的長(zhǎng)度
 *
 * @param str   字符串
 * @param left  左指針
 * @param right 右指針
 * @return 長(zhǎng)度
 */
private fun getExpandAroundCenterLength(str: String, left: Int, right: Int): Int {
    var l = left
    var r = right
    // 找出該元素作為回文子串的起始位置還有結(jié)束位置
    while ((l >= 0 && r < str.length && str[l] == str[r])) {
        // 如果符合條件酱床,左指針向左移動(dòng)一格,右指針向右移動(dòng)一格
        l--
        r++
    }
    // 得到長(zhǎng)度
    return r - l - 1
}

時(shí)間復(fù)雜度:O(N^2)趟佃,其中N是字符串的長(zhǎng)度扇谣。

空間復(fù)雜度:O(1)。

由于回文串的特征是正讀和反讀都一樣闲昭,例如:abba就是回文串罐寨,abda就不是回文串了,所以我們可以從該字符串的中間向兩邊擴(kuò)展地遍歷序矩,這樣就能快速地得到最長(zhǎng)的回文子串鸯绿,要注意的是,因?yàn)閿?shù)組的長(zhǎng)度可能是奇數(shù),也可能是偶數(shù)瓶蝴,如果是奇數(shù)的話毒返,中心點(diǎn)就只有一個(gè);如果是偶數(shù)的話舷手,中心點(diǎn)就有兩個(gè)拧簸。

Z字形變換(ZigZag Conversion)

難度:中等

鏈接:ZigZag Conversion

代碼

Java

/**
 * Created by TanJiaJun on 2021/6/12.
 * 6. Z字形變換(ZigZag Conversion)
 * 難度:中等
 *
 * @see <a >ZigZag Conversion</a>
 */
class ZigZagConversion {

    public static void main(String[] args) {
        // 示例一
        System.out.print("示例一:");

        String firstStr = "PAYPALISHIRING";
        int firstNumRows = 3;
        System.out.println(convert(firstStr, firstNumRows));

        System.out.print("\n");

        // 示例二
        System.out.print("示例二:");

        String secondStr = "PAYPALISHIRING";
        int secondNumRows = 4;
        System.out.println(convert(secondStr, secondNumRows));

        System.out.print("\n");

        // 示例三
        System.out.print("示例三:");

        String thirdStr = "A";
        int thirdNumRows = 1;
        System.out.println(convert(thirdStr, thirdNumRows));
    }

    /**
     * 時(shí)間復(fù)雜度:O(N),其中N是字符串的長(zhǎng)度
     * 空間復(fù)雜度:O(N)
     *
     * @param str     字符串
     * @param numRows 行數(shù)
     * @return 結(jié)果
     */
    private static String convert(String str, int numRows) {
        if (numRows == 1) {
            // 如果只是一行的話男窟,就直接返回該字符串
            return str;
        }
        // 創(chuàng)建長(zhǎng)度是行數(shù)的StringBuilder數(shù)組盆赤,并且每個(gè)元素都創(chuàng)建StringBuilder對(duì)象
        StringBuilder[] stringBuilders = new StringBuilder[numRows];
        for (int i = 0; i < numRows; i++) {
            stringBuilders[i] = new StringBuilder();
        }
        // 索引
        int index = 0;
        // 行數(shù)
        int row = 0;
        int length = str.length();
        while (index < length) {
            // 從第一行開(kāi)始,按照行數(shù)添加到對(duì)應(yīng)的數(shù)組歉眷,并且追加字符弟劲,然后一直遍歷到最后一行對(duì)應(yīng)的數(shù)組
            while (index < length && row < numRows) {
                char ch = str.charAt(index++);
                stringBuilders[row++].append(ch);
            }

            // 此時(shí)row是最后一行,所以我們需要回到倒數(shù)第二行姥芥,執(zhí)行以下邏輯
            row = numRows - 2;

            // 從倒數(shù)第二行開(kāi)始兔乞,按照行數(shù)添加到對(duì)應(yīng)的數(shù)組,并且追加字符凉唐,然后一直遍歷到第一行的對(duì)應(yīng)的數(shù)組
            while (index < length && row >= 0) {
                char ch = str.charAt(index++);
                stringBuilders[row--].append(ch);
            }

            // 此時(shí)row是-1庸追,所以我們需要回到第二行,執(zhí)行以下邏輯
            row += 2;
        }
        // 創(chuàng)建一個(gè)新的StringBuilder台囱,將每一行對(duì)應(yīng)的StringBuilder數(shù)組對(duì)應(yīng)的StringBuilder追加到這個(gè)對(duì)象
        StringBuilder resultSB = new StringBuilder();
        for (StringBuilder stringBuilder : stringBuilders) {
            resultSB.append(stringBuilder);
        }
        // 將這個(gè)StringBuilder轉(zhuǎn)成字符串
        return resultSB.toString();
    }

}

Kotlin

/**
 * Created by TanJiaJun on 2021/6/14.
 * 6. Z字形變換(ZigZag Conversion)
 * 難度:中等
 *
 * @see <a >ZigZag Conversion</a>
 */
object ZigZagConversionKotlin {

    @JvmStatic
    fun main(args: Array<String>) {
        // 示例一
        print("示例一:")

        val firstStr = "PAYPALISHIRING"
        val firstNumRows = 3
        println(convert(firstStr, firstNumRows))

        print("\n")

        // 示例二
        print("示例二:")

        val secondStr = "PAYPALISHIRING"
        val secondNumRows = 4
        println(convert(secondStr, secondNumRows))

        print("\n")

        // 示例三
        print("示例三:")

        val thirdStr = "A"
        val thirdNumRows = 1
        println(convert(thirdStr, thirdNumRows))
    }

    /**
     * 時(shí)間復(fù)雜度:O(N)淡溯,其中N是字符串的長(zhǎng)度
     * 空間復(fù)雜度:O(N)
     *
     * @param str     字符串
     * @param numRows 行數(shù)
     * @return 結(jié)果
     */
    private fun convert(str: String, numRows: Int): String {
        if (numRows == 1) {
            // 如果只是一行的話,就直接返回該字符串
            return str
        }
        // 創(chuàng)建長(zhǎng)度是行數(shù)的StringBuilder數(shù)組簿训,并且每個(gè)元素都創(chuàng)建StringBuilder對(duì)象
        val stringBuilders = Array(numRows, init = { StringBuilder() })
        // 索引
        var index = 0
        // 行數(shù)
        var row = 0
        val length = str.length
        while (index < length) {
            // 從第一行開(kāi)始咱娶,按照行數(shù)添加到對(duì)應(yīng)的數(shù)組,并且追加字符强品,然后一直遍歷到最后一行對(duì)應(yīng)的數(shù)組
            while (index < length && row < numRows) {
                stringBuilders[row++].append(str[index++])
            }

            // 此時(shí)row是最后一行膘侮,所以我們需要回到倒數(shù)第二行,執(zhí)行以下邏輯
            row = numRows - 2

            // 從倒數(shù)第二行開(kāi)始的榛,按照行數(shù)添加到對(duì)應(yīng)的數(shù)組琼了,并且追加字符,然后一直遍歷到第一行的對(duì)應(yīng)的數(shù)組
            while (index < length && row >= 0) {
                stringBuilders[row--].append(str[index++])
            }

            // 此時(shí)row是-1夫晌,所以我們需要回到第二行雕薪,執(zhí)行以下邏輯
            row += 2
        }
        // 創(chuàng)建一個(gè)新的StringBuilder,將每一行對(duì)應(yīng)的StringBuilder數(shù)組對(duì)應(yīng)的StringBuilder追加到這個(gè)對(duì)象
        val resultSB = StringBuilder()
        stringBuilders.forEach { resultSB.append(it) }
        // 將這個(gè)StringBuilder轉(zhuǎn)成字符串
        return resultSB.toString()
    }

}

時(shí)間復(fù)雜度:O(N)晓淀,其中N是字符串的長(zhǎng)度所袁。

空間復(fù)雜度:O(N)。

題解

根據(jù)題目我們可以知道凶掰,字符串是按著反向N字形排列的燥爷,我們可以先創(chuàng)建一個(gè)長(zhǎng)度是行數(shù)StringBuilder數(shù)組蜈亩,然后定義一個(gè)row這樣的指針來(lái)確定行數(shù),為了方便理解局劲,我舉個(gè)例子勺拣,假設(shè)字符串PAYPALISHIRING奶赠,行數(shù)是3鱼填,我們從第一行開(kāi)始,按照行數(shù)將字符添加到對(duì)應(yīng)的數(shù)組毅戈,并且追加字符苹丸,然后一直遍歷到最后一行對(duì)應(yīng)的數(shù)組,此時(shí)這三個(gè)數(shù)組的情況如下所示:

  • 第一個(gè)數(shù)組:P
  • 第二個(gè)數(shù)組:A
  • 第三個(gè)數(shù)組:Y

此時(shí)的row的值是4苇经,然后這個(gè)時(shí)候根據(jù)題目要求赘理,我們的指針要回到倒數(shù)第二行,所以我們就要將row的值調(diào)整為行數(shù)減去2扇单,也就是3-2等于1商模,然后開(kāi)始繼續(xù)遍歷,此時(shí)這三個(gè)數(shù)組的情況如下所示:

  • 第一個(gè)數(shù)組:P
  • 第二個(gè)數(shù)組:A蜘澜、P
  • 第三個(gè)數(shù)組:Y

然后我們的指針要從倒數(shù)第二行開(kāi)始施流,按照行數(shù)添加到對(duì)應(yīng)的數(shù)組,并且追加字符鄙信,然后一直遍歷到第一行的對(duì)應(yīng)的數(shù)組瞪醋,此時(shí)這三個(gè)數(shù)組的情況如下所示:

  • 第一個(gè)數(shù)組:P、A
  • 第二個(gè)數(shù)組:A装诡、P银受、L
  • 第三個(gè)數(shù)組:Y、I

此時(shí)row的值是-1鸦采,最后根據(jù)題目要求宾巍,我們的指針row要回到第二行,所以我們就要將row的值調(diào)整為自增2渔伯,也就是-1+2等于1蜀漆,然后開(kāi)始繼續(xù)遍歷,此時(shí)這三個(gè)數(shù)組的情況如下所示:

  • 第一個(gè)數(shù)組:P咱旱、A
  • 第二個(gè)數(shù)組:A确丢、P、L吐限、S
  • 第三個(gè)數(shù)組:Y鲜侥、I

以此類(lèi)推遍歷下去,后面的邏輯就跟前面的邏輯一樣了诸典,這里就不再贅述了描函。

最后將這個(gè)StringBuilder數(shù)組遍歷后追加到一個(gè)新的StringBuilder對(duì)象,然后轉(zhuǎn)成字符串就可以得到最后的結(jié)果了。

我的GitHub:TanJiaJunBeyond

Android通用框架:Android通用框架

我的掘金:譚嘉俊

我的簡(jiǎn)書(shū):譚嘉俊

我的CSDN:譚嘉俊

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末舀寓,一起剝皮案震驚了整個(gè)濱河市胆数,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌互墓,老刑警劉巖必尼,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異篡撵,居然都是意外死亡判莉,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)育谬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)券盅,“玉大人,你說(shuō)我怎么就攤上這事膛檀∶潭疲” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵咖刃,是天一觀的道長(zhǎng)泳炉。 經(jīng)常有香客問(wèn)我,道長(zhǎng)僵缺,這世上最難降的妖魔是什么胡桃? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮磕潮,結(jié)果婚禮上翠胰,老公的妹妹穿的比我還像新娘。我一直安慰自己自脯,他們只是感情好之景,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著膏潮,像睡著了一般锻狗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上焕参,一...
    開(kāi)封第一講書(shū)人閱讀 48,970評(píng)論 1 284
  • 那天轻纪,我揣著相機(jī)與錄音,去河邊找鬼叠纷。 笑死刻帚,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的涩嚣。 我是一名探鬼主播崇众,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼掂僵,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了顷歌?” 一聲冷哼從身側(cè)響起锰蓬,我...
    開(kāi)封第一講書(shū)人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎眯漩,沒(méi)想到半個(gè)月后芹扭,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡坤塞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年冯勉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了澈蚌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片摹芙。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖宛瞄,靈堂內(nèi)的尸體忽然破棺而出浮禾,到底是詐尸還是另有隱情,我是刑警寧澤份汗,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布盈电,位于F島的核電站,受9級(jí)特大地震影響杯活,放射性物質(zhì)發(fā)生泄漏匆帚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一旁钧、第九天 我趴在偏房一處隱蔽的房頂上張望吸重。 院中可真熱鬧,春花似錦歪今、人聲如沸嚎幸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)嫉晶。三九已至,卻和暖如春田篇,著一層夾襖步出監(jiān)牢的瞬間替废,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工泊柬, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留椎镣,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓彬呻,卻偏偏與公主長(zhǎng)得像衣陶,于是被迫代替她去往敵國(guó)和親柄瑰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 本系列通過(guò)Java和Kotlin這兩種語(yǔ)言來(lái)解決力扣上面的算法題剪况,由于本人算法菜鳥(niǎo)一枚教沾,可能部分題目并不是最優(yōu)題解...
    譚嘉俊閱讀 274評(píng)論 0 1
  • 目錄 1 左神部分集錦 2 Leetcode前150題 3 牛客網(wǎng)劍指offer 4 JavaG 5 題目中的...
    小小千千閱讀 972評(píng)論 0 0
  • 《算法練習(xí)-文章匯總》[http://www.reibang.com/p/fc7c0e8cc5cb] Day1:...
    一畝三分甜閱讀 615評(píng)論 0 0
  • 表情是什么译断,我認(rèn)為表情就是表現(xiàn)出來(lái)的情緒授翻。表情可以傳達(dá)很多信息。高興了當(dāng)然就笑了孙咪,難過(guò)就哭了堪唐。兩者是相互影響密不可...
    Persistenc_6aea閱讀 124,158評(píng)論 2 7
  • 16宿命:用概率思維提高你的勝算 以前的我是風(fēng)險(xiǎn)厭惡者,不喜歡去冒險(xiǎn)翎蹈,但是人生放棄了冒險(xiǎn)淮菠,也就放棄了無(wú)數(shù)的可能。 ...
    yichen大刀閱讀 6,033評(píng)論 0 4