java数据结构与算法刷题目录(剑指Offer、LeetCode、ACM)-----主目录-----持续更新(进不去说明我没写完):https://blog.csdn.net/grd_java/article/details/123063846 |
---|
- 思路分析
- 使用栈,这里我决定只用一个栈
- 因此决定倒着遍历字符串,只入栈"]"和字母。当遇到数字就重复字符串
- 重复完成后,出栈"]",然后将重复的字符串入栈
- 最终返回结果
- 代码
class Solution {
/**
* api
* str.charAt(index) 获取str字符串的指定下标位置
* Character.isDigit(cur) 判断cur是否是一个数字,如果是返回true
* Character.isLetter(cur) 判断cur是否是字母
* LinkedList.addLast(e) 添加元素e到末尾,
* LinkedList.peekLast() 返回末尾元素,没有返回null
* LinkedList.removeLast() 移除并返回末尾元素
* Collections.reverse(sub) 反转Collections容器sub,例如List
*/
int ptr;//字符串遍历指针
//倒着遍历字符串
public String decodeString(String s) {
//用栈
LinkedList<String> letterStack = new LinkedList<>();
ptr = s.length()-1;
while(ptr>=0){//遍历字符串
char c = s.charAt(ptr);
if(Character.isLetter(c)||c==']'){//如果是']'或者是字母,直接入栈
letterStack.addLast(String.valueOf(c));
ptr--;
}else if(Character.isDigit(c)){//如果是数字需要进行处理
Integer digits = getDigits(s);
StringBuffer stringBuffer = new StringBuffer();
String s1 = letterStack.removeLast();
while(!"]".equals(s1)){
stringBuffer.append(s1);
s1 = letterStack.removeLast();
}
while(digits!=0){
digits--;
letterStack.addLast(stringBuffer.toString());
}
}else{//如果是'['直接跳过,因为'['的前一个字符必定是数字,例如2[sss]的2
ptr--;
}
}
return getString(letterStack);
}
//倒着遍历字符串,并将数字取出合并,合并结果为正常顺序,
//比如1024,倒着遍历是4201,最终返回结果依然要1024
//需要考虑0开头的情况,比如100,倒着遍历是001,最终返回结果依然要100,而不是1
private Integer getDigits(String s){
//处理开头0的情况,比如120这样的数字,倒着遍历是021,无法直接还原成120
//因此先处理0,一个0就是10,两个0就是100
//然后处理非0,最终结果乘以处理的0就可以了
int zeroFlag =1;
while(ptr>=0&&s.charAt(ptr)=='0'){
zeroFlag*=10;
ptr--;
}
int ret = 0;//用于获取完整的数字
while(ptr>=0&&Character.isDigit(s.charAt(ptr))){
// 前面取到的数是高位,获取完整的数需要*10,然后+新的数,
//最后-'0'是让其变成十进制,而不是以ASCII码返回
ret = ret * 10 + s.charAt(ptr--)-'0';
}
//1024===>>4201 反转数字
int result = 0;
while(ret % 10 != ret){
result *= 10;
result += ret%10;
ret = ret/10;
}
result = result * 10 + ret;
return result * zeroFlag;
}
private String getString(LinkedList<String> v){
StringBuffer ret = new StringBuffer();
while(v.size()!=0){
ret.append(v.removeLast());
}
return ret.toString();
}
}
刷题一定要坚持,总结套路,不单单要把题做出来,要举一反三,也要参考别人的思路,学习别人解题的优点,找出你觉得可以优化的点。
- 单链表解题思路:双指针、快慢指针、反转链表、预先指针
- 双指针:对于单链表而言,可以方便的让我们遍历结点,并做一些额外的事
- 快慢指针:常用于找链表中点,找循环链表的循环点,一般快指针每次移动两个结点,慢指针每次移动一个结点。
- 反转链表:通常有些题,将链表反转后会更好做,一般选用三指针迭代法,递归的空间复杂度有点高
- 预先指针:常用于找结点,比如找倒数第3个结点,那么定义两个指针,第一个指针先移动3个结点,然后两个指针一起遍历,当第一个指针遍历完成,第二个指针指向的结点就是要找的结点
- 数组解题思路:双指针、三指针,下标标记
- 双指针:多用于减少时间复杂度,快速遍历数组
- 三指针:多用于二分查找,分为中间指针,左和右指针
- 下标标记:常用于在数组范围内找东西,而不想使用额外的空间的情况,比如找数组长度为n,元素取值范围为[1,n]的数组中没有出现的数字,遍历每个元素,然后将对应下标位置的元素变为负数或者超出[1,n]范围的正数,最后没有发生变化的元素,就是缺少的值。
- 栈解题思路:倒着入栈,双栈
- 倒着入栈:适用于出栈时想让输出是正序的情况。比如字符串’abc’,如果倒着入栈,那么栈中元素是(c,b,a)。栈是先进后出,此时出栈,结果为abc。
- 双栈:适用于实现队列的先入先出效果。一个栈负责输入,另一个栈负责输出。