Skip to content

Latest commit

 

History

History
176 lines (132 loc) · 5.6 KB

6.Z字形变换.md

File metadata and controls

176 lines (132 loc) · 5.6 KB

6.Z字形变换

题目

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

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

P   A   H   N
A P L S I I G
Y   I   R

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

请你实现这个将字符串进行指定行数变换的函数:string convert(string s, int numRows);

示例 1:

  • 输入:s = "PAYPALISHIRING", numRows = 3
  • 输出:"PAHNAPLSIIGYIR"

示例 2:

  • 输入:s = "PAYPALISHIRING", numRows = 4
  • 输出:"PINALSIGYAHRPI"

解释:

P     I    N
A   L S  I G
Y A   H R
P     I

示例 3:

  • 输入:s = "A", numRows = 1
  • 输出:"A"

来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/zigzag-conversion 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路以及解答

原来的顺序是 "Z" 字型的,我们用小方块来模拟位置摆放:

现在需要按照每一行来遍历:

那么我们就要先找到里面的规律,首先按照每一行遍历,那么每行有行号,遍历的时候,按照 从第 0 行到 第 row-1 行(也就是最外层循环):

那么每一行需要怎么处理呢?

需要我们发现每一行的规律,我们用 row = 6i = 3的行来摸索,也就是 6 行中的第 3行:

【看 i = 3 的行】从第一个方块到第二个方块,其实间隔的方块数就是就是下面的红色部分,当前为 第 i行,下面还有 row-1-i 行,下面的红色方块的数量计算为: 每一行都是两个方块除了最下面一行只有一个,所以是 行数*2-1 ,即 (row-1-i)*2-1,所以从第一个方块到第二个方块,中间间隔了 方块数 = (row-1-i)*2-1 个方块,那么只要第一个位置加上 方块数+1,就可以得到第二个方块的位置。

gap = 方块数 + 1 = (row - 1 - i) * 2

下面我们看第 3 行,第二个方块到第三个方块,间隔的是上面的红色方块,第 i 行上面还有 i 行,每一行都有两个方块,除了第一行,所以上面方块数为 2*i-1,间隔的 gap:

gap = 方块数 + 1 = 2 * i

我们接着看第 3 行,第三个方块到第四个方块,其实重复了前面的第一个方块到第二个方块的过程:

gap = 方块数 + 1 = (row - 1 - i) * 2

至此,我们发现了其中的规律,每一行中的间隔是不断循环的过程,每个循环的过程,又分为两个阶段:

阶段1: gap1 = 方块数 + 1 = (row - 1 - i) * 2
阶段2: gap2 = 方块数 + 1 = 2 * i

每一行循环的结束条件是:不能超过字符串长度 ,每个循环里面的两个间隔gap 其实用 flag 不断变化也是可以的, 但是我们发现它们的和是固定的 (row - 1) * 2

sum = gap1 + gap2 = (row - 1 - i) * 2 + 2 * i = (row - 1) * 2

那就好办,每次用 sum - gap 就可以得到下一次的 gap

特殊情况: 第一行和第二行,每一个循环中有一个 gap0, gap 为 0 的时候我们不能重复打印该位置的字符:

Java 代码实现:

public class Solution6 {
    public static void main(String[] args) {
        System.out.println(convert("PAYPALISHIRING", 4));
    }

    public static String convert(String s, int numRows) {
        if (numRows <= 1) {
            return s;
        }
        StringBuffer stringBuffer = new StringBuffer();
        for (int i = 0; i < numRows; i++) {
            int sum = 2 * (numRows - 1);
            int gap = (numRows - i - 1) * 2;
            for (int j = i; j < s.length(); ) {
                if (gap != 0) {
                    stringBuffer.append(s.charAt(j));
                }
                j = j + gap;
                gap = sum - gap;
            }
        }
        return stringBuffer.toString();
    }
}

C++ 代码实现:

#include<iostream>
#include<string>
using namespace std;
class Solution {
public:
    string convert(string s, int numRows) {
        if (numRows <= 1) {
            return s;
        }
        string str;
        for (int i = 0; i < numRows; i++) {
            int sum = 2 * (numRows - 1);
            int gap = (numRows - i - 1) * 2;
            for (int j = i; j < s.length(); ) {
                if (gap != 0) {
                    str += s[j];
                }
                j = j + gap;
                gap = sum - gap;
            }
        }
        return str;
    }
};
int main(){
    Solution solution;
    string str = "PAYPALISHIRING";
    cout<< solution.convert(str,4)<<endl;
    return 0;
}

至于时间复杂度,看似是两层循环,实则不是O(n^2^), 只是遍历完了里面所有的字符,所以为O(n)。

今日总结:

算法题有时候就是找规律(苦笑...)