文章目录
写在前面,本文是本人在面试中,面试官问到的一些关于栈和队列的问题,问题的答案是自己总结出来的,可能存在一些偏差,欢迎大家指正
后续如果有碰到更多问题,会继续补充
1. 你知道的线性表有哪些?非线性表呢
线性表:一般线性表、栈与队列、字符串、数组、广义表
非线性表:树、图、集合
2. 简答说一下对栈和队列的了解
栈是先进后出(Last In First Out, LIFO) 队列是先进先出(First In First Out, FIFO)
栈只有两种操作,push(入栈,插入数据)和pop(出栈,删除数据)。用数组实现的栈叫顺序栈,用链表实现的栈叫链式栈
队列:push(入队,插入数据)和pop(出队,删除数据)。用数组实现的栈叫顺序队列,用链表实现的栈叫链式队列
3. 根据你写的代码,说一下入栈、出栈、出队、入队各自的时间复杂度
如下
4. 用数组实现一个栈(顺序栈)
入栈的时间复杂度为O(1),出栈的时间复杂度为O(1)
class Stack {
constructor() {
this.stack = []
}
push(item) {
this.stack.push(item)
}
pop() {
this.stack.pop()
}
getTop() {
return this.stack[this.getCount() - 1]
}
getCount() {
return this.stack.length
}
isEmpty() {
return this.getCount() === 0
}
}
5. 用链表实现一个栈(链式栈)
入栈的时间复杂度为O(1),出栈的时间复杂度为O(1)
function Node(val) {
this.val = val;
this.next = null;
}
function Stack() {
this.top = null;
this.length = 0;
}
Stack.prototype.push = function (node) {
if (!this.top) {
this.top = node;
this.length++;
} else {
node.next = this.top;
this.top = node;
this.length++;
}
}
Stack.prototype.pop = function () {
let res = this.top || undefined;
if (this.top) {
this.top = this.top.next;
this.length--;
}
return res;
}
6. 用数组实现一个队列(顺序队列)
入队的时间复杂度为O(1),出队的时间复杂度为O(1)
class Queue {
constructor() {
this.queue = []
}
push(item) {
this.queue.push(item)
}
pop() {
this.queue.shift()
}
getHeader() {
return this.queue[0]
}
getLength() {
return this.queue.length
}
isEmpty() {
return this.getLength() === 0
}
}
7. 用链表实现一个队列(链式栈)
入队的时间复杂度为O(1),出队的时间复杂度为O(1)
function Node(val) {
this.val = val;
this.next = null;
}
function Queue() {
this.front = null;
this.tail = null;
this.length = 0;
}
Queue.prototype.push = function (node) {
if (!this.front) {
this.front = this.tail = node;
} else {
this.tail.next = node;
this.tail = node;
}
this.length++;
}
Queue.prototype.pop = function () {
if (!this.length) return -1;
let res = this.front;
this.front = this.front.next;
this.length--;
if (!this.length)
this.tail = null;
return res;
}
8. 实现一个循环队列
入队的时间复杂度为O(1),出队的时间复杂度为O(1)
class SqQueue {
constructor(length) {
this.queue = new Array(length + 1)
this.first = 0
this.last = 0
this.size = 0
}
enQueue(item) {
// 判断队尾 + 1 是否为队头
// 如果是就代表需要扩容数组
// % this.queue.length 是为了防止数组越界
if (this.first === (this.last + 1) % this.queue.length) {
this.resize(this.getLength() * 2 + 1)
}
this.queue[this.last] = item
this.size++
this.last = (this.last + 1) % this.queue.length
}
deQueue() {
if (this.isEmpty()) {
throw Error('Queue is empty')
}
let r = this.queue[this.first]
this.queue[this.first] = null
this.first = (this.first + 1) % this.queue.length
this.size--
// 判断当前队列大小是否过小
// 为了保证不浪费空间,在队列空间等于总长度四分之一时
// 且不为 2 时缩小总长度为当前的一半
if (this.size === this.getLength() / 4 && this.getLength() / 2 !== 0) {
this.resize(this.getLength() / 2)
}
return r
}
getHeader() {
if (this.isEmpty()) {
throw Error('Queue is empty')
}
return this.queue[this.first]
}
getLength() {
return this.queue.length - 1
}
isEmpty() {
return this.first === this.last
}
resize(length) {
let q = new Array(length)
for (let i = 0; i < length; i++) {
q[i] = this.queue[(i + this.first) % this.queue.length]
}
this.queue = q
this.first = 0
this.last = this.size
}
}
9. 两个栈实现一个队列
堆栈A和堆栈B,当有元素要插入的时候,就往堆栈A里插入。当要移除元素的时候,先将堆栈A里的元素依次出栈放入到堆栈B中,再从堆栈B的顶部出数据。如此便基于2个堆栈实现了先进先出的原则了
入队的时间复杂度为O(1),出队的时间复杂度为O(n)
class MyQueue {
constructor() {
this.s1 = []
this.s2 = []
this.front = null
}
enPush(item) {
this.s1.push(item)
}
dePop() {
if (this.s2.length === 0) {
while (this.s1.length !== 0) {
this.s2.push(this.s1.pop())
}
}
this.front = this.s2[1]
return this.s2.pop()
}
peek() {
if (this.s2.length === 0) {
return this.s1[0] ? this.s1[0] : null
}
return this.front
}
isEmpty() {
if (this.s1.length === 0 && this.s2.length === 0) {
return '队列为空'
}
return '队列不为空'
}
}
let ans = new MyQueue()
ans.enPush(2)
ans.enPush(3)
console.log(ans.peek()); // 2
console.log(ans.isEmpty()); // 队列不为空
console.log(ans);
10. 两个队列实现一个栈
队列A和队列B,当需要进行入栈操作的时候,直接往队列A中插入元素。当需要进行出栈操作的时候,先将队列A中的前n-1个元素依次出队移动到队列B中,这样队列A中剩下的最后一个元素其实就是我们所需要出栈的元素了,将这个元素出队即可
入栈的时间复杂度为O(1),出栈的时间复杂度为O(n)
class MyStack {
constructor() {
this.q1 = []
this.q2 = []
this.front = null
}
enPush(item) {
this.q1.push(item);
this.front = item;
}
dePop() {
while (this.q1.length > 1) {
this.front = this.q1.shift();
this.q2.push(this.front);
}
// 交换p1和p2
let val = this.q1.pop();
let temp = this.q2;
this.q2 = this.q1;
this.q1 = temp;
return val;
}
getTop() {
return this.front;
}
isEmpty() {
return this.q1.length === 0;
}
}
let ans = new MyStack()
ans.enPush(2)
ans.enPush(5)
ans.dePop()
console.log(ans.getTop()) // 2
console.log(ans.isEmpty()); // false
console.log(ans); // MyStack {q1: Array(1), q2: Array(0), front: 2}
这道题其实还有另一个解法,只需要一个队列就可以做到模拟出堆栈,思路就是:当需要进行入栈操作的时候,先将新元素插入到队列的队尾中,再将这个队列中的其它元素依次出队,队列的特性当然是从队头出队了,但是出来的元素再让它们从队尾入队,这样依次进行,留下刚才插入的新元素不动,这个时候,这个新元素其实就被顶到了队头了,新元素入栈的动作就完成了。当需要进行出栈操作的时候,就直接将队列队头元素出队即是了。
11. 实现一个优先队列
优先队列(priority Queue)是一种特殊的队列,它不遵守先进先出的原则,它是按照优先级出队列的。分为最大优先队列(是指最大的元素优先出队)和最小优先队列(是指最小的元素优先出队)。
入队的时间复杂度为O(1),出队的时间复杂度为O(n)
class priorityQueue {
constructor() {
this.queue = []
this.front = null
this.back = null
}
enQueue(item) {
this.queue.push(item)
}
deQueue() {
let priority = this.queue[0].code
let fromIndex = 0
this.queue.forEach((item, index) => {
if (item.code < priority) {
fromIndex = index
}
})
// 弹出优先级最高的元素
return this.queue.splice(fromIndex, 1)
}
getTop() {
return this.queue[this.queue.length - 1]
}
getBack() {
return this.queue[0]
}
isEmpty() {
if (this.queue.length === 0) {
return true
}
return false
}
getCount() {
return this.queue.length
}
}
class Patient {
constructor(name, code) {
this.name = name
// code代表优先级,越小代表优先级越高
this.code = code
}
}
let q = new priorityQueue()
let p1 = new Patient('Smith', 4)
let p2 = new Patient('Tom', 2)
let p3 = new Patient('Jane', 6)
q.enQueue(p1)
q.enQueue(p2)
q.enQueue(p3)
console.log(q.getTop()) // Jane...
console.log(q.getBack()) // Smith...
console.log(q.isEmpty()) // false
console.log(q.deQueue()) // Tom...
console.log(q.getCount()) // 2