【Leetcode刷题笔记】栈

目录

    • 一、有效的括号
    • 二、最小栈
    • 三、用队列实现栈
    • 四、用栈实现队列
    • 五、下一个更大的元素Ⅰ
    • 六、棒球比赛
    • 七、比较含退格的字符串
    • 八、删除最外层的括号
    • 九、删除字符串中的所有相邻重复字符

一、有效的括号

【题号】20
【题目描述】
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。

示例 1:
输入: “()”
输出: true
示例 2:
输入: “()[]{}”
输出: true
示例 3:
输入: “(]”
输出: false
示例 4:
输入: “([)]”
输出: false
示例 5:
输入: “{[]}”
输出: true
【常规解法】
1.初始化栈 S。
2.一次处理表达式的每个括号。
3.如果遇到开括号,我们只需将其推到栈上即可。这意味着我们将稍后处理它,让我们简单地转到前面的 子表达式。
4.如果我们遇到一个闭括号,那么我们检查栈顶的元素。如果栈顶的元素是一个 相同类型的 左括号,那么我们将它从栈中弹出并继续处理。否则,这意味着表达式无效。
5.如果到最后我们剩下的栈中仍然有元素,那么这意味着表达式无效。
【我的代码】

class Stack(object):
    def __init__(self, limit=10):
        self.stack = [] #存放元素
        self.limit = limit #栈容量极限
    def push(self, data): #判断栈是否溢出
        if len(self.stack) >= self.limit:
            print('StackOverflowError')
            pass
        self.stack.append(data)
    def pop(self):
        if self.stack:
            return self.stack.pop()
        else:
            #raise IndexError('pop from an empty stack') #空栈不能被弹出
            return False
    def peek(self): #查看堆栈的最上面的元素
        if self.stack:
            return self.stack[-1]
    def is_empty(self): #判断栈是否为空
        return not bool(self.stack)
    def size(self): #返回栈的大小
        return len(self.stack)


class Solution(object):
    def isValid(self, s):
        """
        :type s: str
        :rtype: bool
        """
        stack = Stack(len(s))
        for c in s:
            if (c == '(') or (c=='{') or (c=='['):
                stack.push(c)
            elif c==')':
                if(stack.pop()!='('):
                    return False
            elif c=='}':
                if(stack.pop()!='{'):
                    return False
            elif c==']':
                if(stack.pop()!='['):
                    return False
            else:
                return False
        if stack.is_empty():
            return True
        else:
            return False

【执行情况】
执行用时36ms rank 23.13%
消耗内存11.7MB rank 99.23%
【范例代码】

class Solution(object):
    def isValid(self, s):
        """
        :type s: str
        :rtype: bool
        """

        # The stack to keep track of opening brackets.
        stack = []

        # Hash map for keeping track of mappings. This keeps the code very clean.
        # Also makes adding more types of parenthesis easier
        mapping = {")": "(", "}": "{", "]": "["}

        # For every bracket in the expression.
        for char in s:

            # If the character is an closing bracket
            if char in mapping:

                # Pop the topmost element from the stack, if it is non empty
                # Otherwise assign a dummy value of '#' to the top_element variable
                top_element = stack.pop() if stack else '#'

                # The mapping for the opening bracket in our hash and the top
                # element of the stack don't match, return False
                if mapping[char] != top_element:
                    return False
            else:
                # We have an opening bracket, simply push it onto the stack.
                stack.append(char)

        # In the end, if the stack is empty, then we have a valid expression.
        # The stack won't be empty for cases like ((()
        return not stack

【分析】
1.我做这道题的时候专门定义了一个栈类,其实直接定义一个stack数组即可。直接使用stack.pop()以及stack.append()进行出栈和入栈操作,使用stack和not stack进行栈是否为空的判断。
2.我穷举了所有左右闭合的情况进行判断,判断语句很笨重。由于每一个右括号,都对应了一个专属的左括号,所以更好的方法是使用哈希表使用if char in mapping判断哈希表中是否有索引char,此处对应的是判断char是否为某右括号,如果有,则mapping[char]即为对应的左括号。
【拓展】
1.时间复杂度
定义了三个数组。数组1为常规的栈,数组2为左括号数组,数组3为括号对数组
通过与数组2比对,判断新输入字符是否是左括号,直接进栈。若不是左括号且栈不为空,则将栈顶字符与新输入的字符拼接,通过与数组3比对,判断是否能组成正确的括号对,若是,则将栈顶出栈。以上都不满足时即为输入错误。字符串穷尽后,通过判断栈中是否还有元素决定输出结果。
这里的栈不为空保证了后半句的数组不越界。即避免栈空时,错误输入一个右括号的情况
范例:时间消耗4ms

class Solution(object):
    def isValid(self, s):     
        stack, s1, s2 = [], ['(','[','{'], ['()','[]','{}']
        for n in s:
            if n in s1: stack.append(n)
            elif stack and stack[-1]+n in s2: stack.pop()
            else: return False
        return stack == []

二、最小栈

【题号】115
【题目描述】
设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。
push(x) – 将元素 x 推入栈中。
pop() – 删除栈顶的元素。
top() – 获取栈顶元素。
getMin() – 检索栈中的最小元素。

示例:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.
【常规解法】
构建辅助栈,“以空间换时间”
辅助栈栈顶为当前最小值。
(一)同步辅助栈
辅助栈元素个数和数据栈元素个数相同。
辅助栈中的元素完全对应了数据栈中每一个元素入栈时的最小值。并将判断放在入栈阶段。
每当数据栈中入栈新元素时,判断新入栈的元素是否比目前最小值要小,若小则替换当前最小值,并在辅助栈中同步入栈最小值。
每当数据栈中出栈元素时,辅助栈同步出栈。
(二)非同步辅助栈
辅助栈元素个数和数据栈元素个数不相同。
入栈时,为最小值才同步入栈;出栈时,为最小值才同步出栈。
每当数据栈中入栈新元素时,若新元素小于或等于当前最小值,则替换最小值,并将该元素入辅助栈。
每当数据栈中出栈元素时,若出栈元素等于当前最小值时,辅助栈同步出栈。
(此处的或等于容易漏)
【我的代码】

class MinStack(object):

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.stack=[]


    def push(self, x):
        """
        :type x: int
        :rtype: None
        """
        self.stack.append(x)


    def pop(self):
        """
        :rtype: None
        """
        if self.stack:
            return self.stack.pop()
        else:
            return False


    def top(self):
        """
        :rtype: int
        """
        if self.stack:
            return self.stack[-1]
        else:
            return False


    def getMin(self):
        """
        :rtype: int
        """
        if self.stack:
            minEle=self.stack[0]
        else:
            return False
        for temp in self.stack:
            if temp<minEle:
                minEle=temp
        return minEle

【执行情况】
在这里插入图片描述
【范例代码】

class MinStack:

    # 辅助栈和数据栈同步
    # 思路简单不容易出错

    def __init__(self):
        # 数据栈
        self.data = []
        # 辅助栈
        self.helper = []

    def push(self, x):
        self.data.append(x)
        if len(self.helper) == 0 or x <= self.helper[-1]:
            self.helper.append(x)
        else:
            self.helper.append(self.helper[-1])

    def pop(self):
        if self.data:
            self.helper.pop()
            return self.data.pop()

    def top(self):
        if self.data:
            return self.data[-1]

    def getMin(self):
        if self.helper:
            return self.helper[-1]

作者:liweiwei1419
链接:https://leetcode-cn.com/problems/min-stack/solution/shi-yong-fu-zhu-zhan-tong-bu-he-bu-tong-bu-python-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class MinStack:

    # 辅助栈和数据栈不同步
    # 关键 1:辅助栈的元素空的时候,必须放入新进来的数
    # 关键 2:新来的数小于或者等于辅助栈栈顶元素的时候,才放入(特别注意这里等于要考虑进去)
    # 关键 3:出栈的时候,辅助栈的栈顶元素等于数据栈的栈顶元素,才出栈,即"出栈保持同步"就可以了

    def __init__(self):
        # 数据栈
        self.data = []
        # 辅助栈
        self.helper = []

    def push(self, x):
        self.data.append(x)
        # 关键 1 和关键 2
        if len(self.helper) == 0 or x <= self.helper[-1]:
            self.helper.append(x)

    def pop(self):
        # 关键 3:【注意】不论怎么样,数据栈都要 pop 出元素
        top = self.data.pop()

        if self.helper and top == self.helper[-1]:
            self.helper.pop()
        return top

    def top(self):
        if self.data:
            return self.data[-1]

    def getMin(self):
        if self.helper:
            return self.helper[-1]

作者:liweiwei1419
链接:https://leetcode-cn.com/problems/min-stack/solution/shi-yong-fu-zhu-zhan-tong-bu-he-bu-tong-bu-python-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

【分析】
我做这道题的时候,有想过应该不是在输出最小值时才遍历栈,但没想到怎么样在入栈或出栈阶段做判断。本质上来说,没想到引入了一个新的辅助栈来参与判断
本道题辅助栈分为同步栈和非同步栈两种解法,同步栈判断简单但空间消耗略高,非同步栈省了部分的空间消耗但其增加的判断也提高了时间开销。
对于同步辅助栈,辅助栈中的元素完全对应了数据栈中每一个元素入栈时的最小值。并将判断放在入栈阶段。
对于非同步辅助栈,入栈时,为最小值才同步入栈;出栈时,为最小值才同步出栈。
更推荐使用同步栈
【拓展】
若不使用辅助栈,还可以使用一个单独的变量存储最小值。而当最小值发生变化时有如下两种操作方式:最小值入栈和存储差值
(一)最小值入栈
当入栈时,新的元素值小于当前最小值,则替换最小值,并将原先的最小值也压入数据栈
当出栈时,出栈的元素等于当前最小值,则再出栈一个元素,并将新出栈的元素作为新的最小值
演示:
【Leetcode刷题笔记】栈_第1张图片【Leetcode刷题笔记】栈_第2张图片
范例:

class MinStack {
    int min = Integer.MAX_VALUE;
    Stack<Integer> stack = new Stack<Integer>();
    public void push(int x) {
        //当前值更小
        if(x <= min){   
            //将之前的最小值保存
            stack.push(min);
            //更新最小值
            min=x;
        }
        stack.push(x);
    }

    public void pop() {
        //如果弹出的值是最小值,那么将下一个元素更新为最小值
        if(stack.pop() == min) {
            min=stack.pop();
        }
    }

    public int top() {
        return stack.peek();
    }

    public int getMin() {
        return min;
    }
}
作者:windliang
链接:https://leetcode-cn.com/problems/min-stack/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-38/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

(二)存储差值
栈中存储的值为原先元素值与当时最小值之间的差值。
演示:
【Leetcode刷题笔记】栈_第3张图片
范例:

public class MinStack {
    long min;
	Stack<Long> stack;

	public MinStack(){
        stack=new Stack<>();
    }

	public void push(int x) {
		if (stack.isEmpty()) {
			min = x;
			stack.push(x - min);
		} else {
			stack.push(x - min);
			if (x < min){
				min = x; // 更新最小值
			}
				
		}
	}

	public void pop() {
		if (stack.isEmpty())
			return;

		long pop = stack.pop();
		
		//弹出的是负值,要更新 min
		if (pop < 0) {
			min = min - pop;
		}

	}

	public int top() {
		long top = stack.peek();
		//负数的话,出栈的值保存在 minif (top < 0) {
			return (int) (min);
        //出栈元素加上最小值即可
		} else {
			return (int) (top + min);
		}
	}

	public int getMin() {
		return (int) min;
	}
}

作者:windliang
链接:https://leetcode-cn.com/problems/min-stack/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-38/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

三、用队列实现栈

【题号】225
【题目描述】
使用队列实现栈的下列操作:
push(x) – 元素 x 入栈
pop() – 移除栈顶元素
top() – 获取栈顶元素
empty() – 返回栈是否为空
注意:
你只能使用队列的基本操作-- 也就是 push to back, peek/pop from front, size, 和 is empty 这些操作是合法的。
你所使用的语言也许不支持队列。 你可以使用 list 或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
你可以假设所有操作都是有效的(例如, 对一个空的栈不会调用 pop 或者 top 操作)。
【常规解法】
双队列解法一:出栈时调整
定义两个辅助队列 queue1 与 queue2,使用一个变量 top_element 记录栈顶元素。
push():将元素入队 queue1
pop():
将 queue1 内所有元素全部出队,除最后一个元素外,其余入队 queue2,而后删除最后一个元素并返回
更新 top_element

调换 queue1 与 queue2
top():返回 top_elenemt
empty():判断 queue1 的长度
双队列解法二:入栈时调整
定义两个辅助队列 queue1 与 queue2,使用一个变量 top_element 记录栈顶元素。
push():
将元素入队 queue2,且将该元素定义为栈顶元素
更新 top_element
此时若 queue1 不为空,则让 queue1 中的元素逐个出队并加入 queue2 中

交换queue1和queue2
pop(): queue1 首个元素出队,更新 top_element
top(): 返回 top_element
empty(): 判断 queue1 长度
单队列解法:入栈反序操作
定义辅助队列 queue。
push():
将元素入队
除新入队元素,将其他元素从队首取出,再从队尾入队(完成反序)。此时队首元素即为新入队元素
pop():queue 首个元素出队
top():获取 queue 首个元素
empty():判断 queue 长度
【我的代码】

class MyStack(object):

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.queen=[]    #用list存放队列
        self.topEle=0    #存放栈顶元素
        


    def push(self, x):
        """
        Push element x onto stack.
        :type x: int
        :rtype: None
        """
        self.queen.append(x)
        self.topEle=x   #在入栈时 新入列的元素设为栈顶元素


    def pop(self):
        """
        Removes the element on top of the stack and returns that element.
        :rtype: int
        """
        if self.queen:                             #   在出栈时   
            for num in range(0,len(self.queen)-2): #1)对队列中前N-2个元素进行翻转
                self.queen.append(self.queen[0])
                del self.queen[0]
            if len(self.queen)!=1:                 #2)对第N-1个元素设为新的栈顶元素
                self.topEle=self.queen[0]
                del self.queen[0]
                self.queen.append(self.topEle)
            output=self.queen[0]                   #3)出列第N个元素
            del self.queen[0]
            return output


    def top(self):
        """
        Get the top element.
        :rtype: int
        """
        if self.queen:
            return self.topEle
        else:
            return False


    def empty(self):
        """
        Returns whether the stack is empty.
        :rtype: bool
        """
        if self.queen:
            return False
        else:
            return True



# Your MyStack object will be instantiated and called as such:
# obj = MyStack()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.top()
# param_4 = obj.empty()

【执行情况】
执行用时12ms rank 96.15%
消耗内存11.7MB rank 90.07%
【范例代码】

"""
双队列解法一:出栈时调整
"""
class MyStack:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.queue1 = []
        self.queue2 = []
        self.top_element = 0
        
    def push(self, x: int) -> None:
        """
        Push element x onto stack.
        """
        self.queue1.append(x)
        self.top_element = x

    def pop(self) -> int:
        """
        Removes the element on top of the stack and returns that element.
        """
        # 把 queue1 里的元素取出,留下一个,其余塞入 queue2 中
        length1 = len(self.queue1)
        for i in range(length1 - 1):
            item = self.queue1[0]
            del self.queue1[0]
            self.queue2.append(item)
        self.top = item
        target = self.queue1[0]
        del self.queue1[0]
        # 交换 queue1 与 queue2
        self.queue1 = self.queue2
        self.queue2 = []
        return target

    def top(self) -> int:
        """
        Get the top element.
        """
        # length1 = len(self.queue1)
        # for i in range(length1):
        #     item = self.queue1[0]
        #     del self.queue1[0]
        #     self.queue2.append(item)
        # self.queue1 = self.queue2
        # self.queue2 = []
        # return item
        return self.top_element

    def empty(self) -> bool:
        """
        Returns whether the stack is empty.
        """
        return len(self.queue1) == 0


# Your MyStack object will be instantiated and called as such:
# obj = MyStack()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.top()
# param_4 = obj.empty()

"""
双队列解法二:入栈时调整
"""
class MyStack:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.queue1 = []
        self.queue2 = []
        self.top_element = 0


    def push(self, x: int) -> None:
        """
        Push element x onto stack.
        """
        # 更新栈顶元素
        self.top_element = x
        # 加入 queue2 中
        self.queue2.append(x)
        if not self.empty():
            # 如果 queue1 不为空,取出元素并加入 queue2
            length1 = len(self.queue1)
            for i in range(length1):
                self.queue2.append(self.queue1[0])
                del self.queue1[0]
        # 交换
        self.queue1 = self.queue2
        self.queue2 = []


    def pop(self) -> int:
        """
        Removes the element on top of the stack and returns that element.
        """
        target = self.queue1[0]
        del self.queue1[0]
        # 更新 top_element
        if not self.empty():
            self.top_element = self.queue1[0]
        return target


    def top(self) -> int:
        """
        Get the top element.
        """
        return self.top_element


    def empty(self) -> bool:
        """
        Returns whether the stack is empty.
        """
        return len(self.queue1) == 0


# Your MyStack object will be instantiated and called as such:
# obj = MyStack()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.top()
# param_4 = obj.empty()

"""
单队列解法:入栈反序操作
"""
class MyStack:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.queue = []


    def push(self, x: int) -> None:
        """
        Push element x onto stack.
        """
        self.queue.append(x)
        # 队列反序
        length = len(self.queue)
        for i in range(length - 1):
            first = self.queue[0]
            del self.queue[0]
            self.queue.append(first)


    def pop(self) -> int:
        """
        Removes the element on top of the stack and returns that element.
        """
        target = self.queue[0]
        del self.queue[0]
        return target


    def top(self) -> int:
        """
        Get the top element.
        """
        return self.queue[0]


    def empty(self) -> bool:
        """
        Returns whether the stack is empty.
        """
        return len(self.queue) == 0


# Your MyStack object will be instantiated and called as such:
# obj = MyStack()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.top()
# param_4 = obj.empty()

作者:jalan
链接:https://leetcode-cn.com/problems/implement-stack-using-queues/solution/yong-dui-lie-shi-xian-zhan-de-san-chong-jie-fa-pyt/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

【分析】
这道题有两种工具和操作方式。可以构建辅助栈并交换,或者直接对栈本身进行一个反序,我感觉本质上都差不多,时间开销也差不多,而且直接做反序更节省空间。操作方式分为在入栈时判断和出栈时判断,本质上也差不多。我选用了直接对栈做反序,并且在出栈时判断。

四、用栈实现队列

【题号】232
【题目描述】
使用栈实现队列的下列操作:
push(x) – 将一个元素放入队列的尾部。
pop() – 从队列首部移除元素。
peek() – 返回队列首部的元素。
empty() – 返回队列是否为空。
示例:
MyQueue queue = new MyQueue();
queue.push(1);
queue.push(2);
queue.peek(); // 返回 1
queue.pop(); // 返回 1
queue.empty(); // 返回 false
说明:
你只能使用标准的栈操作 – 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。
【常规解法】
建立两个栈,一个栈专门用来入队,一个栈专门用来出队
入队时所有元素都放入入队栈中,入队栈中第一个元素设为备选队首元素
出队时,若出队栈非空,则将入队栈元素依次倒入出队栈中,则出队栈中第一个元素即为需要出队的队首元素。
返回队首元素时,若出队栈不为空,则返回出队栈的栈首元素,否则返回入队栈中的备选队首元素
【我的代码】

class MyQueue(object):

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.stack=[]           #存储队列元素的栈
        self.stackAss=[]        #辅助栈
        self.peekEle=0          #队首元素


    def push(self, x):
        """
        Push element x to the back of queue.
        :type x: int
        :rtype: None
        """
        if not self.stack:      #如果队中无元素 则新入队的元素设为队首元素
            self.peekEle=x
        self.stack.append(x)
        


    def pop(self):
        """
        Removes the element from in front of queue and returns that element.
        :rtype: int
        """
        if self.stack:              
            for i in range(0,len(self.stack)-2):                #栈中前n-2个元素放入辅助栈
                self.stackAss.append(self.stack.pop())
            self.peekEle=self.stack.pop()                       #栈中第n-1个元素设为新的队首元素并放入辅助栈
            if self.stack:                                      #出栈第n个元素 并出队
                self.stackAss.append(self.peekEle)              
                self.output=self.stack.pop()
            else:
                self.output=self.peekEle
            if self.stackAss:                                   #将辅助栈中的元素再放回队列栈重
                for i in range(0,len(self.stackAss)):
                    self.stack.append(self.stackAss.pop())
            return self.output
        else:
            return False


    def peek(self):
        """
        Get the front element.
        :rtype: int
        """
        if self.stack:
            return self.peekEle
        else:
            return False


    def empty(self):
        """
        Returns whether the queue is empty.
        :rtype: bool
        """
        if self.stack:
            return False
        else:
            return True



# Your MyQueue object will be instantiated and called as such:
# obj = MyQueue()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.peek()
# param_4 = obj.empty()

【执行情况】
在这里插入图片描述
【范例代码】

private Stack<Integer> s1 = new Stack<>();
private Stack<Integer> s2 = new Stack<>();

// Push element x to the back of queue.
public void push(int x) {
    if (s1.empty())
        front = x;
    s1.push(x);
}
// Removes the element from in front of queue.
public void pop() {
    if (s2.isEmpty()) {
        while (!s1.isEmpty())
            s2.push(s1.pop());
    }
    s2.pop();    
}
// Return whether the queue is empty.
public boolean empty() {
    return s1.isEmpty() && s2.isEmpty();
}
// Get the front element.
public int peek() {
    if (!s2.isEmpty()) {
        return s2.peek();
    }
    return front;
}

【分析】
用栈实现队列,至少需要两个栈。
我的做法是,将一个栈作为队列栈,存储着队列元素,另一个栈只是在出栈时候起一个辅助作用。入队时正常入栈,出队时,将所有元素都倒序到辅助栈中,提取完第一个元素,再把辅助栈的元素正序回队列栈。这样效率很低,时间复杂度较大。而且我没有把判断放在倒序到辅助栈后再做,而是一边倒序一边做,判断变得繁琐了。
更好的做法是,一个栈专门用来入队,一个栈专门用来出队。入队栈中所有元素都是倒序的,可以正常入队;出队栈中所有元素都是正序的,可以正常出队。二者在出入队时不需要每次都做栈之间的传递,各司其责即可。直到出队栈为空,才再把入队栈的元素倒腾过来。
我对于本题的解法时仿照上一题(用队列来实现栈)的,可以用确实是可以用,但时间复杂度较高。而本题提出的新解法,其均摊复杂度只有O(1)。

五、下一个更大的元素Ⅰ

【题号】496
【题目描述】
给定两个没有重复元素的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。找到 nums1 中每个元素在 nums2 中的下一个比其大的值。
nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出-1。
示例 1:
输入: nums1 = [4,1,2], nums2 = [1,3,4,2].
输出: [-1,3,-1]
解释:
对于num1中的数字4,你无法在第二个数组中找到下一个更大的数字,因此输出 -1。
对于num1中的数字1,第二个数组中数字1右边的下一个较大数字是 3。
对于num1中的数字2,第二个数组中没有下一个更大的数字,因此输出 -1。
示例 2:
输入: nums1 = [2,4], nums2 = [1,2,3,4].
输出: [3,-1]
解释:
对于num1中的数字2,第二个数组中的下一个较大数字是3。
对于num1中的数字4,第二个数组中没有下一个更大的数字,因此输出 -1。
注意:
nums1和nums2中所有元素是唯一的。
nums1和nums2 的数组大小都不超过1000。
【常规解法】
先建立一个哈希表,key为nums2中的每一个元素,value为该元素对应的下一个更大数字。之后再遍历num1去寻找哈希表中对应的value。
对于哈希表的建立,用的是单调栈的方法。
对nums2遍历入栈。
若栈中无元素,则直接入栈。
若栈中有元素,且即将入栈的元素比栈顶元素大,则出栈栈顶元素,存入哈希表。*其中key为栈顶元素,value为即将入站元素。*原栈顶元素出栈后,再次比较待入栈元素与新的栈顶元素,直到待入栈元素也大于栈顶元素时,入栈。
若num2遍历完后,栈中仍然存在元素,则将栈中元素全部出栈,key为栈中元素,value为-1。
由于整个操作过程中,新入栈元素都小于原栈顶元素,所以这是一个由小到大的单调栈。
【我的代码】

class Solution(object):
    def nextGreaterElement(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: List[int]
        """
        """
        nums3=[]               							#待输出的list
        for num in nums1:      							#遍历nums1 
            isExist=False								
            for i in range(0,len(nums2)):
                if num==nums2[i]:						#遍历寻找nums2中对应的num1元素
                    i=i+1
                    while(inum):
                            isExist=True
                            nums3.append(nums2[i])
                            break
                        i=i+1
                    break
            if not isExist:
                nums3.append(-1)
        return nums3
        """
        nums3=[]  										#待输出的list
        stack=[]										#单调栈
        mapping={}										#哈希表
        for num in nums2:								#将num2元素依次尝试入栈
            while(stack  and stack[-1]<num):            #若待入栈元素大于栈顶元素 
                mapping.update({stack.pop():num})		#则表栈顶元素出栈且入哈希表
            stack.append(num)
        while stack:									#nums2全部入栈后 单调栈中的剩余元素全部入表
            mapping.update({stack.pop():-1})
        for num in nums1:
            nums3.append(mapping[num])
        return nums3

【执行情况】
在这里插入图片描述
在这里插入图片描述
【范例代码】

class Solution:
    def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
        stack, hashmap = list(), dict()
        for i in nums2:
            while len(stack) != 0 and stack[-1] < i:hashmap[stack.pop()] = i
            stack.append(i)
        return [hashmap.get(i,-1) for i in nums1]

作者:qsctech-sange
链接:https://leetcode-cn.com/problems/next-greater-element-i/solution/python3-wu-xing-dai-ma-di-jian-zhan-ha-xi-biao-by-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

【分析】
这道题,我先用了纯粹遍历得笨方法,也能运行得通,不过消耗时间较大。
我参考了利用单调栈建立哈希表的方法,写了第二种解法,运行效率大大提高。
范例代码中一个地方值得注意,就是其return的写法:return [hashmap.get(i,-1) for i in nums1]。比我再去将遍历完nums2的单调栈重新入表,建立完哈希表再遍历num1去生成新的输出list,要简洁且省事。

六、棒球比赛

【题号】682
【题目描述】
你现在是棒球比赛记录员。
给定一个字符串列表,每个字符串可以是以下四种类型之一:
1.整数(一轮的得分):直接表示您在本轮中获得的积分数。
2. “+”(一轮的得分):表示本轮获得的得分是前两轮有效 回合得分的总和。
3. “D”(一轮的得分):表示本轮获得的得分是前一轮有效 回合得分的两倍。
4. “C”(一个操作,这不是一个回合的分数):表示您获得的最后一个有效 回合的分数是无效的,应该被移除。
每一轮的操作都是永久性的,可能会对前一轮和后一轮产生影响。
你需要返回你在所有回合中得分的总和。
示例 1:
输入: [“5”,“2”,“C”,“D”,"+"]
输出: 30
解释:
第1轮:你可以得到5分。总和是:5。
第2轮:你可以得到2分。总和是:7。
操作1:第2轮的数据无效。总和是:5。
第3轮:你可以得到10分(第2轮的数据已被删除)。总数是:15。
第4轮:你可以得到5 + 10 = 15分。总数是:30。
示例 2:
输入: [“5”,"-2",“4”,“C”,“D”,“9”,"+","+"]
输出: 27
解释:
第1轮:你可以得到5分。总和是:5。
第2轮:你可以得到-2分。总数是:3。
第3轮:你可以得到4分。总和是:7。
操作1:第3轮的数据无效。总数是:3。
第4轮:你可以得到-4分(第三轮的数据已被删除)。总和是:-1。
第5轮:你可以得到9分。总数是:8。
第6轮:你可以得到-4 + 9 = 5分。总数是13。
第7轮:你可以得到9 + 5 = 14分。总数是27。
【常规解法】
让我们在处理数据时保持栈上每个有效回合的值。栈是理想的,因为我们只处理涉及最后或倒数第二轮的操作。
【我的代码】

class Solution(object):
    def calPoints(self, ops):
        """
        :type ops: List[str]
        :rtype: int
        """
        stack=[]
        for string in ops:
            if string.isdigit():
                stack.append(int(string))
            elif string[0]=='-' and string[1:].isdigit():
                stack.append(int(string[1:])*-1)
            elif string=="+" and len(stack)>1:
                num1=stack.pop()
                num2=stack.pop()
                stack.append(num2)
                stack.append(num1)
                stack.append(num1+num2)
            elif string=="D" and stack:
                num=stack.pop()
                stack.append(num)
                stack.append(2*num)
            elif string=="C" and stack:
                stack.pop()
            else:
                return False
        output=0
        for score in stack:
            output=output+score
        return output

【执行情况】
在这里插入图片描述
【范例代码】

class Solution(object):
    def calPoints(self, ops):
        stack = []
        for op in ops:
            if op == '+':
                stack.append(stack[-1] + stack[-2])
            elif op == 'C':
                stack.pop()
            elif op == 'D':
                stack.append(2 * stack[-1])
            else:
                stack.append(int(op))

        return sum(stack)

作者:LeetCode
链接:https://leetcode-cn.com/problems/baseball-game/solution/bang-qiu-bi-sai-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

【分析】
这道题本身的做法比较简单。只是涉及到栈顶和次栈顶元素的调用处理而已。
不过范例代码有我需要注意的地方,调用栈顶和次栈顶元素可以直接使用stack[-1]和stack[-2],计算栈和可以直接用sum(stack)

七、比较含退格的字符串

【题号884】
【题目描述】
给定 S 和 T 两个字符串,当它们分别被输入到空白的文本编辑器后,判断二者是否相等,并返回结果。 # 代表退格字符。
示例 1:
输入:S = “ab#c”, T = “ad#c”
输出:true
解释:S 和 T 都会变成 “ac”。
示例 2:
输入:S = “ab##”, T = “c#d#”
输出:true
解释:S 和 T 都会变成 “”。
示例 3:
输入:S = “a##c”, T = “#a#c”
输出:true
解释:S 和 T 都会变成 “c”。
示例 4:
输入:S = “a#c”, T = “b”
输出:false
解释:S 会变成 “c”,但 T 仍然是 “b”。
【常规解法】
使用 build(S) 和 build(T) 构造去除了退格符和被删除字符后的字符串,然后比较它们是否相等。
在 build(S) 中,使用栈存储每次输入的字符。
【我的代码】

class Solution(object):
    def backspaceCompare(self, S, T):
        """
        :type S: str
        :type T: str
        :rtype: bool
        """
        stackS,stackT=[],[]
        for char in S:
            if char == '#' :
                if stackS:
                    stackS.pop()
            else:
                stackS.append(char)
        for char in T:
            if char == '#':
                if stackT:
                    stackT.pop()
            else:
                stackT.append(char)
        return stackS==stackT

【执行情况】
在这里插入图片描述
【范例代码】

class Solution(object):
    def backspaceCompare(self, S, T):
        def build(S):
            ans = []
            for c in S:
                if c != '#':
                    ans.append(c)
                elif ans:
                    ans.pop()
            return "".join(ans)
        return build(S) == build(T)

作者:LeetCode
链接:https://leetcode-cn.com/problems/backspace-string-compare/solution/bi-jiao-han-tui-ge-de-zi-fu-chuan-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

【分析】
我的思路和范例基本是一样的。主要是代码方面可以做一点改进。
我的判断逻辑是,(如果是# (如果非空 (出栈) ))(否则 入栈)
范例的判断逻辑是,(如果不是# 入栈)(又如果非空 出栈)
其实本质上没有增加多少判断的时间,但我是有嵌套判断,看着有点累赘。
【拓展】
反向遍历字符串,如果遍历到一个退格符,那么再往左第一个非退格字符将会被删除,剩余未被删除的字符就是最终的字符串。
这里使用到了迭代器。空间复杂度较小。

class Solution(object):
    def backspaceCompare(self, S, T):
        def F(S):
            skip = 0
            for x in reversed(S):
                if x == '#':
                    skip += 1
                elif skip:
                    skip -= 1
                else:
                    yield x

        return all(x == y for x, y in itertools.izip_longest(F(S), F(T)))

作者:LeetCode
链接:https://leetcode-cn.com/problems/backspace-string-compare/solution/bi-jiao-han-tui-ge-de-zi-fu-chuan-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

八、删除最外层的括号

【题号】1021
【题目描述】
有效括号字符串为空 ("")、"(" + A + “)” 或 A + B,其中 A 和 B 都是有效的括号字符串,+ 代表字符串的连接。例如,"","()","(())()" 和 “(()(()))” 都是有效的括号字符串。
如果有效字符串 S 非空,且不存在将其拆分为 S = A+B 的方法,我们称其为原语(primitive),其中 A 和 B 都是非空有效括号字符串。
给出一个非空有效字符串 S,考虑将其进行原语化分解,使得:S = P_1 + P_2 + … + P_k,其中 P_i 是有效括号字符串原语。
对 S 进行原语化分解,删除分解中每个原语字符串的最外层括号,返回 S 。
示例 1:
输入:"(()())(())"
输出:"()()()"
解释:
输入字符串为 “(()())(())”,原语化分解得到 “(()())” + “(())”,
删除每个部分中的最外层括号后得到 “()()” + “()” = “()()()”。
示例 2:
输入:"(()())(())(()(()))"
输出:"()()()()(())"
解释:
输入字符串为 “(()())(())(()(()))”,原语化分解得到 “(()())” + “(())” + “(()(()))”,
删除每隔部分中的最外层括号后得到 “()()” + “()” + “()(())” = “()()()()(())”。
示例 3:
输入:"()()"
输出:""
解释:
输入字符串为 “()()”,原语化分解得到 “()” + “()”,
删除每个部分中的最外层括号后得到 “” + “” = “”。
【常规解法】
利用栈或标志符统计左右括号,当栈空/左右括号数量相等时,则表明最外层的括号已出现。
【我的代码】

class Solution(object):
    def removeOuterParentheses(self, S):
        """
        :type S: str
        :rtype: str
        """
        leftNum=0
        output=""
        for char in S:
            if char=='(':
                leftNum=leftNum+1
                if leftNum>1:
                    output=output+char
            else:
                leftNum=leftNum-1
                if leftNum:
                    output=output+char
        return output

【执行情况】
在这里插入图片描述
【范例代码】

class Solution {
    public String removeOuterParentheses(String S) {
        StringBuilder sb = new StringBuilder();
        int level = 0;
        for (char c : S.toCharArray()) {
            if (c == ')') --level;
            if (level >= 1) sb.append(c);
            if (c == '(') ++level;
        }
        return sb.toString();
    }
}

作者:huaouo
链接:https://leetcode-cn.com/problems/remove-outermost-parentheses/solution/jian-ji-de-java-shi-xian-by-huaouo/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution(object):
    def removeOuterParentheses(self, S):
        """
        :type S: str
        :rtype: str
        """
        i=0
        lnum=0
        rnum=0
        m=''
        while i<len(S):
            if S[i] == '(':
                lnum+=1
            if S[i] == ')':
                rnum+=1
            if lnum == rnum:
                k=S[i-lnum-rnum+2:i]
                m=m+k
                lnum=0
                rnum=0
            i+=1
        return m



作者:qin-ken-de-xiao-ma-nong-2
链接:https://leetcode-cn.com/problems/remove-outermost-parentheses/solution/python-whilexun-huan-qiu-jie-by-qin-ken-de-xiao-ma/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

【分析】
这道题的思路并不复杂。不过在代码方面还是有可以借鉴的地方。
1)第一个范例代码中用了三个单层判断,代替了我的两个双层判断。我的双层判断是:若左括号则自增,后又如果大于一执行;若右括号则自减,后大于零执行。可以直接用若右括号则自减;若大于零则执行;若左括号则自增。 主要是刚好我的第二层判断只差一个1
2)第二个范例中,是已经出现了最外层的括号时,直接把中间的放进去,而我是非最外层括号的每一个括号都放进去,执行次数比较多。k=S[i-lnum-rnum+2:i]m=m+k

九、删除字符串中的所有相邻重复字符

【题号】1047
【题目描述】
给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
示例:
输入:“abbaca”
输出:“ca”
解释:
例如,在 “abbaca” 中,我们可以删除 “bb” 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 “aaca”,其中又只有 “aa” 可以执行重复项删除操作,所以最后的字符串为 “ca”。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-all-adjacent-duplicates-in-string
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
【常规解法】
方法一:替换函数
我们可以用字符串自带的替换函数,由于字符串仅包含小写字母,因此只有 26 种不同的重复项。
将 aa 到 zz 的 26 种重复项放入集合中;
遍历这 26 种重复项,并用字符串的替换函数把重复项替换成空串。
注意,在进行过一次替换之后,可能会出现新的重复项。例如对于字符串 abbaca,如果替换了重复项 bb,字符串会变为 aaca,出现了新的重复项 aa。因此,上面的过程需要背重复若干次,直到字符串在一整轮替换过程后保持不变(即长度不变)为止
方法二:栈
我们可以用栈来维护没有重复项的字母序列:
若当前的字母和栈顶的字母相同,则弹出栈顶的字母;
若当前的字母和栈顶的字母不同,则放入当前的字母。
【我的代码】

class Solution(object):
    def removeDuplicates(self, S):
        """
        :type S: str
        :rtype: str
        """
        stack=[]
        for char in S:
            if stack and stack[-1]==char:
                stack.pop()
                else:
                    stack.append(char)
            else:
                stack.append(char)
        return "".join(stack)

【执行情况】
在这里插入图片描述
【范例代码】

#替换函数
from string import ascii_lowercase
class Solution:
    def removeDuplicates(self, S: str) -> str:
        # generate 26 possible duplicates
        duplicates = {2 * ch for ch in ascii_lowercase}
        
        prev_length = -1
        while prev_length != len(S):
            prev_length = len(S)
            for d in duplicates:
                S = S.replace(d, '')
                
        return S

#栈
class Solution:
    def removeDuplicates(self, S: str) -> str:
        output = []
        for ch in S:
            if output and ch == output[-1]: 
                output.pop()
            else: 
                output.append(ch)
        return ''.join(output)

作者:LeetCode
链接:https://leetcode-cn.com/problems/remove-all-adjacent-duplicates-in-string/solution/shan-chu-zi-fu-chuan-zhong-de-suo-you-xiang-lin-zh/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

【分析】
我的代码使用的是方法二,用栈的方法进行重复元素的出栈。这个方法的思路和代码都相对简单高效。
范例中提到了使用字符串替代函数的方法,虽然时间复杂度更高了,不过这个思路还是可以参考一下。利用字符表生成重复字符表duplicates = {2 * ch for ch in ascii_lowercase};遍历重复字符表,若一次完整遍历后,原字符串长度发生改变,则再次遍历重复字符表S = S.replace(d, ‘’)

你可能感兴趣的:(【Leetcode刷题笔记】栈)