1.有效的括号
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
有效字符串需满足:左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
解题思路:
(1)判断字符串的长度是否为奇数,如果为奇数,返回false;
(2)遍历字符串,遇到‘{’、‘[’、‘(’入栈;
(3)如果栈顶元素与当前元素刚好匹配,将栈顶元素出栈;若不匹配,返回false;
(4)栈最后为空,返回true,否则false.
class Solution {
public boolean isValid(String s) {
if(s.length() % 2 == 1){
return false;
}
Stack<Character> stack = new Stack<>();
for(int i = 0; i < s.length(); i++){
char c = s.charAt(i);
if(c == '{' || c == '[' || c == '('){
stack.push(c);
}else{
if(stack.empty()){
return false;
}
char curchar = stack.peek();
if((curchar == '{' && c == '}') || (curchar == '(' && c == ')') || (curchar == '[' && c == ']')){
stack.pop();
}else{
return false;
}
}
}
return stack.empty();
}
}
运行结果:
2.最小栈
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
push(x) —— 将元素 x 推入栈中。
pop() —— 删除栈顶的元素。
top() —— 获取栈顶元素。
getMin() —— 检索栈中的最小元素。
解题思路:
设置一个辅助栈min_stack,保存最小元素。
push()方法: 每当push()新值进来时,如果 小于等于 min_stack栈顶值,则一起push()到min_stack,即更新了栈顶最小值;
pop()方法: 判断将pop()出去的元素值是否是min_stack栈顶元素值(即最小值),如果是则将min_stack栈顶元素一起pop(),这样可以保证min_stack栈顶元素始终是stack中的最小值。
getMin()方法: 返回min_stack栈顶即可。
class MinStack {
private Stack<Integer> stack;
private Stack<Integer> min_stack;
/** initialize your data structure here. */
public MinStack() {
stack = new Stack<>();
min_stack = new Stack<>();
}
public void push(int x) {
stack.push(x);
if(min_stack.isEmpty() || x<= min_stack.peek()){
min_stack.push(x);
}
}
public void pop() {
if(stack.pop().equals(min_stack.peek())){
min_stack.pop();
}
}
public int top() {
return stack.peek();
}
public int getMin() {
return min_stack.peek();
}
}
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(x);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/
运行结果:
3.用队列实现栈
使用队列实现栈的下列操作:
push(x) – 元素 x 入栈
pop() – 移除栈顶元素
top() – 获取栈顶元素
empty() – 返回栈是否为空注意:
你只能使用队列的基本操作-- 也就是 push to back, peek/pop from front, size, 和 is empty 这些操作是合法的。
你所使用的语言也许不支持队列。 你可以使用 list 或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。 你可以假设所有操作都是有效的(例如, 对一个空的栈不会调用 pop 或者 top 操作)。
解题思路:
使用top维护栈顶元素。考虑入栈时直接让元素加入到队列中,更新top的值。
弹出栈顶元素时,也就是要将队列中最后一个元素删除并返回。假设现在队列中有n个元素,将前n-1个元素依次出队再入队,此时要弹出的元素就在队列头部,此时调用q.remove()即可将栈顶元素弹出。不过这里要注意更新变量tail,因为弹出之后栈顶元素就变了。
class MyStack {
Queue<Integer> queue;
int top;
/** Initialize your data structure here. */
public MyStack() {
queue = new LinkedList<>();
}
/** Push element x onto stack. */
public void push(int x) {
queue.add(x);
top = x;
}
/** Removes the element on top of the stack and returns that element. */
public int pop() {
int size = queue.size();
for(int i = 0; i < size-2; i++){
queue.add(queue.remove());
}
top = queue.remove();
queue.add(top);
return queue.remove();
}
/** Get the top element. */
public int top() {
return top;
}
/** Returns whether the stack is empty. */
public boolean empty() {
return queue.isEmpty();
}
}
/**
* Your MyStack object will be instantiated and called as such:
* MyStack obj = new MyStack();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.top();
* boolean param_4 = obj.empty();
*/
运行结果:
4.用栈实现队列
使用栈实现队列的下列操作:
push(x) – 将一个元素放入队列的尾部。
pop() – 从队列首部移除元素。
peek() – 返回队列首部的元素。
empty() – 返回队列是否为空。
解题思路:
栈:后进先出;队列:先进先出。使用两个栈实现队列。
入队操作:栈的push操作。
出队操作:当stack2为空时,需要将stack1中所有元素弹出并放入stack2中,再将此时stack2中栈顶元素弹出即可。如果out不为空时,直接弹出其栈顶元素即可。
peek操作:与pop类似,只不过取元素时不删除栈中元素,而是返回stack2的栈顶元素。
判断是否为空:由于我们使用了两个栈来存放元素,所以必须两个栈都为空时才能判断队列为空。
lass MyQueue {
Stack<Integer> stack_1;
Stack<Integer> stack_2;
/** Initialize your data structure here. */
public MyQueue() {
stack_1 = new Stack<>();
stack_2 = new Stack<>();
}
/** Push element x to the back of queue. */
public void push(int x) {
stack_1.push(x);
}
/** Removes the element from in front of queue and returns that element. */
public int pop() {
if (stack_2.empty()) {
int size = stack_1.size();
for (int i = 0; i < size; i++)
stack_2.push(stack_1.pop());
}
return stack_2.pop();
}
/** Get the front element. */
public int peek() {
if (stack_2.empty()) {
int size = stack_1.size();
for (int i = 0; i < size; i++)
stack_2.push(stack_1.pop());
}
return stack_2.peek();
}
/** Returns whether the queue is empty. */
public boolean empty() {
return (stack_1.empty() && stack_2.empty());
}
}
运行结果:
5.下一个更大元素I
给定两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。找到 nums1 中每个元素在
nums2 中的下一个比其大的值。nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出 -1。
解题思路:
首先忽略数组 nums1,先将 nums2 中的每一个元素,求出其下一个更大的元素。随后将此元素与其更大元素放入哈希映射(HashMap)中,再遍历数组 nums1,并直接找出答案。对于 nums2,我们可以使用单调栈来解决这个问题。
首先把第一个元素 nums2[0] 放入栈,随后对于第二个元素 nums2[1],如果 nums2[1] > nums2[0],那么我们就找到了 nums2[0] 的下一个更大元素 nums2[1],此时就可以把 nums2[0] 出栈并把 nums2[1] 入栈;如果 nums2[1] <= nums2[0],我们就仅把 nums2[1] 入栈。对于第三个元素 nums2[2],此时栈中有若干个元素,那么所有比 nums2[2] 小的元素都找到了下一个更大元素(即 nums2[2]),因此可以出栈,在这之后,我们将 nums2[2] 入栈,以此类推。
class Solution {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
Stack<Integer> stack = new Stack<>();
Map<Integer,Integer> map = new HashMap<Integer,Integer>();
int[] res = new int[nums1.length];
for(int i = 0; i < nums2.length; i++){
while(!stack.empty() && nums2[i] > stack.peek()){
map.put(stack.pop(), nums2[i]);
}
stack.push(nums2[i]);
}
while(!stack.empty()){
map.put(stack.pop(), -1);
}
for(int i = 0; i < nums1.length; i++){
res[i] = map.get(nums1[i]);
}
return res;
}
}
运行结果:
6.棒球比赛
你现在是棒球比赛记录员。 给定一个字符串列表,每个字符串可以是以下四种类型之一:
1.整数(一轮的得分):直接表示您在本轮中获得的积分数。
2. “+”(一轮的得分):表示本轮获得的得分是前两轮有效 回合得分的总和。
3. “D”(一轮的得分):表示本轮获得的得分是前一轮有效 回合得分的两倍。
4. “C”(一个操作,这不是一个回合的分数):表示您获得的最后一个有效 回合的分数是无效的,应该被移除。每一轮的操作都是永久性的,可能会对前一轮和后一轮产生影响。 你需要返回你在所有回合中得分的总和。
解题思路:
用栈保存分数。如果遇到整数,直接将整数入栈,如果遇到“+”,将栈顶元素和下一个元素相加的和入栈;如果遇到“D”,将栈顶元素的2倍入栈;如果遇到“C”,将栈顶元素出栈。
class Solution {
public int calPoints(String[] ops) {
Stack<Integer> stack = new Stack<>();
for(int i = 0; i < ops.length; i++){
if(ops[i].equals("+")){
int top = stack.pop();
int newtop = top + stack.peek();
stack.push(top);
stack.push(newtop);
}else if(ops[i].equals("D")){
stack.push(stack.peek() * 2);
}else if(ops[i].equals("C")){
stack.pop();
}else{
stack.push(Integer.valueOf(ops[i]));
}
}
int sum = 0;
for(int a : stack){
sum += a;
}
return sum;
}
}
运行结果:
7.比较含退格的字符串
给定 S 和 T 两个字符串,当它们分别被输入到空白的文本编辑器后,判断二者是否相等,并返回结果。 # 代表退格字符。
注意:如果对空文本输入退格字符,文本继续为空。
解题思路:
利用栈来保存每次输入的字符。如果字符不等于‘#’,就入栈;否则(栈不为空)出栈。
class Solution {
public boolean backspaceCompare(String S, String T) {
return build(S).equals(build(T));
}
public String build(String S) {
Stack<Character> ans = new Stack();
for (char c: S.toCharArray()) {
if (c != '#')
ans.push(c);
else if (!ans.empty())
ans.pop();
}
return String.valueOf(ans);
}
}
运行结果:
8.删除最外层的括号
有效括号字符串为空 ("")、"(" + 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:遍历字符串,遇到左括号就入栈,遇到右括号就出栈,每次栈空的时候,都说明找到了一个原语,记录下每个原语的起始位置和结束位置,取原字符串在原语的起始位置+1到原语的结束位置的子串便得到原语删除了最外层括号的字符串,拼接,即可解出答案。
class Solution {
public String removeOuterParentheses(String S) {
int start = 0;
int end = 0;
boolean flag = false;
Stack<Character> stack = new Stack<>();
StringBuilder sb = new StringBuilder();
for(int i = 0; i < S.length(); i++){
char c = S.charAt(i);
if(c == '('){
stack.push(c);
if(!flag){
start = i;
flag = true;
}
}
if(c == ')'){
stack.pop();
if(stack.isEmpty()){
end = i;
sb.append(S.substring(start+1, end));
flag = false;
start = end;
}
}
}
return sb.toString();
}
}
思路2:不使用栈。
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();
}
}
运行结果:
9.删除字符串中的所有相邻重复项
给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
解题思路:
利用栈保存不重复的字符,如果当前字符与栈顶元素相同,则将栈顶元素出栈;如果不同则入栈。
class Solution {
public String removeDuplicates(String S) {
Stack<Character> stack = new Stack<>();
for(int i = 0; i < S.length(); i++){
char c = S.charAt(i);
if(stack.isEmpty() || c != stack.peek()){
stack.push(c);
}else {
stack.pop();
}
}
StringBuilder str = new StringBuilder();
for (Character c : stack) {
str.append(c);
}
return str.toString();
}
}
运行结果:
10.用栈操作构造数组
给你一个目标数组 target 和一个整数 n。每次迭代,需要从 list = {1,2,3…, n} 中依序读取一个数字。
请使用下述操作来构建目标数组 target :
Push:从 list 中读取一个新元素, 并将其推入数组中。
Pop:删除数组中的最后一个元素。
如果目标数组构建完成,就停止读取更多元素。题目数据保证目标数组严格递增,并且只包含 1 到 n 之间的数字。
请返回构建目标数组所用的操作序列。
解题思路:
建立两个指针i和j,i指向target数组,j指向list = {1,2,…,n}。
同时遍历target和list,当i指向的元素大于j指向的元素,说明此时j指向的元素在target中不需要,因此执行"Push"和"Pop",略去不需要的元素,同时j自增,直至i指向的元素不大于j指向的元素。当i指向的元素不大于j指向的元素(实为等于),说明此时j指向的元素正是target中需要的元素,因此执行"Push"。
i遍历至target末端退出。
class Solution {
public List<String> buildArray(int[] target, int n) {
List<String> list = new ArrayList<>();
int j = 1;
for(int i = 0; i < target.length;i++){
while(target[i] > j){
list.add("Push");
list.add("Pop");
j++;
}
list.add("Push");
j++;
}
return list;
}
}
运行结果: