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"
实现思路:
这题先说一下题目,它是选取字符串的每个字母进行排列,按照限定行数,先从上往下排列,然后再从左往右进行排列,最终排出的形状如同几个逆时针旋转90度的 "Z"字形状。
实现思路一:暴力求解
我们就使用二维数组,将每个字母,一个个按照位置进行排列,形成最终的Z字排列效果。
实现步骤:
-
检查特殊情况:
- 如果
numRows
等于1,或者numRows
大于或等于字符串s
的长度,直接返回原始字符串,因为Z字形变换在这些情况下没有必要或没有意义。
- 如果
-
初始化数据结构:
- 创建一个二维字符数组
rows
,其大小为numRows
乘以字符串长度。这个数组将用于存储Z字形的每一行。 - 初始化变量
currentRow
为0,表示当前填充的行。 - 初始化布尔变量
goingDown
,用于指示当前移动方向是向下还是向上。
- 创建一个二维字符数组
-
填充二维数组:
- 使用for循环遍历字符串
s
中的每个字符。 - 将当前字符放入
rows
数组的currentRow
行的当前索引位置。 - 检查
currentRow
是否在第一行或最后一行,如果是,则切换goingDown
的值,以改变移动方向。 - 根据
goingDown
的值更新currentRow
,如果goingDown
为true
,则currentRow
递增;否则递减。
- 使用for循环遍历字符串
-
构建结果字符串:
- 创建
StringBuilder
对象result
,用于高效地构建最终的字符串。 - 使用嵌套的for循环遍历
rows
数组的每一行和每一行中的每个字符。 - 对于每个字符,如果它不是初始值(在Java中,字符数组的初始值是0,但这里应该是空字符
'\u0000'
),则将其追加到StringBuilder
对象中。
- 创建
-
返回结果:
- 将
StringBuilder
对象转换为字符串,并返回这个字符串,它是Z字形变换后的字符串。
- 将
实现代码1:
public String convert(String s, int numRows) {
// 如果只有1行或者需要的行数大于或等于字符串的长度,直接返回原始字符串
if (numRows == 1 || numRows >= s.length()) {
return s;
}
// 创建一个二维字符数组,每个子数组的大小为s的长度,用于存储每一行的字符
char[][] rows = new char[numRows][s.length()];
// 初始化当前行索引为0,表示从第一行开始填充
int currentRow = 0;
// 初始化方向标志,false表示向上移动,true表示向下移动
boolean goingDown = false;
// 遍历字符串s中的每个字符
for (int i = 0; i < s.length(); i++) {
// 将当前字符放入二维数组的相应位置
rows[currentRow][i] = s.charAt(i);
// 如果当前行是第一行或最后一行,改变移动方向
if (currentRow == 0 || currentRow == numRows - 1) {
goingDown = !goingDown;
}
// 根据当前的移动方向更新currentRow的值
// 如果goingDown为true,currentRow递增,否则递减
if (goingDown) {
currentRow++;
} else {
currentRow--;
}
}
// 创建StringBuilder对象用于构建最终的字符串结果
StringBuilder result = new StringBuilder();
// 遍历二维数组的每一行
for (char[] row : rows) {
// 遍历行中的每个字符
for (char c : row) {
// 过滤掉未赋值的位置
if (c != '\u0000') {
result.append(c);
}
}
}
// 将StringBuilder对象转换为字符串并返回
return result.toString();
}
为了方便字符串拼接操作,使用了StringBuilder类和增强的for循环,如果有忘记StringBuilder类这个知识点的朋友可以看看反转字符串中的单词 一题,可以写写,也可以看后面的知识补充。
实现思路2:对思路一进行了小小优化,提高了执行效率
-
输入验证:检查
numRows
是否小于等于1或者大于等于字符串s
的长度。如果是,则直接返回原始字符串,因为不需要进行Z字形变换。 -
初始化行数组:创建一个
StringBuilder
数组rows
,其长度为numRows
。数组中的每个元素都将用于构建Z字形变换后的每一行。 -
初始化当前行和方向:设置
currentRow
为0,表示当前填充的行。goingDown
标志用来指示当前是在向下填充(true
)还是向上填充(false
)。 -
遍历字符串:遍历输入字符串
s
中的每个字符。 -
添加字符到当前行:将当前字符添加到
rows
数组的currentRow
索引处的StringBuilder
对象中。 -
更新方向:如果当前行是第一行或最后一行,翻转
goingDown
的值,以改变填充方向。 -
移动到下一行:根据
goingDown
的值,更新currentRow
。如果goingDown
为true
,currentRow
递增;如果为false
,currentRow
递减。 -
重复填充:继续遍历字符串,重复步骤5-7,直到所有字符都被添加到相应的行。
-
拼接结果:创建一个
StringBuilder
对象result
,用于拼接最终的Z字形字符串。遍历rows
数组,将每个StringBuilder
对象转换成字符串并追加到result
。 -
返回结果:将
result
转换成字符串并返回,这是Z字形变换后的最终结果。
实现代码2:
public String convert(String s, int numRows) {
// 特殊情况处理:如果只有1行或者行数大于字符串长度,直接返回原始字符串
if (numRows <= 1 || numRows >= s.length()) {
return s;
}
// 创建一个二维数组,每个子数组代表一行,长度为字符串的长度
StringBuilder[] rows = new StringBuilder[numRows];
for (int i = 0; i < numRows; i++) {
rows[i] = new StringBuilder();
}
// 遍历字符串中的每个字符
int currentRow = 0; // 当前行
boolean goingDown = false; // 是否向下移动
for (char c : s.toCharArray()) {
rows[currentRow].append(c); // 将字符添加到当前行
// 判断移动方向
if (currentRow == 0 || currentRow == numRows - 1) {
goingDown = !goingDown;
}
// 根据移动方向更新当前行号
currentRow += goingDown ? 1 : -1;
}
// 将所有行的字符串拼接起来,形成最终的Z字形字符串
StringBuilder result = new StringBuilder();
for (StringBuilder row : rows) {
result.append(row);
}
return result.toString();
}
currentRow += goingDown ? 1 : -1 等价于
if (goingDown) {
currentRow += 1; // 如果goingDown为true,currentRow增加1
} else {
currentRow -= 1; // 如果goingDown为false,currentRow减少1
}