0
点赞
收藏
分享

微信扫一扫

《Java初阶数据结构》----4.<线性表---Stack栈和Queue队列>

张宏涛心理 2024-07-28 阅读 32

前言

语言只是工具,不能决定你好不好找工作,决定你好不好找工作的是你的能力!!!!!

学历本科及以上就够用了!!!!!!!!!!!!!!!!!!!!!!


一、栈(stack)

1.1 栈的概念

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。

出栈:栈的删除操作叫做出栈。出数据在栈顶。 

 

1.2 栈的使用 

代码示例:

public static void main(String[] args) {
   Stack<Integer> s = new Stack();
   s.push(1);
   s.push(2);
   s.push(3);
   s.push(4);
   System.out.println(s.size());   // 获取栈中有效元素个数---> 4
   System.out.println(s.peek());   // 获取栈顶元素---> 4
   s.pop();   // 4出栈,栈中剩余1   2   3,栈顶元素为3
   System.out.println(s.pop());   // 3出栈,栈中剩余1 2   栈顶元素为3
   if(s.empty()){
       System.out.println("栈空");
  }else{
       System.out.println(s.size());
  }
}

1.3 栈的模拟实现

    private int[] elem;//定义一个数组来存储栈中元素
private int useSize;//记录栈中元素
private static final int DEFAULT_CAPACITY = 10;//定义一个静态常量
    public int getUseSize() {
return useSize;
}
    //构造一个容量为DEFAULT_CAPACITY的栈
public MyStack(){
this.elem = new int[DEFAULT_CAPACITY];
}
    //检测栈是否满了
private boolean isFull(){
return this.useSize == this.elem.length;
}
    //将val放入栈
public void push(int val){
if (isFull()){
this.elem = Arrays.copyOf(this.elem,2*this.elem.length);
}
this.elem[useSize++] = val;
}
    //将栈顶元素取出并返回
public int pop(){
if (isEmpty()){
throw new EmptyException("Stack为空!");
}
return elem[--useSize];
}
    //获取栈顶元素
public int peek(){
if (isEmpty()){
throw new EmptyException("Stack为空!");
}
return elem[useSize-1];
}
    //检测栈是否为空
private boolean isEmpty(){
return this.useSize == 0;
}

1.4 栈的常见编程题

1.有效的括号

    public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
//判断是否为有效的括号,具有先进后匹配的特点,因此我们用栈。首先创建一个栈
int len = s.length(); //首先得到字符串长度
if (len == 0) { //如果字符串为空,则返回true
return true;
}
if (len % 2 == 1) { //括号成双成对,因此如果字符串为奇数,那么直接返回false
return false;
} else { //如果为偶数,符合预期则,将字符串转字符数组。遍历这个字符数组
char[] chars = s.toCharArray();
for (char ch : chars
) {
if (ch == '(' || ch == '[' || ch == '{') { //如果为左括号,则入栈。
stack.push(ch);
}
if (!stack.empty()) { //如果有左括号,到这里栈一定不为空。如果栈为空,则返回false,因为先得有左括号才会是有效括号
//接下来判断右括号,如果遍历到右括号,那么必有栈顶元素与之配对才会是有效括号,并出栈栈顶元素。否则返回false。
if (ch == '}') {
if (stack.pop() != '{') {
return false;
}
}
if (ch == ']') {
if (stack.pop() != '[') {
return false;
}
}
if (ch == ')') {
if (stack.pop() != '(') {
return false;
}
}
} else {
return false;
}
}
}
//最终判断栈是否为空,若全是左括号,那么就没有出栈。因此如果栈内有元素则为false。若匹配成功
//栈为空,返回true
return stack.empty();
}

2.逆波兰表达式求值

import java.util.Stack;
//运算方法是将数字入栈,如果碰到运算符号。则出栈将第一个出栈元素放在运算符右边,第二个出栈元素放入运算符左边
//计算这个结果,并将这个计算结果入栈。重复以上操作。即可计算出逆波兰表达式的值。
public class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
int right;
int left;
for (String token:tokens
) {
switch (token){
case "+":
stack.push(stack.pop()+stack.pop());
break;
case "-":
right = stack.pop();
left = stack.pop();
stack.push(left-right);
break;
case "*":
stack.push(stack.pop()*stack.pop());
break;
case "/":
right = stack.pop();
left = stack.pop();
stack.push(left/right);
break;
default:stack.push(Integer.parseInt(token)); //注意这里放入栈的时候要将字符串转整型类型
}
}
return stack.peek();
}
}

3.栈的压入、弹出序列 

import java.util.Stack;
public class Solution {
public boolean IsPopOrder(int [] pushA,int [] popA) {
int n = pushA.length;
//辅助栈
Stack<Integer> s = new Stack<>();
//遍历入栈的下标
int j = 0;
//遍历出栈的数组
for(int i = 0; i < n; i++){
//入栈:栈为空或者栈顶不等于出栈数组
while(j < n && (s.isEmpty() || s.peek() != popA[i])){
s.push(pushA[j]);
j++;
}
//栈顶等于出栈数组
if(s.peek() == popA[i])
s.pop();
//不匹配序列
else
return false;
}
return true;
}
}

4.最小栈

class MinStack {
Deque<Integer> xStack;
Deque<Integer> minStack;

public MinStack() {
xStack = new LinkedList<Integer>();
minStack = new LinkedList<Integer>();
minStack.push(Integer.MAX_VALUE);
}

public void push(int x) {
xStack.push(x);
minStack.push(Math.min(minStack.peek(), x));
}

public void pop() {
xStack.pop();
minStack.pop();
}

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

public int getMin() {
return minStack.peek();
}
}

1.5栈的应用场景

将递归转化为循环

比如:逆序打印链表

// 递归方式
void printList(Node head){
   if(null != head){
       printList(head.next);
       System.out.print(head.val + " ");
  }
}
// 循环方式
void printList(Node head){
   if(null == head){
       return;
  }
   
   Stack<Node> s = new Stack<>();
   // 将链表中的结点保存在栈中
   Node cur = head;
   while(null != cur){
       s.push(cur);
       cur = cur.next;
  }
   // 将栈中的元素出栈
   while(!s.empty()){
       System.out.print(s.pop().val + " ");
  }
}

二、队列 

2.1队列的概念

 

 

2.2 队列的使用 

public static void main(String[] args) {
   Queue<Integer> q = new LinkedList<>();
   q.offer(1);
   q.offer(2);
   q.offer(3);
   q.offer(4);
   q.offer(5);                  // 从队尾入队列
   System.out.println(q.size());
   System.out.println(q.peek());  // 获取队头元素
   
   q.poll();
   System.out.println(q.poll());  // 从队头出队列,并将删除的元素返回
   
   if(q.isEmpty()){
       System.out.println("队列空");
  }else{
       System.out.println(q.size());
  }
}

2.3 队列模拟实现

 

    static class ListNode{
private int val;
private ListNode prev;
private ListNode next;

public ListNode(int val){
this.val = val;
}

private ListNode front;//队头
private ListNode rear;//队尾

private int useSize;//队员数

}
    public int getUseSize() {
return useSize;
}
    //入队操作,相当于头插法
public void offer(int x){
ListNode node = new ListNode(x);
if(front == null){
front = rear = node;
}else {
node.next = front;
front.prev = node;
front = node;
}
useSize++;
}
    //出队操作,相当于删除尾节点
public int poll(){
if(rear == null){
return -1;
}
int ret = rear.val;
if(front == rear){
front = null;
rear = null;
return ret;
}
rear = rear.prev;
rear.next = null;
useSize--;
return ret;
}
    //获取队头元素
public int peek(){
if(front == null){
return -1;
}
return front.val;
}
    //检测队列是否为空
public boolean isEmpty(){
return this.useSize == 0;
}

2.4 循环队列

 

数组下标循环的小技巧  

 

 

 三、双端队列 (Deque)

 

Deque<Integer> stack = new ArrayDeque<>();//双端队列的线性实现
Deque<Integer> queue = new LinkedList<>();//双端队列的链式实现

 2.5设计循环队列

class MyCircularQueue {
private int front;
private int rear;
private int capacity;
private int[] elements;

public MyCircularQueue(int k) {
capacity = k + 1;
elements = new int[capacity];
rear = front = 0;
}

public boolean enQueue(int value) {
if (isFull()) {
return false;
}
elements[rear] = value;
rear = (rear + 1) % capacity;
return true;
}

public boolean deQueue() {
if (isEmpty()) {
return false;
}
front = (front + 1) % capacity;
return true;
}

public int Front() {
if (isEmpty()) {
return -1;
}
return elements[front];
}

public int Rear() {
if (isEmpty()) {
return -1;
}
return elements[(rear - 1 + capacity) % capacity];
}

public boolean isEmpty() {
return rear == front;
}

public boolean isFull() {
return ((rear + 1) % capacity) == front;
}
}

四、面试题 

1.用队列实现栈

class MyStack {
Queue<Integer> queue1;
Queue<Integer> queue2;

/** Initialize your data structure here. */
public MyStack() {
queue1 = new LinkedList<Integer>();
queue2 = new LinkedList<Integer>();
}

/** Push element x onto stack. */
public void push(int x) {
queue2.offer(x);
while (!queue1.isEmpty()) {
queue2.offer(queue1.poll());
}
Queue<Integer> temp = queue1;
queue1 = queue2;
queue2 = temp;
}

/** Removes the element on top of the stack and returns that element. */
public int pop() {
return queue1.poll();
}

/** Get the top element. */
public int top() {
return queue1.peek();
}

/** Returns whether the stack is empty. */
public boolean empty() {
return queue1.isEmpty();
}
}

2.用栈实现队列

 

class MyQueue {
Deque<Integer> inStack;
Deque<Integer> outStack;

public MyQueue() {
inStack = new ArrayDeque<Integer>();
outStack = new ArrayDeque<Integer>();
}

public void push(int x) {
inStack.push(x);
}

public int pop() {
if (outStack.isEmpty()) {
in2out();
}
return outStack.pop();
}

public int peek() {
if (outStack.isEmpty()) {
in2out();
}
return outStack.peek();
}

public boolean empty() {
return inStack.isEmpty() && outStack.isEmpty();
}

private void in2out() {
while (!inStack.isEmpty()) {
outStack.push(inStack.pop());
}
}
}
举报

相关推荐

0 条评论