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

本系列通過JavaKotlin這兩種語言來解決力扣上面的算法題,由于本人算法菜鳥一枚,可能部分題目并不是最優(yōu)題解,希望能和各位大神共同討論~

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

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

項目的GitHub:Algorithm

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

難度:困難

鏈接:Median of Two Sorted Arrays

代碼

Java

/**
 * Created by TanJiaJun on 2021/6/9.
 * 4. 尋找兩個正序數(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));
    }

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

}

Kotlin

/**
 * Created by TanJiaJun on 2021/6/13.
 * 4. 尋找兩個正序數(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))
    }

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

時間復(fù)雜度:O(m+n),其中m是數(shù)組num1的長度,n是數(shù)組num2的長度。

空間復(fù)雜度:O(m+n),其中m是數(shù)組num1的長度,n是數(shù)組num2的長度。

題解

根據(jù)題目可知,這兩個數(shù)組都是正序(從小到大)的,所以我們可以先使用雙指針將這兩個數(shù)組合并成一個數(shù)組,注釋寫得挺詳細了,這里就不在贅述了,合并后得到一個大小是兩個數(shù)組長度之和的新數(shù)組,然后我們從這個數(shù)組找出中位數(shù),有兩種情況:

  • 如果數(shù)組長度是偶數(shù),就找出這條中線旁邊的兩個元素,然后相加之后除以2得到結(jié)果。
  • 如果數(shù)組長度是奇數(shù),就找出這條中線對應(yīng)的元素,該元素就是結(jié)果。

最長回文子串(Longest Palindromic Substring)

難度:中等

鏈接:Longest Palindromic Substring

代碼

Java

/**
 * Created by TanJiaJun on 2021/6/10.
 * 5. 最長回文子串(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));
    }

    /**
     * 方法一:枚舉算法
     * 時間復(fù)雜度:O(N^3),其中N是字符串的長度
     * 空間復(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)) {
                // 如果其中一個不相同,證明該字符串不是回文串
                return false;
            }
        }
        // 如果字符都相同,證明該字符串是回文串
        return true;
    }

    /**
     * 方法二:中心擴展法(雙指針)
     * 時間復(fù)雜度:O(N^2),其中N是字符串的長度
     * 空間復(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++) {
            // 長度是奇數(shù)
            int oddLength = getExpandAroundCenterLength(str, i, i);
            // 長度是偶數(shù)
            int evenLength = getExpandAroundCenterLength(str, i, i + 1);
            // 得到最大長度
            int maxLength = Math.max(oddLength, evenLength);
            if (maxLength > end - start) {
                // 得到起始位置
                start = i - (maxLength - 1) / 2;
                // 得到結(jié)束位置
                end = i + maxLength / 2;
            }
        }
        // 截取對應(yīng)的字符
        return str.substring(start, end + 1);
    }

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

Kotlin

import kotlin.math.max

/**
 * Created by TanJiaJun on 2021/6/14.
 * 5. 最長回文子串(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))
    }

    /**
     * 方法一:枚舉算法
     * 時間復(fù)雜度:O(N^3),其中N是字符串的長度
     * 空間復(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]) {
                // 如果其中一個不相同,證明該字符串不是回文串
                return false
            }
        }
        // 如果字符都相同,證明該字符串是回文串
        return true
    }

    /**
     * 方法二:中心擴展法(雙指針)
     * 時間復(fù)雜度:O(N^2),其中N是字符串的長度
     * 空間復(fù)雜度:O(1)
     *
     * @param str 字符串
     * @return 結(jié)果
     */
    private fun expandAroundCenterLongestPalindrome(str: String): String {
        var start = 0
        var end = 0
        str.forEachIndexed { index, _ ->
            // 長度是奇數(shù)
            val oddLength = getExpandAroundCenterLength(str = str, left = index, right = index)
            // 長度是偶數(shù)
            val evenLength = getExpandAroundCenterLength(str = str, left = index, right = index + 1)
            // 得到最大長度
            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)
    }

    /**
     * 得到中心往兩邊擴展的長度
     *
     * @param str   字符串
     * @param left  左指針
     * @param right 右指針
     * @return 長度
     */
    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])) {
            // 如果符合條件,左指針向左移動一格,右指針向右移動一格
            l--
            r++
        }
        // 得到長度
        return r - l - 1
    }

}

題解

枚舉

// LongestPalindromicSubstringKotlin.kt
/**
 * 方法一:枚舉
 * 時間復(fù)雜度:O(N^3),其中N是字符串的長度
 * 空間復(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]) {
            // 如果其中一個不相同,證明該字符串不是回文串
            return false
        }
    }
    // 如果字符都相同,證明該字符串是回文串
    return true
}

時間復(fù)雜度:O(N^3),其中N是字符串的長度。

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

暴力解法,枚舉所有元素,要注意的是,由于回文串的特征是正讀和反讀都一樣,例如:abba就是回文串,abda就不是回文串了,所以我們只要找到某個字符,并且找到該字符對應(yīng)索引的字符,只需要遍歷該數(shù)組長度一半就可以判斷該字符串是否為回文串,注釋寫得挺詳細了,這里就不再贅述了。

中心擴展法(雙指針)

// LongestPalindromicSubstringKotlin.kt
/**
 * 方法二:中心擴展法(雙指針)
 * 時間復(fù)雜度:O(N^2),其中N是字符串的長度
 * 空間復(fù)雜度:O(1)
 *
 * @param str 字符串
 * @return 結(jié)果
 */
private fun expandAroundCenterLongestPalindrome(str: String): String {
    var start = 0
    var end = 0
    str.forEachIndexed { index, _ ->
        // 長度是奇數(shù)
        val oddLength = getExpandAroundCenterLength(str = str, left = index, right = index)
        // 長度是偶數(shù)
        val evenLength = getExpandAroundCenterLength(str = str, left = index, right = index + 1)
        // 得到最大長度
        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)
}

/**
 * 得到中心往兩邊擴展的長度
 *
 * @param str   字符串
 * @param left  左指針
 * @param right 右指針
 * @return 長度
 */
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])) {
        // 如果符合條件,左指針向左移動一格,右指針向右移動一格
        l--
        r++
    }
    // 得到長度
    return r - l - 1
}

時間復(fù)雜度:O(N^2),其中N是字符串的長度。

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

由于回文串的特征是正讀和反讀都一樣,例如:abba就是回文串abda就不是回文串了,所以我們可以從該字符串的中間向兩邊擴展地遍歷,這樣就能快速地得到最長的回文子串,要注意的是,因為數(shù)組的長度可能是奇數(shù),也可能是偶數(shù),如果是奇數(shù)的話,中心點就只有一個;如果是偶數(shù)的話,中心點就有兩個

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));
    }

    /**
     * 時間復(fù)雜度:O(N),其中N是字符串的長度
     * 空間復(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)建長度是行數(shù)的StringBuilder數(shù)組,并且每個元素都創(chuàng)建StringBuilder對象
        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) {
            // 從第一行開始,按照行數(shù)添加到對應(yīng)的數(shù)組,并且追加字符,然后一直遍歷到最后一行對應(yīng)的數(shù)組
            while (index < length && row < numRows) {
                char ch = str.charAt(index++);
                stringBuilders[row++].append(ch);
            }

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

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

            // 此時row是-1,所以我們需要回到第二行,執(zhí)行以下邏輯
            row += 2;
        }
        // 創(chuàng)建一個新的StringBuilder,將每一行對應(yīng)的StringBuilder數(shù)組對應(yīng)的StringBuilder追加到這個對象
        StringBuilder resultSB = new StringBuilder();
        for (StringBuilder stringBuilder : stringBuilders) {
            resultSB.append(stringBuilder);
        }
        // 將這個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))
    }

    /**
     * 時間復(fù)雜度:O(N),其中N是字符串的長度
     * 空間復(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)建長度是行數(shù)的StringBuilder數(shù)組,并且每個元素都創(chuàng)建StringBuilder對象
        val stringBuilders = Array(numRows, init = { StringBuilder() })
        // 索引
        var index = 0
        // 行數(shù)
        var row = 0
        val length = str.length
        while (index < length) {
            // 從第一行開始,按照行數(shù)添加到對應(yīng)的數(shù)組,并且追加字符,然后一直遍歷到最后一行對應(yīng)的數(shù)組
            while (index < length && row < numRows) {
                stringBuilders[row++].append(str[index++])
            }

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

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

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

}

時間復(fù)雜度:O(N),其中N是字符串的長度。

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

題解

根據(jù)題目我們可以知道,字符串是按著反向N字形排列的,我們可以先創(chuàng)建一個長度是行數(shù)StringBuilder數(shù)組,然后定義一個row這樣的指針來確定行數(shù),為了方便理解,我舉個例子,假設(shè)字符串PAYPALISHIRING,行數(shù)是3,我們從第一行開始,按照行數(shù)將字符添加到對應(yīng)的數(shù)組,并且追加字符,然后一直遍歷到最后一行對應(yīng)的數(shù)組,此時這三個數(shù)組的情況如下所示:

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

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

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

然后我們的指針要從倒數(shù)第二行開始,按照行數(shù)添加到對應(yīng)的數(shù)組,并且追加字符,然后一直遍歷到第一行的對應(yīng)的數(shù)組,此時這三個數(shù)組的情況如下所示:

  • 第一個數(shù)組:P、A
  • 第二個數(shù)組:A、P、L
  • 第三個數(shù)組:Y、I

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

  • 第一個數(shù)組:P、A
  • 第二個數(shù)組:A、P、L、S
  • 第三個數(shù)組:Y、I

以此類推遍歷下去,后面的邏輯就跟前面的邏輯一樣了,這里就不再贅述了。

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

我的GitHub:TanJiaJunBeyond

Android通用框架:Android通用框架

我的掘金:譚嘉俊

我的簡書:譚嘉俊

我的CSDN:譚嘉俊

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本系列通過Java和Kotlin這兩種語言來解決力扣上面的算法題,由于本人算法菜鳥一枚,可能部分題目并不是最優(yōu)題解...
    譚嘉俊閱讀 339評論 0 1
  • 目錄 1 左神部分集錦 2 Leetcode前150題 3 牛客網(wǎng)劍指offer 4 JavaG 5 題目中的...
    小小千千閱讀 1,376評論 0 0
  • 《算法練習-文章匯總》[http://m.itdecent.cn/p/fc7c0e8cc5cb] Day1:...
    一畝三分甜閱讀 816評論 0 0
  • 表情是什么,我認為表情就是表現(xiàn)出來的情緒。表情可以傳達很多信息。高興了當然就笑了,難過就哭了。兩者是相互影響密不可...
    Persistenc_6aea閱讀 129,919評論 2 7
  • 16宿命:用概率思維提高你的勝算 以前的我是風險厭惡者,不喜歡去冒險,但是人生放棄了冒險,也就放棄了無數(shù)的可能。 ...
    yichen大刀閱讀 8,185評論 0 4

友情鏈接更多精彩內(nèi)容