LeetCode06 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"
提示:
1 <= s.length <= 1000
s 由英文字母(小写和大写)、',' 和 '.' 组成
1 <= numRows <= 1000
代码
class LeetCode06 {
public static void main(String[] args) {
System.out.println(new Solution().convert("LEETCODEISHIRING", 4));
System.out.println(new Solution2().convert("LEETCODEISHIRING", 4));
System.out.println(new Solution3().convert("LEETCODEISHIRING", 4));
System.out.println(new Solution4().convert("LEETCODEISHIRING", 4));
}
/*
利用二维矩阵模拟
*/
static 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();
}
}
/*
方法二:压缩矩阵空间
*/
static class Solution2 {
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();
}
}
static class Solution3 {
/*
* 官方方法3,直接构造
* 根据方法2可知,一个字符只要直到要放到哪一行即可
* 假设原字符串字符的下标为idx
* 周期t = r+(r -2) = 2r-2(解法一已有解释)
* 可以发现:
* 对于idx mod t < r的,就会放到第(idx mod t)行,
* 对于idx mod t > r(小于t)的,就会放到第t - (idx mod t)行
* 有了这个规律,就可以直接按公式算出要放的行,不用像方法2那样求行了,
* 只要从前往后依次计算该字符的行,然后直接加到每行的末尾,即可达到效果
*/
public String convert(String s, int numRows) {
//特殊情况
//numRows为1,直接返回s
if (numRows == 1) {
return s;
}
//s长度 <= numRows,直接返回s
if (s.length() <= numRows) {
return s;
}
int n = s.length();
//行数为numRows,简写为r。计算所需的列数,为(n/t向上取整)⋅(r−1)
// Z 字形变换的周期 t=r+(r−2)=2r−2
int t = numRows + (numRows - 2);
//定义数组,每行用StringBuilder
StringBuilder[] matrix = new StringBuilder[numRows];
for (int i = 0; i < matrix.length; i++) {
matrix[i] = new StringBuilder();
}
//填入每个字符
for (int i = 0; i < n; i++) {
//计算要放的行
int targetRow;
int mod = i % t;
if (mod < numRows) {
//对于idx mod t < r的,就会放到第(idx mod t)行
targetRow = mod;
} else {
//对于idx mod t >= r(小于t)的,就会放到第t - (idx mod t)行
targetRow = t - mod;
}
//放到对应行
matrix[targetRow].append(s.charAt(i));
}
//输出结果字符串
StringBuilder result = new StringBuilder();
for (int i = 0; i < numRows; i++) {
result.append(matrix[i]);
}
return result.toString();
}
}
/*
flag使用
*/
static class Solution4 {
public String convert(String s, int numRows) {
if (numRows < 2) return s;
List rows = new ArrayList();
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();
}
}
}
LeetCode07 整数反转
题目详情
给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] ,就返回 0。
假设环境不允许存储 64 位整数(有符号或无符号)。
示例 1:
输入:x = 123
输出:321
示例 2:
输入:x = -123
输出:-321
示例 3:
输入:x = 120
输出:21
示例 4:
输入:x = 0
输出:0
提示:
-231 <= x <= 231 - 1
代码
public class LeetCode07 {
public static void main(String[] args) {
System.out.println(new Solution().reverse(619));
System.out.println(new Solution2().reverse(619));
}
/*
方法:弹出和推入数字 & 溢出前进行检查
思路
我们可以一次构建反转整数的一位数字。在这样做的时候,我们可以预先检查向原整数附加另一位数字是否会导致溢出。
算法
反转整数的方法可以与反转字符串进行类比。
我们想重复“弹出” x 的最后一位数字,并将它“推入”到 rev 的后面。最后,rev 将与 x 相反。
要在没有辅助堆栈 / 数组的帮助下 “弹出” 和 “推入” 数字,我们可以使用数学方法。
//pop operation:
pop = x % 10;
x /= 10;
//push operation:
temp = rev * 10 + pop;
rev = temp;
但是,这种方法很危险,因为当 temp=rev⋅10+pop 时会导致溢出。
幸运的是,事先检查这个语句是否会导致溢出很容易。
*/
static class Solution {
public int reverse(int x) {
int rev = 0;
while (x != 0) {
int pop = x % 10;//出栈
x /= 10;//指下一个
if (rev > Integer.MAX_VALUE / 10 || (rev == Integer.MAX_VALUE / 10 && pop > 7))
return 0;
if (rev < Integer.MIN_VALUE / 10 || (rev == Integer.MIN_VALUE / 10 && pop < -8))
return 0;
rev = rev * 10 + pop;//入栈
}
return rev;
}
}
/*
数学
*/
static class Solution2 {
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;
}
}
}
LeetCode08 字符串转换整数(atoi)
题目详情
字符串转换整数(atoi)
请你来实现一个atoi函数,使其能将字符串转换成整数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。接下来的转化规则如下:
如果第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字字符组合起来,形成一个有符号整数。
假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成一个整数。
该字符串在有效的整数部分之后也可能会存在多余的字符,那么这些字符可以被忽略,它们对函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换,即无法进行有效转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0 。
提示:
本题中的空白字符只包括空格字符 ' ' 。
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为[−231, 231− 1]。如果数值超过这个范围,请返回 INT_MAX (231− 1) 或INT_MIN (−231) 。
示例1:
输入: "42"
输出: 42
示例2:
输入: " -42"
输出: -42
解释: 第一个非空白字符为 '-', 它是一个负号。
我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。
示例3:
输入: "4193 with words"
输出: 4193
解释: 转换截止于数字 '3' ,因为它的下一个字符不为数字。
示例4:
输入: "words and 987"
输出: 0
解释: 第一个非空字符是 'w', 但它不是数字或正、负号。
因此无法执行有效的转换。
示例5:
输入: "-91283472332"
输出: -2147483648
解释: 数字 "-91283472332" 超过 32 位有符号整数范围。
因此返回 INT_MIN (−231) 。
代码
class LeetCode08 {
public static void main(String[] args) {
System.out.println(new Solution().myAtoi(" 4193 with words"));
System.out.println(new Solution2().myAtoi(" 4193 with words"));
System.out.println(new Solution3().myAtoi(" 4193 with words"));
}
/*
这是确定有限状态机(deterministic finite automaton, DFA)。
其实这题已经不算是容易写“出臃肿代码”的了。
考虑清楚边界(主要是溢出处理)和输入种类(+, -, 0-9以及其他),大概在20行内完成代码不难。
说实话LC官方题解里很少见给出标准DFA解法的,点个赞。
给两个更加需要DFA的题目吧:utf-8-validation ( 附dfa解法 ) 以及 valid-number。
顺便贴一下普通解法(那种臃肿的、易错的、可能会被test cases虐到骂娘的;但如果骂娘了,本质还是基本功不扎实)。
*/
static class Solution {
public int myAtoi(String str) {
str = str.trim();
if (str.length() == 0) return 0;
if (!Character.isDigit(str.charAt(0))
&& str.charAt(0) != '-' && str.charAt(0) != '+')
return 0;
long ans = 0L;
boolean neg = str.charAt(0) == '-';
int i = !Character.isDigit(str.charAt(0)) ? 1 : 0;
while (i < str.length() && Character.isDigit(str.charAt(i))) {
ans = ans * 10 + (str.charAt(i++) - '0');
if (!neg && ans > Integer.MAX_VALUE) {
ans = Integer.MAX_VALUE;
break;
}
if (neg && ans > 1L + Integer.MAX_VALUE) {
ans = 1L + Integer.MAX_VALUE;
break;
}
}
return neg ? (int) -ans : (int) ans;
}
}
/*
自动机
*/
static class Solution2 {
static class Automaton {
final String START = "start";
final String SIGNED = "signed";
final String IN_NUM = "in_number";
final String END = "end";
String state = START;
Map map;
public int sign = 1;
public long ans = 0;
public Automaton() {
map = new HashMap<>();
map.put(START, new String[]{START, SIGNED, IN_NUM, END});
map.put(SIGNED, new String[]{END, END, IN_NUM, END});
map.put(IN_NUM, new String[]{END, END, IN_NUM, END});
map.put(END, new String[]{END, END, END, END});
}
public int get_col(char c) {
if (c == ' ') return 0;
if (c == '+' || c == '-') return 1;
if (c >= '0' && c <= '9') return 2;
return 3;
}
public void get(char c) {
state = map.get(state)[get_col(c)];
if (state.equals(IN_NUM)) {
ans = ans * 10 + c - '0';
if (sign == 1) {
ans = Math.min(ans, Integer.MAX_VALUE);
} else {
// -(long)Integer.MIN_VALUE,这个操作有点东西,不然越界
ans = Math.min(ans, -(long) Integer.MIN_VALUE);
}
} else if (state.equals(SIGNED))
sign = c == '+' ? 1 : -1;
}
}
public int myAtoi(String str) {
Automaton automaton = new Automaton();
char[] c = str.toCharArray();
for (char ch : c) {
automaton.get(ch);
}
return automaton.sign * ((int) automaton.ans);
}
}
/*
直接写
*/
static class Solution3 {
public int myAtoi(String s) {
int i = 0;
int len = s.length();
int sign = 1;
int res = 0;
while (i < len && s.charAt(i) == ' ') { //如果字符串前导位置为空格,循环到有数据的那一个位置
i++;
}
int start = i; //记录一下当前之后所有数据开始的位置
for (; i < len; i++) {
int c = s.charAt(i);
if (i == start && c == '+') { //判断是否是+,并且+要在初始位置
sign = 1;
} else if (i == start && c == '-') { //判断是-
sign = -1;
} else if (Character.isDigit(c)) { //判断是数字
int num = c - '0';
//如果是数字,其他不用考虑,只需要考虑两种超限的情况,这里不细说,具体去"https://leetcode-cn.com/problems/reverse-integer/"看
if (res > Integer.MAX_VALUE / 10 || (res == Integer.MAX_VALUE / 10 && num > Integer.MAX_VALUE % 10)) {
return Integer.MAX_VALUE;
} else if (res < Integer.MIN_VALUE / 10 || (res == Integer.MIN_VALUE / 10 && -num < Integer.MIN_VALUE % 10)) {
return Integer.MIN_VALUE;
}
res = res * 10 + sign * num;
} else { //如果有一次循环既不是数字,又不是'+'和'-',那么立即退出循环,并返回当前res中已经储存的值
break;
}
}
return res;
}
}
}
LeetCode14 最长公共前缀
题目详情
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 ""。
示例 1:
输入:strs = ["flower","flow","flight"]
输出:"fl"
示例 2:
输入:strs = ["dog","racecar","car"]
输出:""
解释:输入不存在公共前缀。
提示:
1 <= strs.length <= 200
0 <= strs[i].length <= 200
strs[i] 仅由小写英文字母组成
代码
public class LeetCode14 {
public static void main(String[] args) {
System.out.print(new Solution().longestCommonPrefix(new String[]{"flowsad", "flosad", "flisad"}));
}
/*
方法一是横向扫描,依次遍历每个字符串,更新最长公共前缀。另一种方法是纵向扫描。
纵向扫描时,从前往后遍历所有字符串的每一列,比较相同列上的字符是否相同,如果相同则继续对下一列进行比较,如果不相同则当前列不再属于公共前缀,当前列之前的部分为最长公共前缀。
*/
static class Solution {
public String longestCommonPrefix(String[] strs) {
if (strs == null || strs.length == 0) return "";
int i = 0;
StringBuilder res = new StringBuilder();
while (i < strs[0].length()) {
String pre = strs[0];
for (int j = 1; j < strs.length; j++) {
if (strs[j].length() < i + 1 || pre.charAt(i) != strs[j].charAt(i)) {
return res.toString();
}
pre = strs[j];
}
res.append(pre.charAt(i));
i++;
}
return res.toString();
}
}
}
LeetCode30 串联所有单词的子串
题目详情
给定一个字符串 s 和一些 长度相同 的单词 words 。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。
注意子串要与 words 中的单词完全匹配,中间不能有其他字符 ,但不需要考虑 words 中单词串联的顺序。
示例 1:
输入:s = "barfoothefoobarman", words = ["foo","bar"]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 "barfoo" 和 "foobar" 。
输出的顺序不重要, [9,0] 也是有效答案。
示例 2:
输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
输出:[]
示例 3:
输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
输出:[6,9,12]
提示:
1 <= s.length <= 104
s 由小写英文字母组成
1 <= words.length <= 5000
1 <= words[i].length <= 30
words[i] 由小写英文字母组成
代码
class LeetCode30 {
public static void main(String[] args) {
System.out.println(new Solution().findSubstring("barfoothefoobarman", new String[]{"foo", "bar"}));
System.out.println(new Solution2().findSubstring("barfoothefoobarman", new String[]{"foo", "bar"}));
}
/*
思路一:
因为单词长度固定的,我们可以计算出截取字符串的单词个数是否和 words 里相等,所以我们可以借用哈希表。
一个是哈希表是 words,一个哈希表是截取的字符串,比较两个哈希是否相等!
因为遍历和比较都是线性的,所以时间复杂度:O(n^2)O(n
2
)
上面思路每次都要反复遍历 s;下面介绍滑动窗口。
思路二:
滑动窗口!
我们一直在 s 维护着所有单词长度总和的一个长度队列!
时间复杂度:O(n)O(n)
还可以再优化,只是加一些判断,详细看代码吧!
*/
static class Solution {
public List findSubstring(String s, String[] words) {
List res = new ArrayList<>();
if (s == null || s.length() == 0 || words == null || words.length == 0) return res;
HashMap map = new HashMap<>();
int one_word = words[0].length();
int word_num = words.length;
int all_len = one_word * word_num;
for (String word : words) {
map.put(word, map.getOrDefault(word, 0) + 1);
}
for (int i = 0; i < s.length() - all_len + 1; i++) {
String tmp = s.substring(i, i + all_len);
HashMap tmp_map = new HashMap<>();
for (int j = 0; j < all_len; j += one_word) {
String w = tmp.substring(j, j + one_word);
tmp_map.put(w, tmp_map.getOrDefault(w, 0) + 1);
}
if (map.equals(tmp_map)) res.add(i);
}
return res;
}
}
static class Solution2 {
public List findSubstring(String s, String[] words) {
List res = new ArrayList<>();
if (s == null || s.length() == 0 || words == null || words.length == 0) return res;
HashMap map = new HashMap<>();
int one_word = words[0].length();
int word_num = words.length;
int all_len = one_word * word_num;
for (String word : words) {
map.put(word, map.getOrDefault(word, 0) + 1);
}
for (int i = 0; i < one_word; i++) {
int left = i, right = i, count = 0;
HashMap tmp_map = new HashMap<>();
while (right + one_word <= s.length()) {
String w = s.substring(right, right + one_word);
right += one_word;
if (!map.containsKey(w)) {
count = 0;
left = right;
tmp_map.clear();
} else {
tmp_map.put(w, tmp_map.getOrDefault(w, 0) + 1);
count++;
while (tmp_map.getOrDefault(w, 0) > map.getOrDefault(w, 0)) {
String t_w = s.substring(left, left + one_word);
count--;
tmp_map.put(t_w, tmp_map.getOrDefault(t_w, 0) - 1);
left += one_word;
}
if (count == word_num) res.add(left);
}
}
}
return res;
}
}
}
LeetCode131 分割回文串
题目详情
给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。
回文串 是正着读和反着读都一样的字符串。
示例 1:
输入:s = "aab"
输出:[["a","a","b"],["aa","b"]]
示例 2:
输入:s = "a"
输出:[["a"]]
提示:
1 <= s.length <= 16
s 仅由小写英文字母组成
代码
class LeetCode131 {
public static void main(String[] args) {
//todo 回溯-backtrack
System.out.println(new Solution1().partition("aab"));
System.out.println(new Solution2().partition("aab"));
}
static class Solution1 {
public List> partition(String s) {
int len = s.length();
List> res = new ArrayList<>();
if (len == 0) {
return res;
}
// Stack 这个类 Java 的文档里推荐写成 Deque stack = new ArrayDeque();
// 注意:只使用 stack 相关的接口
Deque stack = new ArrayDeque<>();
backtracking(s, 0, len, stack, res);
return res;
}
/*
* @param s
* @param start 起始字符的索引
* @param len 字符串 s 的长度,可以设置为全局变量
* @param path 记录从根结点到叶子结点的路径
* @param res 记录所有的结果
*/
private void backtracking(String s, int start, int len, Deque path, List> res) {
if (start == len) {
res.add(new ArrayList<>(path));
return;
}
for (int i = start; i < len; i++) {
// 因为截取字符串是消耗性能的,因此,采用传子串索引的方式判断一个子串是否是回文子串
// 不是的话,剪枝
if (!checkPalindrome(s, start, i)) {
continue;
}
path.addLast(s.substring(start, i + 1));
backtracking(s, i + 1, len, path, res);
path.removeLast();
}
}
/*
* 这一步的时间复杂度是 O(N),因此,可以采用动态规划先把回文子串的结果记录在一个表格里
*
* @param str
* @param left 子串的左边界,可以取到
* @param right 子串的右边界,可以取到
* @return
*/
private boolean checkPalindrome(String str, int left, int right) {
// 严格小于即可
while (left < right) {
if (str.charAt(left) != str.charAt(right)) {
return false;
}
left++;
right--;
}
return true;
}
}
static class Solution2 {
public List> partition(String s) {
int len = s.length();
List> res = new ArrayList<>();
if (len == 0) {
return res;
}
// 预处理
// 状态:dp[i][j] 表示 s[i][j] 是否是回文
boolean[][] dp = new boolean[len][len];
// 状态转移方程:在 s[i] == s[j] 的时候,dp[i][j] 参考 dp[i + 1][j - 1]
for (int right = 0; right < len; right++) {
// 注意:left <= right 取等号表示 1 个字符的时候也需要判断
for (int left = 0; left <= right; left++) {
if (s.charAt(left) == s.charAt(right) && (right - left <= 2 || dp[left + 1][right - 1])) {
dp[left][right] = true;
}
}
}
Deque stack = new ArrayDeque<>();
backtracking(s, 0, len, dp, stack, res);
return res;
}
private void backtracking(String s,
int start,
int len,
boolean[][] dp,
Deque path,
List> res) {
if (start == len) {
res.add(new ArrayList<>(path));
return;
}
for (int i = start; i < len; i++) {
// 剪枝
if (!dp[start][i]) {
continue;
}
path.addLast(s.substring(start, i + 1));
backtracking(s, i + 1, len, dp, path, res);
path.removeLast();
}
}
}
}
LeetCode165 比较版本号
题目详情
给你两个版本号 version1 和 version2 ,请你比较它们。
版本号由一个或多个修订号组成,各修订号由一个 '.' 连接。每个修订号由 多位数字 组成,可能包含 前导零 。每个版本号至少包含一个字符。修订号从左到右编号,下标从 0 开始,最左边的修订号下标为 0 ,下一个修订号下标为 1 ,以此类推。例如,2.5.33 和 0.1 都是有效的版本号。
比较版本号时,请按从左到右的顺序依次比较它们的修订号。比较修订号时,只需比较 忽略任何前导零后的整数值 。也就是说,修订号 1 和修订号 001 相等 。如果版本号没有指定某个下标处的修订号,则该修订号视为 0 。例如,版本 1.0 小于版本 1.1 ,因为它们下标为 0 的修订号相同,而下标为 1 的修订号分别为 0 和 1 ,0 < 1 。
返回规则如下:
如果 version1 > version2 返回 1,
如果 version1 < version2 返回 -1,
除此之外返回 0。
示例 1:
输入:version1 = "1.01", version2 = "1.001"
输出:0
解释:忽略前导零,"01" 和 "001" 都表示相同的整数 "1"
示例 2:
输入:version1 = "1.0", version2 = "1.0.0"
输出:0
解释:version1 没有指定下标为 2 的修订号,即视为 "0"
示例 3:
输入:version1 = "0.1", version2 = "1.1"
输出:-1
解释:version1 中下标为 0 的修订号是 "0",version2 中下标为 0 的修订号是 "1" 。0 < 1,所以 version1 < version2
提示:
1 <= version1.length, version2.length <= 500
version1 和 version2 仅包含数字和 '.'
version1 和 version2 都是 有效版本号
version1 和 version2 的所有修订号都可以存储在 32 位整数 中
代码
class LeetCode165 {
public static void main(String[] args) {
System.out.println(new Solution().compareVersion("1.01", "1.001"));
System.out.println(new Solution2().compareVersion("1.01", "1.001"));
}
//方法一:分割+解析,两次遍历,线性空间
static class Solution {
public int compareVersion(String version1, String version2) {
String[] nums1 = version1.split("\\.");
String[] nums2 = version2.split("\\.");
int n1 = nums1.length, n2 = nums2.length;
// 比较块
int i1, i2;
for (int i = 0; i < Math.max(n1, n2); ++i) {
i1 = i < n1 ? Integer.parseInt(nums1[i]) : 0;
i2 = i < n2 ? Integer.parseInt(nums2[i]) : 0;
if (i1 != i2) {
return i1 > i2 ? 1 : -1;
}
}
// 版本相等
return 0;
}
}
//方法二:双指针,一次遍历,常数空间
static class Solution2 {
public Pair getNextChunk(String version, int n, int p) {
// 如果指针指向字符串末尾,返回0
if (p > n - 1) {
return new Pair(0, p);
}
// 找到块的结尾
int i, pEnd = p;
while (pEnd < n && version.charAt(pEnd) != '.') {
++pEnd;
}
// 检索块
if (pEnd != n - 1) {
i = Integer.parseInt(version.substring(p, pEnd));
} else {
i = Integer.parseInt(version.substring(p, n));
}
// 找到下一个块的开始
p = pEnd + 1;
return new Pair(i, p);
}
public int compareVersion(String version1, String version2) {
int p1 = 0, p2 = 0;
int n1 = version1.length(), n2 = version2.length();
// 比较版本
int i1, i2;
Pair pair;
while (p1 < n1 || p2 < n2) {
pair = getNextChunk(version1, n1, p1);
i1 = pair.first;
p1 = pair.second;
pair = getNextChunk(version2, n2, p2);
i2 = pair.first;
p2 = pair.second;
if (i1 != i2) {
return i1 > i2 ? 1 : -1;
}
}
// 版本相等
return 0;
}
}
}
LeetCode344 反转字符串
题目详情
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
示例 1:
输入:s = ["h","e","l","l","o"]
输出:["o","l","l","e","h"]
示例 2:
输入:s = ["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]
提示:
1 <= s.length <= 105
s[i] 都是 ASCII 码表中的可打印字符
代码
public class LeetCode344 {
public static void main(String[] args) {
//todo 双指针
char[] s = {'h', 'e', 'l', 'l', 'o'};
new Solution().reverseString(s);
System.out.print(s);
}
//反向双指针
static class Solution {
public void reverseString(char[] s) {
int left = 0, right = s.length - 1;
while (left < right) {
char tmp = s[left];
s[left++] = s[right];
s[right--] = tmp;
}
}
}
}