学好算法很重要,然后要学好算法,大量的练习是必不可少的,LeetCode是我经常去的一个刷题网站,上面的题目非常详细,各个标签的题目都有,可以整体练习,本公众号后续会带大家做一做上面的算法题。

官方链接:https://leetcode-cn.com/problemset/all/


一、题意

难度:中等

https://leetcode-cn.com/problems/zigzag-conversion/

将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。

比如输入字符串为 "LEETCODEISHIRING" 行数为 3 时,排列如下:

L   C   I   R
E T O E S I I G
E   D   H   N

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"LCIRETOESIIGEDHN"

请你实现这个将字符串进行指定行数变换的函数:

string convert(string s, int numRows);

示例

输入: s = "LEETCODEISHIRING", numRows = 3
输出: "LCIRETOESIIGEDHN"

输入: s = "LEETCODEISHIRING", numRows = 4
输出: "LDREOEIIECIHNTSG"
解释:
L     D     R
E   O E   I I
E C   I H   N
T     S     G


二、解题

方法一:按行排序

思路:
通过案例我们可以比较容易的发现该 Z 字形图案中有一个规律,就是当前字符在首行的时候,它都是向下延伸排列的
而当前字符在末行的时候,就会向上逐列移动,就像爬楼梯一样。
那么我们这里是不是可以用一个标记来做个判断呢?通过标记来控制这个方向,从而实现向上与向下两种不同的排列。

代码:

class Solution {
    public String convert(String s, int numRows) {
        // 特殊判断
        if(numRows == 1){
            return s;
        }
        // 创建一个集合保存每一行字符串
        List<StringBuilder> rows = new ArrayList<>();
        for(int i = 0; i < Math.min(numRows, s.length()); ++i){
            rows.add(new StringBuilder());
        }
        // 定义行索引,方向标记
        int rowIndex = 0;
        boolean flag = false;
        // 遍历原字符串
        for(char c : s.toCharArray()){
            // 按行拼接字符串
            rows.get(rowIndex).append(c);
            // 首行,末行判断
            if(rowIndex == 0 || rowIndex == numRows - 1) flag = !flag;
            // 方向控制
            rowIndex += flag ? 1 : -1;
        }
        // 结果拼接
        StringBuilder res = new StringBuilder();
        for(StringBuilder row : rows){
            res.append(row);
        }
        return res.toString();
    }
}

复杂度分析:

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)


方法二:按行访问

思路:
做完上面的解法,相信你对这个形状也有一定了解了。这时你应该会有一个想法,就是首行的字母每一个之间的距离都是 numRows * 2 - 2,是吧?那么,我们就可以利用这一点,不去转换形状,直接按照这个距离来进行字符串拼接即可。

代码:

class Solution {
    public String convert(String s, int numRows) {
        // 特殊判断
        if(numRows == 1){
            return s;
        }
        // 字符串拼接对象,字符串长度,距离
        StringBuilder res = new StringBuilder();
        int n = s.length();
        int max = 2 * numRows - 2;
        // 按行遍历
        for(int i = 0; i < numRows; ++i){
            for(int j = 0; j + i < n; j += max){
                // 拼接每一行的字符
                res.append(s.charAt(j + i));
                // 拼接非首行非末行的中间部分
                if(i != 0 && i != numRows - 1 && j + max - i < n){
                    res.append(s.charAt(j + max - i));
                }
            }
        }
        return res.toString();
    }
}

复杂度分析:

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)