目录
1. N 字形变换(6)
题解一(力扣官方解析力扣)
题解二(官方解法)
题解三(力扣)
2. 整数反转(7)
题解一
题解二
题解三(官方解析,与第一种类似)
3.字符串转换整数 (atoi)(8)
题解一
题解二
题解三(最优解)
题解四
1. N 字形变换(6)
将一个给定字符串 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"
题解一(力扣官方解析力扣)
class Solution {
public String convert(String s, int numRows) {
int n = s.length(), r = numRows;
if (r == 1 || r >= n) {
return s;
}
int t = r * 2 - 2;
int c = (n + t - 1) / t * (r - 1);
char[][] mat = new char[r][c];
for (int i = 0, x = 0, y = 0; i < n; ++i) {
mat[x][y] = s.charAt(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();
}
}
题解二(官方解法)
方法一中的矩阵有大量的空间没有被使用,能否优化呢?
注意到每次往矩阵的某一行添加字符时,都会添加到该行上一个字符的右侧,且最后组成答案时只会用到每行的非空字符。因此我们可以将矩阵的每行初始化为一个空列表,每次向某一行添加字符时,添加到该行的列表末尾即可。
class Solution {
public String convert(String s, int numRows) {
int n = s.length(), r = numRows;
if (r == 1 || r >= n) {
return s;
}
StringBuffer[] mat = new StringBuffer[r];
for (int i = 0; i < r; ++i) {
mat[i] = new StringBuffer();
}
for (int i = 0, x = 0, t = r * 2 - 2; i < n; ++i) {
mat[x].append(s.charAt(i));
if (i % t < r - 1) {
++x;
} else {
--x;
}
}
StringBuffer ans = new StringBuffer();
for (StringBuffer row : mat) {
ans.append(row);
}
return ans.toString();
}
}
题解三(力扣)
这是k神的解法,只能说看完以后,直接佩服,用字符缓冲区完美的解决了这个问题,我真的有点惯性思维了,第一时间只想到了二维数组。没想到用字符缓冲区。还有flag用的很妙。
class Solution {
public String convert(String s, int numRows) {
if(numRows < 2) return s;
List<StringBuilder> rows = new ArrayList<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();
}
}
2. 整数反转(7)
给你一个 32 位的有符号整数 x
,返回将 x
中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1]
,就返回 0。
假设环境不允许存储 64 位整数(有符号或无符号)。
示例 1:
输入:x = 123 输出:321
示例 2:
输入:x = -123 输出:-321
示例 3:
输入:x = 120 输出:21
题解一
每次取末尾数字,拼成一个新的。(参考自力扣)判断条件,因为x本身会被int限制,当x为正数并且位数和Integer.MAX_VALUE的位数相等时首位最大只能为2,所以逆转后不会出现
res = Integer.MAX_VALUE / 10 && tmp > 2的情况,自然也不需要判断res==214748364 && tmp>7了,反之负数情况也一样
class Solution {
public int reverse(int x) {
int res = 0;
while(x!=0) {
//每次取末尾数字
int tmp = x%10;
//判断是否 大于 最大32位整数
if (res > Integer.MAX_VALUE / 10 || res < Integer.MIN_VALUE / 10) {
return 0;
}
res = res*10 + tmp;
x /= 10;
}
return res;
}
}
题解二
先转成string,再转成char,然后先判断符号,再循环首尾交换。处理异常。
class Solution {
public int reverse(int x) {
String s=String.valueOf(x);
char[] array=s.toCharArray();
int i=0,right=s.length()-1;
char temp=' ';
if(s.charAt(i)=='+'||s.charAt(i)=='-'){
i++;
}
while(i<right){
temp=array[i];
array[i]=array[right];
array[right]=temp;
i++;
right--;
}
try{
return Integer.parseInt(String.valueOf(array));
}catch(NumberFormatException e){
return 0;
}
}
}
题解三(官方解析,与第一种类似)
参考力扣
记rev 为翻转后的数字,为完成翻转,我们可以重复「弹出」xxx 的末尾数字,将其「推入」rev 的末尾,直至 xxx 为 000。
要在没有辅助栈或数组的帮助下「弹出」和「推入」数字,我们可以使用如下数学方法:
// 弹出 x 的末尾数字 digit
digit = x % 10
x /= 10
// 将数字 digit 推入 rev 末尾
rev = rev * 10 + digit
class Solution {
public int reverse(int x) {
int rev = 0;
while (x != 0) {
if (rev < Integer.MIN_VALUE / 10 || rev > Integer.MAX_VALUE / 10) {
return 0;
}
int digit = x % 10;
x /= 10;
rev = rev * 10 + digit;
}
return rev;
}
}
3.字符串转换整数 (atoi)(8)
请你来实现一个 myAtoi(string s)
函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi
函数)。
函数 myAtoi(string s)
的算法如下:
- 读入字符串并丢弃无用的前导空格
- 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
- 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
- 将前面步骤读入的这些数字转换为整数(即,"123" -> 123, "0032" -> 32)。如果没有读入数字,则整数为
0
。必要时更改符号(从步骤 2 开始)。 - 如果整数数超过 32 位有符号整数范围
[−231, 231 − 1]
,需要截断这个整数,使其保持在这个范围内。具体来说,小于−231
的整数应该被固定为−231
,大于231 − 1
的整数应该被固定为231 − 1
。 - 返回整数作为最终结果。
注意:
- 本题中的空白字符只包括空格字符
' '
。 - 除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。
示例 1:
输入:s = "42" 输出:42 解释:加粗的字符串为已经读入的字符,插入符号是当前读取的字符。 第 1 步:"42"(当前没有读入字符,因为没有前导空格) ^ 第 2 步:"42"(当前没有读入字符,因为这里不存在 '-' 或者 '+') ^ 第 3 步:"42"(读入 "42") ^ 解析得到整数 42 。 由于 "42" 在范围 [-231, 231 - 1] 内,最终结果为 42 。
示例 2:
输入:s = " -42" 输出:-42 解释: 第 1 步:" -42"(读入前导空格,但忽视掉) ^ 第 2 步:" -42"(读入 '-' 字符,所以结果应该是负数) ^ 第 3 步:" -42"(读入 "42") ^ 解析得到整数 -42 。 由于 "-42" 在范围 [-231, 231 - 1] 内,最终结果为 -42 。
示例 3:
输入:s = "4193 with words" 输出:4193 解释: 第 1 步:"4193 with words"(当前没有读入字符,因为没有前导空格) ^ 第 2 步:"4193 with words"(当前没有读入字符,因为这里不存在 '-' 或者 '+') ^ 第 3 步:"4193 with words"(读入 "4193";由于下一个字符不是一个数字,所以读入停止) ^ 解析得到整数 4193 。 由于 "4193" 在范围 [-231, 231 - 1] 内,最终结果为 4193 。
题解一
首先将字符串分割成数组并去掉首部空格,然后遍历,如果第一个为符号,则存入,然后匹配数字,不是数字则break。然后对字符进行异常处理,判断边界和符号。
class Solution {
public int myAtoi(String s) {
int result=0,i=0;
String[] str=s.trim().split("");//去掉首部空格并分割成字符数组
String flag="";//正负符号
StringBuffer newStr=new StringBuffer();//新建一个临时存储区域
while(i<str.length){
if(str[i].equals("+")&&i==0||str[i].equals("-")&&i==0){//如果有符号则存储
flag=str[i];
i++;
continue;
}
if(str[i].matches("[0-9]+")){//用正则匹配数字
newStr.append(str[i]);
i++;
}else{
break;
}
}
try{
if(!newStr.toString().equals("")){
if(!flag.equals("")){
result=Integer.parseInt(flag+newStr.toString());
}else{
result=Integer.parseInt(newStr.toString());
}
}else{
result=0;
}
}catch(NumberFormatException e){
// System.out.println(e);
if(flag.equals("-")){//判断范围 处理异常
result=(int)Math.pow(-2,31);
}else{
result=(int)Math.pow(2,31);
}
}finally{
return result;
}
}
}
题解二
这个是力扣上的官方解析,算法很好,首先列一个状态表,存入map,再遍历字符串,根据状态分别进行不同的判断。
class Solution {
public int myAtoi(String str) {
Automaton automaton = new Automaton();
int length = str.length();
for (int i = 0; i < length; ++i) {
automaton.get(str.charAt(i));
}
return (int) (automaton.sign * automaton.ans);
}
}
class Automaton {
public int sign = 1;
public long ans = 0;
private String state = "start";
private Map<String, String[]> table = new HashMap<String, String[]>() {{
put("start", new String[]{"start", "signed", "in_number", "end"});
put("signed", new String[]{"end", "end", "in_number", "end"});
put("in_number", new String[]{"end", "end", "in_number", "end"});
put("end", new String[]{"end", "end", "end", "end"});
}};
public void get(char c) {
state = table.get(state)[get_col(c)];
if ("in_number".equals(state)) {
ans = ans * 10 + c - '0';
ans = sign == 1 ? Math.min(ans, (long) Integer.MAX_VALUE) : Math.min(ans, -(long) Integer.MIN_VALUE);
} else if ("signed".equals(state)) {
sign = c == '+' ? 1 : -1;
}
}
private int get_col(char c) {
if (c == ' ') {
return 0;
}
if (c == '+' || c == '-') {
return 1;
}
if (Character.isDigit(c)) {
return 2;
}
return 3;
}
}
题解三(最优解)
首先这个方法去除首部空格很巧妙,并且考虑了全是空格的极端情况。
然后判断符号位。紧接着遍历字符串,判断数字,并且判断边界。
public class Solution {
public int myAtoi(String str) {
int len = str.length();
// str.charAt(i) 方法回去检查下标的合法性,一般先转换成字符数组
char[] charArray = str.toCharArray();
// 1、去除前导空格
int index = 0;
while (index < len && charArray[index] == ' ') {
index++;
}
// 2、如果已经遍历完成(针对极端用例 " ")
if (index == len) {
return 0;
}
// 3、如果出现符号字符,仅第 1 个有效,并记录正负
int sign = 1;
char firstChar = charArray[index];
if (firstChar == '+') {
index++;
} else if (firstChar == '-') {
index++;
sign = -1;
}
// 4、将后续出现的数字字符进行转换
// 不能使用 long 类型,这是题目说的
int res = 0;
while (index < len) {
char currChar = charArray[index];
// 4.1 先判断不合法的情况
if (currChar > '9' || currChar < '0') {
break;
}
// 题目中说:环境只能存储 32 位大小的有符号整数,因此,需要提前判:断乘以 10 以后是否越界
if (res > Integer.MAX_VALUE / 10 || (res == Integer.MAX_VALUE / 10 && (currChar - '0') > Integer.MAX_VALUE % 10)) {
return Integer.MAX_VALUE;
}
if (res < Integer.MIN_VALUE / 10 || (res == Integer.MIN_VALUE / 10 && (currChar - '0') > -(Integer.MIN_VALUE % 10))) {
return Integer.MIN_VALUE;
}
// 4.2 合法的情况下,才考虑转换,每一步都把符号位乘进去
res = res * 10 + sign * (currChar - '0');
index++;
}
return res;
}
}
题解四
我的看法是不如解法三更加容易理解。效率与解法三一致。
public class Solution {
public int myAtoi(String str) {
int result = 0;
boolean negative = false;
int i = 0, len = str.length();
int limit = -Integer.MAX_VALUE;
int multmin;
int digit;
//skip space before string
while (i < len) {
if (str.charAt(i) == ' ') {
i++;
} else {
break;
}
}
if (i < len) {
char firstChar = str.charAt(i);
if (firstChar < '0') { // Possible leading "+" or "-"
if (firstChar == '-') {
negative = true;
limit = Integer.MIN_VALUE;
} else if (firstChar != '+') {
return 0;
}
i++;
if (len == i) {
return 0;
}
}
multmin = limit / 10;
while (i < len) {
// Accumulating negatively avoids surprises near MAX_VALUE
digit = Character.digit(str.charAt(i++), 10);
if (digit < 0) {
return negative ? result : -result;
}
if (result < multmin) {
return negative ? Integer.MIN_VALUE : Integer.MAX_VALUE;
}
result *= 10;
if (result < limit + digit) {
return negative ? Integer.MIN_VALUE : Integer.MAX_VALUE;
}
result -= digit;
}
} else {
return 0;
}
return negative ? result : -result;
}
}