java数据结构与算法刷题目录(剑指Offer、LeetCode、ACM)-----主目录-----持续更新(进不去说明我没写完):https://blog.csdn.net/grd_java/article/details/123063846 |
---|
- 思路分析
除了直接用if判断,还有LeetCode官方题解的自动机deterministic finite
automaton, DFA解法
- 代码
class Solution {
//方法1,直接判断,时间复杂度O(n),但是这种方法非常容易把代码写的很冗余
public static int myAtoi(String s) {
if(s.length()==0) return 0;
int res =0;
int i = 0;//下标
int flag = 1;//是否是负数,1表示正数
//先丢弃无用前导空格
while(i<s.length()&&s.charAt(i)==' '){i++;}
//如果有符号位,看是不是负号
if(i<s.length()&&s.charAt(i)=='-')flag = -1;
//如果有符号位,下标后移
if(i<s.length()&&(s.charAt(i)=='+' || s.charAt(i)=='-'))i++;
//进行遍历转换,直到下一个非数字字符,或达到输出结尾
while(i<s.length()&&Character.isDigit(s.charAt(i))){
int r = s.charAt(i)-'0';//转换为int型
//处理越界,我们需要判断下一次是否越界,也就是Integer.MAX_VALUE/10
//需要额外处理2147483646这种比2147483647这个最大数小了几个值的情况
//因为2147483647/10会取整。这时应该输出2147483646,而不是2147483647。
//也就是r这个数,如果<=7其实是不越界的,但是res == Integer.MAX_VALUE/10这个条件却是成了的
//因此需要加判断res == Integer.MAX_VALUE/10 && r>7
if(res>Integer.MAX_VALUE/10 || (res == Integer.MAX_VALUE/10 && r>7)){
return flag>0?Integer.MAX_VALUE:Integer.MIN_VALUE;
}
res = res*10 + r;
i++;
}
return flag>0?res:-res;
}
//方法二:自动机,LeetCode官方题解给出的,时间复杂度O(n),逻辑更简单,但是资源消耗多一丢丢,在意排名的同学就用上面的方法喽
//典型的编译原理里面的知识。把整个字符串自动化
//首先是start状态,如果是空格,就继续start状态
//遇到数字,进入in_number状态,遇到"+/-"号,进入signed状态,signed状态处理完,进入in_number状态
//如果直接遇到其它字符,直接进入end状态
public static int myAtoi1(String s) {
Automaton automaton = new Automaton();
int length = s.length();
for (int i = 0; i < length; ++i) {
automaton.get(s.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;
}
}
刷题一定要坚持,总结套路,不单单要把题做出来,要举一反三,也要参考别人的思路,学习别人解题的优点,找出你觉得可以优化的点。
- 单链表解题思路:双指针、快慢指针、反转链表、预先指针
- 双指针:对于单链表而言,可以方便的让我们遍历结点,并做一些额外的事
- 快慢指针:常用于找链表中点,找循环链表的循环点,一般快指针每次移动两个结点,慢指针每次移动一个结点。
- 反转链表:通常有些题,将链表反转后会更好做,一般选用三指针迭代法,递归的空间复杂度有点高
- 预先指针:常用于找结点,比如找倒数第3个结点,那么定义两个指针,第一个指针先移动3个结点,然后两个指针一起遍历,当第一个指针遍历完成,第二个指针指向的结点就是要找的结点
- 数组解题思路:双指针、三指针,下标标记
- 双指针:多用于减少时间复杂度,快速遍历数组
- 三指针:多用于二分查找,分为中间指针,左和右指针
- 下标标记:常用于在数组范围内找东西,而不想使用额外的空间的情况,比如找数组长度为n,元素取值范围为[1,n]的数组中没有出现的数字,遍历每个元素,然后将对应下标位置的元素变为负数或者超出[1,n]范围的正数,最后没有发生变化的元素,就是缺少的值。
- 栈解题思路:倒着入栈,双栈
- 倒着入栈:适用于出栈时想让输出是正序的情况。比如字符串’abc’,如果倒着入栈,那么栈中元素是(c,b,a)。栈是先进后出,此时出栈,结果为abc。
- 双栈:适用于实现队列的先入先出效果。一个栈负责输入,另一个栈负责输出。