pursue wind pursue wind
首页
Java
Python
数据库
框架
Linux
中间件
前端
计算机基础
DevOps
项目
面试
书
关于
归档
MacOS🤣 (opens new window)
GitHub (opens new window)
首页
Java
Python
数据库
框架
Linux
中间件
前端
计算机基础
DevOps
项目
面试
书
关于
归档
MacOS🤣 (opens new window)
GitHub (opens new window)
  • 技术面试题篇

  • 面试准备篇

  • 技术面试题自测篇

  • 练级攻略篇

  • 工作篇

  • 面经篇

  • 笑傲Java面试

    • 2-1 导学-Java编程技巧部分
    • 2-2 IDEA Java配置补充
    • 2-4 Java8 Stream 接口:流和并发计算实例
    • 2-5 和面试官聊聊实现管道和流计算的基石:函数式的Monad
    • 2-6 Buffer的原理和使用场景+面试题解读
    • 2-7 补充提问:同步和阻塞、异步和非阻塞等不等价?
    • 2-8 阿里面试题:中文乱码处理和大文件计算词频
    • 2-9 实战场景Coding训练:解读反射+代理+AOP 并结合业务逻辑实现
    • 2-10 注解部分答案
    • 2-11 反射-元编程面试题目合集
    • 2-12 面试必备:Java8-11的新特性和理解的误区
    • 2-13 白板篇-Java编程总结(以及面试题)
    • 3-1 算法和数据结构导学
    • 3-2 教你面试时不会忘记的5种手写排序
    • 3-3 手写链表算法
    • 3-4 手写栈和队列面试专项
      • [](#栈stack)栈(Stack)
      • [](#队列queue)队列(Queue)
        • [](#面试题:两个栈实现队列)面试题:两个栈实现队列
      • [](#面试题:括号匹配问题)面试题:括号匹配问题
      • [](#总结)总结
    • 3-5 课后习题+面试题:用栈和队列实现表达式解析
    • 3-6 迷宫伪代码和8皇后问题源代码
    • 3-7 3-7 树部分源代码
    • 3-8 8皇后问题
    • 3-10 动态规划的课前题目
    • 3-11 总结和课后习题:白板篇-数据结构和算法
    • 4-1 解读:并发编程知识体系
    • 4-2 看看你的基础:Java线程状态之间如何转换?
    • 4-3 CAS和原子操作
    • 4-4 同步器(上篇)——面试官问synchronized本质是什么?
    • 4-5 同步器(中)——AbstractQueuedSynchronizer
    • 4-6 面试官:说6个Java的同步器?
    • 4-7 面试官出难题:并发环境下单例怎么写性能最高
    • 4-8 面试官:LinkedBlockingDeque和SynchronousQueue工作原理一样吗?
    • 4-9 面试要点:volatile的简短补充
    • 4-10 给面试官讲讲无锁编程(Lock-Free Programming)
    • 4-11 高阶并发编程Coding训练:N种优化哲学家就餐问题的方法
    • 4-12 并发基础篇:总结和思考题
    • 4-13 并发部分的通关Boss: 生成、发放大量红包并控制资金流速
  • LeetCode

  • 面试
  • 笑傲Java面试
pursuewind
2021-12-13
目录

3-4 手写栈和队列面试专项

# 手写栈和队列面试专项

这节课我们学习栈和队列。 栈和队列都是非常基础的数据结构,使用场景非常广泛。比如栈是实现函数调用的基础;队列是实现生产者消费者模型的基础。关于栈和队列,难度不高,但是重要性很高。也有一些巧妙的算法,是基于栈和队列的,比如说表达式的解析。这节课我们就一起来学习栈和队列。

关联的面试题:

  1. 栈和队列的基本概念:什么是栈、队列?
  2. 栈和队列的实现和基本操作接口?
  3. 栈实现队列?队列实现栈?
  4. 括号匹配问题?
  5. 表达式解析问题?

# 栈(Stack)

栈(Stack), 你可以想象成一打摆放整齐的书。只有两种操作,继续往上放书,称之为压栈(push);把最上面的书拿下来,称之为出栈(pop)。

image-20210223233239965

考虑现实意义,例如:子函数调用,压在父函数调用之上。子函数调用完成,父级函数才能返回。这样,函数调用,就可以使用压栈(push)来模拟;函数返回,就可以用出栈来模拟。构造起来是一个先入先出(First In First Out)的顺序。

栈的实现可以基于双向链表(LinkedList)或者数组(比如Array,ArrayList)。程序如下:


    public class Stack<T> {
    
        LinkedList<T> list = new LinkedList<>();
    
        public void push(T e) {
            list.addFirst(e);
    //        list.push(e);
        }
    
        public T pop() {
            //list.removeFirst();
            return list.pop();
        }
    
        public int size(){
            return list.size();
        }
    
        @Test
        public void test() {
            var stack = new Stack<>();
            stack.push(1);
            stack.push(2);
            stack.push(3);
            stack.push(4);
    
            assertEquals(4, stack.pop());
            assertEquals(3, stack.pop());
            assertEquals(2, stack.pop());
            assertEquals(1, stack.pop());
        }
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

在上面的程序中push和pop两个操作都可以在O(1)完成。

# 队列(Queue)

队列(Queue), 对应现实世界中的场景就是排队。在计算机领域举个例子,比如排队执行的线程、排队执行的任务。

排队会满足陷入先出的顺序,First In First Out(FIFO)。

排队也称作入队(enqueue)操作,离开队列也称为出队(dequeue)操作。这两个操作都可以用双向链表模拟,代码如下:

    public class Queue<T> {
        LinkedList<T> list = new LinkedList<>();
        public void enqueue(T e){
            list.add(e);
        }
        public T dequeue(){
            return list.remove();
        }
    
        public int size(){
            return list.size();
        }
    
        @Override
        public String toString(){
            return list.toString();
        }
    
        @Test
        public void test(){
            var queue = new Queue<>();
            queue.enqueue(1);
            queue.enqueue(2);
            queue.enqueue(3);
            queue.enqueue(4);
    
            assertEquals(1, queue.dequeue());
            assertEquals(2, queue.dequeue());
            assertEquals(3, queue.dequeue());
            assertEquals(4, queue.dequeue());
    
        }
    }
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

上面程序中,enqueue和dequeue都是O(1)的操作。

# 面试题:两个栈实现队列

问题:用两个栈实现队列

栈是FILO结构,如果一个FILO结构,再接上一个FILO结构。最先进去的元素,会最先通过两个栈。因此这个题目可以用这样的思路解决。

    public class Queue2<T> {
    
        Stack<T> s1 = new Stack<>();
        Stack<T> s2 = new Stack<>();
    
        public void enqueue(T e) {
            s1.push(e);
        }
    
        public T dequeue() {
            if(s2.size() > 0) {
                return s2.pop();
            }
            while(s1.size() > 0) {
                var item = s1.pop();
                s2.push(item);
            }
            return s2.pop();
        }
    
        @Test
        public void test(){
            var queue = new Queue2<>();
            queue.enqueue(1);
            queue.enqueue(2);
            queue.enqueue(3);
            queue.enqueue(4);
    
            assertEquals(1, queue.dequeue());
            assertEquals(2, queue.dequeue());
            assertEquals(3, queue.dequeue());
            assertEquals(4, queue.dequeue());
    
        }
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# 面试题:括号匹配问题

问题描述:判断一个含有{}的表达式中括号是否匹配。例如:

    {}{} : ture
    
    {123{a+b}} : true
    
    }{ : false

1
2
3
4
5
6

解析

image-20210224002433041

遍历字符串:

  • 遇到{将它压栈
  • 遇到其他字符,什么都不做
  • 遇到} 出栈一个字符,看是不是{;如果不是,说明不匹配返回false

最后看栈中是否还有元素,如果还有,也说明不匹配。

程序如下:

    public class Bracket {
    
        public boolean isMatch(String str){
            var stack = new Stack<Character>();
    
            for(char c : str.toCharArray()) {
                if(c == '{') {
                    stack.push(c);
                    continue;
                }
    
    
                if(c == '}') {
                    if(stack.size() == 0) {
                        return false;
                    }
                    var item = stack.pop();
                    if(item != '{') {
                        return false;
                    }
                }
            }
            return stack.size() == 0;
    
        }
    
        @Test
        public void test(){
            var cases = new String[]{
                   "{1}{2}{3}",
                    "{{1+2}{{3+4}}}",
                    "1*{2+3*{4+5}}",
                    "",
                    "{",
                    "}}{{",
                    "{{{}",
            };
    
            var values = new Boolean[] {
                    true,
                    true,
                    true,
                    true,
                    false,
                    false,
                    false
            };
    
            for(int i = 0; i < cases.length; i++) {
                var c = cases[i];
                assertEquals(values[i], isMatch(c));
            }
    
    
    
    
        }
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

# 总结

Queue是后续我们学习并发编程的基础;Stack是理解程序执行的基础——这只是冰山一角。作为最常见的两个数据结构,他们的使用范围非常广泛。

如果想进一步灵活运用Queue和Stack相关的技巧,可以学习一下下一堂课——用栈和队列实现表达式解析。

Last Updated: 2023/02/14, 18:02:00
3-3 手写链表算法
3-5 课后习题+面试题:用栈和队列实现表达式解析

← 3-3 手写链表算法 3-5 课后习题+面试题:用栈和队列实现表达式解析→

Theme by Vdoing | Copyright © 2019-2023 pursue-wind | 粤ICP备2022093130号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
  • 飙升榜
  • 新歌榜
  • 云音乐民谣榜
  • 美国Billboard榜
  • UK排行榜周榜
  • 网络DJ