1.问题
2.题目意思
给定一个字符串,使它按照Z字型排列(说起来应该是一个倒N字形吧),这个Z字形的大小由传入的参数决定,然后我们应该返回一个顺序读的字符串
3.思路
当时找了个纸画了一下,不过等想起写博客的时候纸已经不见了……
为了 弥补我的过失,特地画了一个狗血的示意图。【processon.com】
按照图中所示,我们目前拿数组下标来表示数据,而且整个题目都会以数组下标作为关键。那我们的核心就是创建一个StringBuilder,直接从源字符串取数据进行append,像是一种没有进行zigzag排序,直接进行合理的字符串选取.
- 图中可以显然知道numRows等于4,也就是说在字符串长度内,首先访问第一排数字 0,6,12 (这里我把转折点理解做两个重叠的数),参考下面的代码,i = 0时,在字符串长度范围内,添加第一个字符,
-
下一行代码 j += 2*(numRows-i-1);是一个找规律找出来的数学公式,有种像初中找规律的题,就是说我们要确定3这个转折点到6这个转折点中间有多少数据,当然我们口算都能答上来的问题,计算机可需要提示,那么我们看到下面的图的数据
image.png
numRows = 4
0 --- 6 间隔6 (i = 0)
1 --- 5 间隔4 (i = 1)
2 --- 4 间隔2 (i = 2)
3 --- 3 间隔0 (i = 3)
那么我们把他转换成一个找规律的数学题,小学生也可以找到规律吧,
6,4,2,0 也就是 2(4-i-1) 或者 2(numRows-i-1); 可以带入值验证一下。
-
当然我们这个是不够的,因为这是一个V的形状, 倒V的形状是一个反的公式,2*i ,大家可以研究一下。
image.png 那么这个循环就可以理解了,访问添加第一个字符,按照V型公式位移j,判断位移后的j的合法性,而且需要判断是否是转折点(因为我们刚才说假设转折点两个重复数据的情况,这里只需要添加一个才能满足题意),合法后添加字符串,接着按照倒V型公式位移j,直到字符串遍历完成。
while(j < s.length()){
sb.append(s.charAt(j));
j += 2*(numRows-i-1);
if(i!=numRows-1&&i!=0&&j<=s.length()-1)
{
sb.append(s.charAt(j));
}
j += 2*i;
}
- 最后的i循环,就是控制一层一层向下遍历。此外一些特殊的情况,numRows == 1的情况,直接返回,采用StringBuilder方式加快构造字符串
4.代码
class Solution {
public String convert(String s, int numRows) {
int j = 0;
if(numRows == 1){
return s;
}
StringBuilder sb = new StringBuilder();
for(int i=0;i<numRows;i++){
j = i;
while(j < s.length()){
sb.append(s.charAt(j));
j += 2*(numRows-i-1);
if(i!=numRows-1&&i!=0&&j<=s.length()-1)
{
sb.append(s.charAt(j));
}
j += 2*i;
}
}
return sb.toString();
}
}
5.大佬算法
public String convert(String s, int nRows) {
char[] c = s.toCharArray();
int len = c.length;
StringBuffer[] sb = new StringBuffer[nRows];
for (int i = 0; i < sb.length; i++) sb[i] = new StringBuffer();
int i = 0;
while (i < len) {
for (int idx = 0; idx < nRows && i < len; idx++) // vertically down
sb[idx].append(c[i++]);
for (int idx = nRows-2; idx >= 1 && i < len; idx--) // obliquely up
sb[idx].append(c[i++]);
}
for (int idx = 1; idx < sb.length; idx++)
sb[0].append(sb[idx]);
return sb[0].toString();
}
这个算法,也算是空间换取时间吧,建立一个StringBuffer数组,数组长度是nRows的长度,首先初始化,接着来一遍字符串循环,然后把竖行数据分别添加到StringBuffer[0] ,StringBuffer[1],StringBuffer[2] ……StringBuffer[nRows -1],然后下一个循环是把斜行数据存起来,如此循环,那么我们就可以知道,StringBuffer[n] 存的就是第n横行的数据,这样子在最后的循环中拼接到StringBuffer[0]上就行了。
6.根据大佬算法改进我的算法
class Solution {
public String convert(String s, int numRows) {
int i,j = 0;
if(numRows == 1){
return s;
}
StringBuffer[] sbs = new StringBuffer[numRows];
for ( i = 0; i < sbs.length; i++) sbs[i] = new StringBuffer();
i = 0;
while(j < s.length() && i < numRows){
sbs[i].append(s.charAt(j));
j += 2*(numRows-i-1);
if(i!=numRows-1&&i!=0&&j<=s.length()-1)
{
sbs[i].append(s.charAt(j));
}
j += 2*i;
if(s.length() <= j ){
j = ++i;
}
}
for (i = 1; i < sbs.length; i++)
sbs[0].append(sbs[i]);
return sbs[0].toString();
}
}
这样优化的话,从240ms的用时到了50多,核心的思想,也就是那个公式没有变,增加了StringBuffer来数组来管理数据。
7.总结
人丑就得多读书。
LeeCode 之旅继续.