LeetCode [20. 有效的括号]
题目:给定一个只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串 s
,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
示例 1:
输入:s = "()"
输出:true
示例 2:
输入:s = "()[]{}"
输出:true
示例 3:
输入:s = "(]"
输出:false
提示:
1 <= s.length <= 104
s
仅由括号'()[]{}'
组成
class Solution {
public boolean isValid(String s) {
Deque<Character> deque = new LinkedList();
char c;
// 如果s的长度为奇数,一定不符合要求
if(s.length() % 2 != 0) return false;
for(int i = 0;i < s.length();i++){
c = s.charAt(i);
//碰到左括号,就把相应的右括号入栈
if(c == '('){
deque.push(')');
}
else if(c == '['){
deque.push(']');
}
else if(c == '{'){
deque.push('}');
}
// 第三种情况:遍历字符串匹配的过程中,栈已经为空了,
// 没有匹配的字符了,说明右括号没有找到对应的左括号 return false
// 第二种情况:遍历字符串匹配的过程中,发现栈里没有我们要匹配的字符。所以return false
else if(deque.isEmpty() || deque.peek() != c){
return false;
}
//当前字符与栈顶元素为一对括号,将栈顶元素出栈
else{
deque.pop();
}
}
// 第一种情况:此时我们已经遍历完了字符串,但是栈不为空
// 说明有相应的左括号没有右括号来匹配,所以return false,否则就return true
return deque.isEmpty();
}
}
LeetCode [1047. 删除字符串中的所有相邻重复项]
题目:给出由小写字母组成的字符串 S
,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
示例:
输入:"abbaca"
输出:"ca"
解释:
例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。
提示:
1 <= S.length <= 20000
S
仅由小写英文字母组成。
思路:
//栈方法
class Solution {
public String removeDuplicates(String s) {
Deque<Character> deque = new ArrayDeque<>();
char c;
//遍历字符串
for(int i = 0;i < s.length();i++){
c = s.charAt(i);
//当栈为空或者栈顶元素和此时c不相等,就把该字母压入栈中
if(deque.isEmpty() || deque.peek() != c){
deque.push(c);
//否则就把该栈顶元素弹出
}else{
deque.pop();
}
}
String str = "";
//当栈不为空时
while(!deque.isEmpty()){
//输出内容为栈中元素依次弹出
str = deque.pop() + str;
}
return str;
}
}
//直接把字符串作为栈
class Solution {
public String removeDuplicates(String s) {
StringBuilder sb = new StringBuilder();
// top为 sb 的长度,作为栈顶
int top = -1;
char c;
for(int i = 0;i < s.length();i++){
c = s.charAt(i);
// 当 top > 0,即栈中有字符时,当前字符如果和栈中字符相等,弹出栈顶字符,同时 top--
if(top >= 0 && sb.charAt(top) == c){
sb.deleteCharAt(top);
top--;
// 否则,将该字符 入栈,同时top++
}else{
sb.append(c);
top++;
}
}
return sb.toString();
}
}
扩展:String str=null与String str=“”的区别;String str=""与new String()的区别
1. String str=null与String str=“”的区别
String str=null
这句话的意思就是定义一个字符串,变量str,字符串的内容为空值。
String str=“”
定义一个String类型的变量str,并为其赋值。
String str=null与String str=“”区别
-
""分配了内存;null没有分配内存。
-
""是一个字符串(String).它在内存中是存在的.而null它是一个空对象.在内存中是不存在的。
-
""占内存,在内存中会分配一个空间。null不占内存. 为空引用.
String str1= null; str引用为空
String str2= “”; str应用一个空字符串
注意null不是对象,""是对象。
2. String str=""与new String()的区别
- 2.1 String str=“”
String str = "hello world";
1
在编译期间,先检查常量池中是否存在"hello world",如果不存在,就在常量池中开辟一个空间来存储"hello world";如果存在,就不用新开辟空间。在栈内存中开辟一个名字为str的空间,来存储"hello world"在常量池中的地址值。
public static void main(String[] args) {
String str1 = "hello world";
String str2 = "hello world";
System.out.println(str1 == str2);
}
12345
注意!这里就是上面说的,从常量池中直接取用,所以,两者引用地址应该也相同,即 == 比较结果为true。
- 2.2 new String()
String str =new String("hello world");
在编译期间,先检查常量池中是否存在"hello world",如果不存在,就在常量池中开辟一个空间来存储"hello world";如果存在,就不用新开辟空间。在运行期间,将常量池中的"hello world"复制一份存放到堆中,在栈内存中开辟一个名字为str的空间,来存储"hello world"在堆中的地址值。
public static void main(String[] args) {
String str1 =new String("hello world");
String str2 =new String("hello world");
System.out.println(str1 == str2);
}
注意!这里这里和上面不一样,这里直接在堆里创建对象,每新建一个都会创建一个,所以,两者引用地址肯定不相同,即 == 比较为false。
3. 引申概念
- 1.1 常量池
用来存放一些常量,该常量是在编译期被确定,并被保存在已编译的.class文件中,其中包括了类,方法,接口等包含的数值常量,字符常量和字符串常量。
- 1.2 字符串常量池
在常量池中,有个专门用来存储字符串常量的,称之为字符串常量池。当我们需要使用字符串时,首先会在字符串常量池中查找是否存在该字符串,若存在则直接进行使用;若不存在,则会新建一个对应的字符串,并保存在该字符串常量池中。
- 1.3 在编译期间创建的常量和运行期间创建的常量保存的地址
对象的引用都是存放在栈中,编译期间创建的常量保存在常量池中;
运行期间通过new创建时,会直接创建一个新字符串常量并会存储在堆中。所以在堆中可以有很多重复相同的字符串,而在常量池中,不会出现重复的值。
LeetCode [150. 逆波兰表达式求值]
题目:根据 逆波兰表示法,求表达式的值。
有效的算符包括 +
、-
、*
、/
。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
注意 两个整数之间的除法只保留整数部分。
可以保证给定的逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
示例 1:
输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
示例 2:
输入:tokens = ["4","13","5","/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
示例 3:
输入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
输出:22
解释:该算式转化为常见的中缀算术表达式为:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22
提示:
1 <= tokens.length <= 104
tokens[i]
是一个算符("+"
、"-"
、"*"
或"/"
),或是在范围[-200, 200]
内的一个整数
逆波兰表达式:
逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。
- 平常使用的算式则是一种中缀表达式,如
( 1 + 2 ) * ( 3 + 4 )
。 - 该算式的逆波兰表达式写法为
( ( 1 2 + ) ( 3 4 + ) * )
。
逆波兰表达式主要有以下两个优点:
- 去掉括号后表达式无歧义,上式即便写成
1 2 + 3 4 + *
也可以依据次序计算出正确结果。 - 适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中
思路:
class Solution {
public int evalRPN(String[] tokens) {
Deque<Integer> deque = new LinkedList();
String s;
for(int i = 0;i < tokens.length;i++){
s = tokens[i];
if("+".equals(s)){
deque.push(deque.pop() + deque.pop());
}else if("-".equals(s)){
deque.push(-deque.pop() + deque.pop());
}else if("*".equals(s)){
deque.push(deque.pop() * deque.pop());
}else if("/".equals(s)){
int temp1 = deque.pop();
int temp2 = deque.pop();
deque.push(temp2 / temp1);
}else{
deque.push(Integer.parseInt(s));
}
}
return deque.pop();
}
}
扩展:Integer.parseInt(s)与Integer.valueOf(s)的区别
一、Integer.parseInt(s)用法
String s1 = "1000";
String s2 = "1000";
int n1 = Integer.parseInt(s1);
int n2 = Integer.parseInt(s2);
if (n1 == n2) {
System.out.println("Integer.parseInt(s1) == Integer.parseInt(s2)");
}
输出:
Integer.parseInt(s1) == Integer.parseInt(s2)
Integer.parseInt(s)的作用就是把字符串s解析成有符号的int基本类型。
二、Integer.valueOf(s)用法
String s = "123";
Integer integer = Integer.valueOf(s);
System.out.println("integer : " + integer);
Integer.valueOf(s)把字符串s解析成Integer对象类型,返回的integer 可以调用对象中的方法。
三、Integer.parseInt(s)与Integer.valueOf(s)的区别
Integer.parseInt(s)
Integer.parseInt(s)多次解析同一个字符串得到的int基本类型数据是相等的,可以直接通过“==”进行判断是否相等。
String s = "10000";
if (Integer.parseInt(s) == Integer.parseInt(s)) { //true
System.out.println("Integer.parseInt(s) == Integer.parseInt(s)");
}
输出:
Integer.parseInt(s) == Integer.parseInt(s)
int是基本类型,不含有equals方法,所以只能用“”比较,基本类型用“”比较的是两个值的大小。
Integer.valueOf(s)
Integer.valueOf(s)多次解析相同的一个字符串时,得到的是Integer类型的对象,得到的对象有时是同一个对象,有时是不同的对象,要根据把s字符串解析的整数值的大小进行决定:如果s字符串对应的整数值在 -128127之间,则解析出的Integer类型的对象是同一个对象;如果s字符串对应的整数值不在-128127之间,则解析出的Integer类型的对象不是同一个对象。不管对象是否相等,对象中的value值是相等的。
String s = "100";
Integer i1 = Integer.valueOf(s);
Integer i2 = Integer.valueOf(s);
if (i1 == i2) { //两个对象相等
System.out.println("i1 == i2");
}
if (i1.equals(i2)) { //两个对象中的value值相等
System.out.println("i1.equals(i2)");
}
输出:
i1 == i2
i1.equals(i2)
通过上面示例,字符串s对应的整数值为100,在-128~127之间,所以解析出的两个对象i1和i2是相等的。equals是比较的两个对象i1和i2中的value值是否相等,“==”是比较i1和i2两个对象是否相等。
当s字符串对应的整数值不在-128~127之间,示例如下:
String s = "1000";
Integer i1 = Integer.valueOf(s);
Integer i2 = Integer.valueOf(s);
if (i1 != i2) { //两个对象不相等
System.out.println("i1 != i2");
}
if (i1.equals(i2)) { //两个对象中的value值相等
System.out.println("i1.equals(i2)");
}
输出:
i1 != i2
i1.equals(i2)
可见,当s字符串对应的整数值为1000,不在-128~127之间,通过Integer.valueOf(s)解析出的两个对象i1和i2是不同的对象,对象中的value值是相同的。
原因: 为什么Integer.valueOf(s)会出现这种情况呢?这是由于JDK中源码已经定义好的。由于在-128127之间的整数值用的比较频繁,当每次要创建一个value值在-128127之间的Integer对象时,直接从缓存中拿到这个对象,所以value值相同的Integer对象都是对应缓存中同一个对象。-128~127之外的整数值用的不是太频繁,每次创建value值相同的Integer对象时,都是重新创建一个对象,所以创建的对象不是同一个对象。这个从JDK中源码可以看出
public static Integer valueOf(String s) throws NumberFormatException {
return Integer.valueOf(parseInt(s, 10));
}
然后上面的源码又调用了Integer.valueOf(int)方法
public static Integer valueOf(int i) {
/*IntegerCache.low为 -128;
IntegerCache.high默认为127,但可以在JVM进行配置,一般默认就是127*/
if (i >= IntegerCache.low && i <= IntegerCache.high)
/*如果i在-128~127之间,从缓存中取对象*/
return IntegerCache.cache[i + (-IntegerCache.low)];
/*如果i不在-128~127之间,重新创建一个对象*/
return new Integer(i);
}
四、Integer.parseInt(s)与Integer.valueOf(s)的联系
Integer.parseInt(s)是把字符串解析成int基本类型,Integer.valueOf(s)是把字符串解析成Integer对象类型,其实int就是Integer解包装,Integer就是int的包装,在jdk8中已经自动实现了自动解包装和自动包装,所以两种方式都能得到想要的整数值。
把int类型包装成Integer类型
Integer i = 1000; //自动把1000类型包装成Integer类型
Integer i2 = 1000;
if (i != i2) {
System.out.println("i != i2");
}
if (i.equals(i2)) {
System.out.println("i.equals(i2)");
}
输出:
i != i2
i.equals(i2)
把Integer类型自动解包装成int类型
Integer n = new Integer(100);
int n1 = n;
System.out.println("n1 = " + n1);123
输出:
i.equals(i2)
n1 = 100