LeetCode第六题: Z 字形变换(Java)

题目: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);

示例:

输入:s = "PAYPALISHIRING", numRows = 4
输出:"PINALSIGYAHRPI"
解释:
P     I    N
A   L S  I G
Y A   H R
P     I

方法一:自己通过每个数规律找到的方法

每一行看作一个集合,最后按顺序输出

先解释一下题意

输入字符串后像Z(蛇形)一样排列  排列后的字符串从第一行,一行一行输出。

说下我自己的思路:

为了方便说明,画出了每个字母的下标

发现的一些规律:

如图:每6个数字一组循环,竖线+斜线。我们以第一组为例

         竖线上有4个元素,下标为0到3。定义四个元素集合,对应字符分别放入第0个元素集合,第1个元素集合、第2个元素集合、第3个元素集合里。

         斜线上有2个元素 ,下标为4和5。放入第3个元素集合和第2个元素集合里。

推广:

        i 表示对应字符的下标

       竖线上有numRows个元素,斜线上有 numRows-2 个元素, numRows + numRows - 2个元素(称为 j )一组循环,竖线+斜线的元素和。

       竖线上的元素放到第 i % j 个元素集合,斜线上放入第 j - (i % j) 个元素集合里。最后按集合顺序读取元素即可。

       最开始我不知道该用什么集合存储,用过四个数组,还用过map集合。看了官方解答用 list 集合存储四个 StringBuilder 感觉比我想到的存储办法都好。

       

    //每个竖线+斜线为一组  自己的想法
    public static String convert(String s, int numRows) {
        //Map<Integer, String> str = new HashMap<>();
        //String[] str = new String[numRows];
        if (numRows == 1) {
            return s;
        }
        //竖线有几个元素就创建几个StringBuilder,
        List<StringBuilder> rows = new ArrayList<>();
        //Math.min(numRows, s.length()),防止字符个数比传入的numRows小,创建多余的StringBuilder浪费空间
        for (int i = 0; i < Math.min(numRows, s.length()); i++) {
            rows.add(new StringBuilder());
        }
        int n = s.length();
        //j个元素为一组循环
        int j = numRows + numRows - 2;
        for (int i = 0; i < n; i++) {
            //i再竖线上
            if (i % j < numRows) {
                //把对应字符存入第i % j个元素集合
                rows.get(i % j).append(s.charAt(i));
            } else {
                //i在斜线上,把对应字符存入第j - (i % j)个元素集合
                rows.get(j - (i % j)).append(s.charAt(i));
            }
        }
        //输出字符串
        StringBuilder ret = new StringBuilder();
        for (StringBuilder row : rows) {
            ret.append(row);
        }
        return ret.toString();
    }

看了官方的解答,感觉点像按行排序,但是比官方的方法消耗内存多点。

方法二:按行排序

相比方法一,去掉了几个元素为一组,增加个flag变量。当flag = true则表明 i 在竖线上。当flag = false则表明 i 在斜线上。每次 i 到最底下或最顶上一个元素时 flag 改变。

//每次到最下面一行就—1  最上一行就+1 存储
    public static 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 curRow = 0;
        boolean flag = false;

        for (char c : s.toCharArray()) {
            rows.get(curRow).append(c);
            if (curRow == 0 || curRow == numRows - 1) flag = !flag;
            curRow += flag ? 1 : -1;
        }

        StringBuilder ret = new StringBuilder();
        for (StringBuilder row : rows) ret.append(row);
        return ret.toString();
    }

方法三:按行访问

由方法一的规律,我们可以知道每个元素的下标。把第0行元素都存储到StringBuilder中,再把第一行元素都存储StringBuilder中,直到第numRows-1行。

    //官方二
    public static String convert(String s, int numRows) {
        if (numRows == 1) return s;
        StringBuilder ret = new StringBuilder();
        int n =s.length();
        //cycleLen个元素为一组
        int cycleLen = numRows+ numRows-2;
        for (int i = 0;i<numRows;i++){
            for (int j =0;j+i<n;j+=cycleLen ){
                //i位于竖线上
                ret.append(s.charAt(j+i));
                //i不在第0行也不在最后一行(即i在斜线上)
                if ( i != 0 && i != numRows-1 && j+cycleLen-i<n){
                    ret.append(s.charAt(j+cycleLen-i));
                }
            }
        }
        return ret.toString();
    }

这种方法是最优解了。

总结:

          这题相对来说不算太难,只要想到规律就很容易解决。

        for (int i = 0; i < s.length(); i++) {
            rows.add(new StringBuilder());
        }

    这么创建StringBulider头次见过,要记住。

同时map的value还可以存储list类型。也比较常用

         

       

        

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值