代码随想录算法训练营第11天 | LeetCode 20. 有效的括号,1047. 删除字符串中的所有相邻重复项,150. 逆波兰表达式求值

LeetCode [20. 有效的括号]

题目:给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

示例 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. 1 <= S.length <= 20000
  2. 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=“”区别

  1. ""分配了内存;null没有分配内存。

  2. ""是一个字符串(String).它在内存中是存在的.而null它是一个空对象.在内存中是不存在的。

  3. ""占内存,在内存中会分配一个空间。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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值