算法思路练习003-Z字变换
题目
将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 “PAYPALISHIRING” 行数为 3 时,排列如下:
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:“PAHNAPLSIIGYIR”。
请你实现这个将字符串进行指定行数变换的函数:
string convert(string s, int numRows);
思路
先在本子上随便画了一下图,也就是上面那个图,比较直观的感觉就是可以用一个二维数组把字符按列循环塞进指定的位置。然后按行挨着遍历出来。
如果字符串长度小于高度,直接返回字符串本身。
代码尝试——尝试失败
写了半天还是没想明白怎么正确的循环把字符放入数组,直接贴leetcode的官方代码,用注释捋一下思路
public String convert(String s, int numRows) {
int n = s.length(), r = numRows;
if (r == 1 || r >= n) {
return s;
}
//从第一行到最后一行,再回到第二行,一个完整周期所需要的字符数量t
int t = r * 2 - 2;
//矩阵的总列数c。
//一个周期的列数是r-1
//周期数量是我自己写的是n/t +1=(n+t)/t,+1是因为n对t取整,可能会缺少一个周期。
//答案是(n + t - 1) / t,
//事实证明,答案更准确。
//因为n/t +1是必定会多算一个周期,即使n/t是刚好可以除尽没有余数。
//而(n + t - 1) / t , 当n/t没有余数时,由于(t-1)/t= 0 ,(n + t - 1) / t =n/t
//吗的好烦!
//所以总列数,用c = (n + t - 1) / t * (r - 1)
int c = (n + t - 1) / t * (r - 1);
//定义数组。列数c就算多定义一点应该也没关系吧,时间全浪费在上面了 :(
char[][] mat = new char[r][c];
//x控制上下移动,y控制左右移动
for (int i = 0, x = 0, y = 0; i < n; ++i) {
mat[x][y] = s.charAt(i);
//循环核心:i纵向移动的次数对一个周期取余,如果小于一半的周期就向下移动,大于一半周期了,就向上移动
if (i % t < r - 1) {
++x; // 向下移动
} else {
--x;
++y; // 向右上移动
}
}
StringBuffer ans = new StringBuffer();
for (char[] row : mat) {
for (char ch : row) {
if (ch != 0) {
ans.append(ch);
}
}
}
return ans.toString();
}
作者:力扣官方题解
链接:https://leetcode.cn/problems/zigzag-conversion/solutions/1298127/z-zi-xing-bian-huan-by-leetcode-solution-4n3u/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
leetcode的大神算法
public String convert(String s, int numRows) {
if(numRows < 2) return s;
List<StringBuilder> rows = new ArrayList<StringBuilder>();
//变换之后的图形除了看作一个二维数组之外,更进一步整合,可以看作由numRows行的字符串组成
//而且输出的时候不需要输出字符之间的空格,所以直接将遍历到的字符拼接到对应行数的后面
//创建numRows个数的StringBuilder,这些StringBuilder用来存储变换之后每一行的内容
for(int i = 0; i < numRows; i++) {
rows.add(new StringBuilder());
}
int i = 0, flag = -1;
for(char c : s.toCharArray()) {
rows.get(i).append(c);
//太妙了
if(i == 0 || i == numRows -1) flag = - flag;
i += flag;
}
StringBuilder res = new StringBuilder();
for(StringBuilder row : rows) res.append(row);
return res.toString();
}
作者:Krahets
链接:https://leetcode.cn/problems/zigzag-conversion/solutions/21610/zzi-xing-bian-huan-by-jyd/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
太妙了,默写一遍,加深记忆
//因为输出不需要输出空格,所以可以将每一行字符汇总成一个字符串StringBuilder,依次往后append()
//然后将字符串存入list,需要修改哪一行字符串就用list.get()
List<StringBuilder> list = new ArrayList<StringBuilder>();
for(int i = 0 ;i < numRows ; i++){
list.add(new StringBuilder());
}
//将s中的字符拿出来依次往list里的字符串上append,
//如何循环获取list中的String?
int i = 0;
int flag = -1;
for(Char c : s.toCharArray()){
//当循环到第一行或者最后一行时,flag变换符号,让i可以掉头。
if(i == 0 || i == numRows-1){
flag = -flag;
}
list.get(i).append(c);
i+=flag;
}
//输出
StringBuilder result = new StringBuilder();
//把list中的string拿出来
for(StringBuilder s : list){
//把string转换成char依次拼入结果字符串————这一步多余了,可以直接把字符串拼入字符串
for(Char c : s.toCharArray()){
result.append(c);
}
}
return result;
总结——有周期的循环方法
- 对于有周期性的问题,先对周期进行拆解分析,分析临界点的变化规律,分段分析的解决问题
- 巧用flag在临界点变换
你要等候,再等候。