堆栈是一种线性数据结构,遵循特定的操作执行顺序。顺序可以是 LIFO(后进先出)或 FILO(先进后出)。LIFO 意味着最后插入的元素最先出现,而 FILO 意味着最先插入的元素最后出现。
现实生活中有很多堆栈的例子。考虑一个在食堂里盘子叠在一起的例子。位于顶部的盘子是第一个被移除的盘子,即放置在最底部位置的盘子在堆叠中保留最长的时间。因此,可以简单地看出遵循LIFO(后进先出)/FILO(先进后出)顺序。
为了实现栈,需要维护一个指向栈顶的指针,栈顶是最后插入的元素,因为我们只能访问栈顶的元素。
LIFO(后进先出):
该策略规定最后插入的元素将首先出现。您可以将一堆相互叠放的盘子作为现实生活中的示例。我们最后放置的盘子位于顶部,并且由于我们移除了顶部的盘子,所以我们可以说最后放置的盘子最先出现。
为了在堆栈中进行操作,向我们提供了某些操作。
给栈里面添加一个元素, 如果栈满后, 则称为溢出条件
begin
if stack is full
return
endif
else
increment top
stack[top] assign value
end else
end procedure
从栈中移除元素, 这些元素按照入栈的反方向一次出栈.
begin
if stack is empty
return
endif
else
store value of stack[top]
decrement top
return value
end else
end procedure
判断栈是否为空,如果为空返回 true
, 否则返回 false
现实生活中有很多堆栈的例子。考虑一个简单的例子,在食堂里,盘子一个一个地叠在一起。位于顶部的盘子是第一个被移除的盘子,即放置在最底部位置的盘子在堆叠中保留最长的时间。因此,可以简单地看出遵循 LIFO/FILO 顺序。
时间复杂度:
操作 | 复杂度 |
---|---|
push() | O(1) |
pop() | O(1) |
isEmpty() | O(1) |
size() | O(1) |
除了这两种主要类型之外,堆栈还有其他几种变体,包括:
堆栈可以使用数组或链表来实现。
在基于数组的实现中,push
操作是通过递增顶部元素的索引并将新元素存储在该索引处来实现的。弹出操作是通过递减顶部元素的索引并返回存储在该索引处的值来实现的。
在基于链表的实现中,推送操作是通过使用新元素创建新节点并将当前顶节点的下一个指针设置为新节点来实现的。出栈操作是通过将当前顶节点的next指针设置为下一个节点并返回当前顶节点的值来实现的。
堆栈在计算机科学中通常用于各种应用,包括表达式求值、函数调用和内存管理。在表达式的计算中,堆栈可用于在处理操作数和运算符时存储它们。在函数调用中,堆栈可用于跟踪函数调用的顺序,并在函数返回时将控制权返回到正确的函数。在内存管理中,堆栈可用于存储计算机程序中的程序计数器的值和寄存器的值,从而允许程序在函数返回时返回到先前的状态。
总之,堆栈是一种按照 LIFO 原理运行的线性数据结构,可以使用数组或链表来实现。可以在堆栈上执行的基本操作包括入栈、出栈和查看,并且堆栈在计算机科学中常用于各种应用,包括表达式求值、函数调用和内存管理。有两种方法实现一个堆栈
from sys import maxsize
def createStack():
stack = []
return stack
def isEmpty(stack):
return len(stack) == 0
def push(stack, item):
stack.append(item)
print(item + " pushed to stack ")
def pop(stack):
if (isEmpty(stack)):
return str(-maxsize -1) # 返回负无穷大
return stack.pop()
def peek(stack):
if (isEmpty(stack)):
return str(-maxsize -1) # 返回负无穷大
return stack[len(stack) - 1]
stack = createStack()
push(stack, str(10))
push(stack, str(20))
push(stack, str(30))
print(pop(stack) + " popped from stack")
class StackNode:
# Constructor to initialize a node
def __init__(self, data):
self.data = data
self.next = None
class Stack:
# Constructor to initialize the root of linked list
def __init__(self):
self.root = None
def isEmpty(self):
return True if self.root is None else False
def push(self, data):
newNode = StackNode(data)
newNode.next = self.root
self.root = newNode
print ("% d pushed to stack" % (data))
def pop(self):
if (self.isEmpty()):
return float("-inf")
temp = self.root
self.root = self.root.next
popped = temp.data
return popped
def peek(self):
if self.isEmpty():
return float("-inf")
return self.root.data
stack = Stack()
stack.push(10)
stack.push(20)
stack.push(30)
print ("% d popped from stack" % (stack.pop()))
print ("Top element is % d " % (stack.peek()))
我们给出了可以引用的总可能页码。我们还给出了缓存(或内存)大小(缓存一次可以容纳的页帧数)。LRU 缓存方案是当缓存已满并且引用缓存中不存在的新页面时删除最近最少使用的帧。
要解决该问题,需要遵循以下想法:
我们使用两种数据结构来实现 LRU Cache。 队列是使用双向链表实现的。队列的最大大小将等于可用帧的总数(缓存大小)。最近使用的页面将靠近前端,最近最少使用的页面将靠近后端。 以页码为键、对应队列节点的地址为值的哈希。 当一个页面被引用时,所需的页面可能在内存中。如果它在内存中,我们需要分离列表的节点并将其带到队列的前面。 如果所需的页面不在内存中,我们会将其放入内存中。简单来说,我们将一个新节点添加到队列的前面,并更新哈希中相应的节点地址。如果队列已满,即所有帧都已满,我们从队列的后面删除一个节点,并将新节点添加到队列的前面。
示例 –: 考虑以下参考字符串:1, 2, 3, 4, 1, 2, 5, 1, 2, 3, 4, 5
下面是上述方法的图示:
注意: 最初内存中没有任何元素。
请按照以下步骤解决问题:
//使用 Set 和 LinkedList 实现 LRU 缓存的 JavaScript 程序
class LRUCache {
constructor(capacity) {
this.cache = new Set();
this.capacity = capacity;
}
// 此函数如果缓存中不存在键,则返回false。否则,它会通过先移除再添加的方式将键移到前面,并返回true。
get(key) {
if (!this.cache.has(key)) {
return false;
}
this.cache.delete(key);
this.cache.add(key);
return true;
}
/* 在LRU缓存中引用键x */
refer(key) {
if (!this.get(key)) {
this.put(key);
}
}
// 以相反的顺序显示缓存内容
display() {
const list = [...this.cache];
// Array类用于反转数组中的元素
list.reverse();
let ans="";
for (const key of list) {
ans = ans +key + " ";
}
console.log(ans);
}
put(key) {
if (this.cache.size === this.capacity) {
const firstKey = this.cache.values().next().value;
this.cache.delete(firstKey);
}
this.cache.add(key);
}
}
const ca = new LRUCache(4);
ca.refer(1);
ca.refer(2);
ca.refer(3);
ca.refer(1);
ca.refer(4);
ca.refer(5);
ca.display();
package main
import (
"fmt"
"testing"
)
type LRUCache struct {
list []int
csize int
ma map[int]int
}
func (lru *LRUCache) refer(x int) {
if index, ok := lru.ma[x]; !ok {
// 如果存在,比较当前的容量是否已达上限
if len(lru.list) == lru.csize {
// 如果已达上限,则删除栈顶元素
lru.list = lru.list[:lru.csize-1]
}
} else {
// 如果存在, 则删除对应 index 位置的值, 并将期追加到队尾
lru.list = append(lru.list[:index-1], lru.list[index+1:]...)
}
lru.list = append(lru.list, x)
lru.ma[x] = len(lru.list)
}
func (lru *LRUCache) Display() {
for i := len(lru.list) - 1; i >= 0; i-- {
fmt.Println(lru.list[i])
}
}
func NewLRUCache(size int) *LRUCache {
ma := make(map[int]int)
return &LRUCache{
list: []int{},
csize: size,
ma: ma,
}
}
func Test_NewLRUCache(t *testing.T) {
cache := NewLRUCache(4)
cache.refer(1)
cache.refer(2)
cache.refer(3)
cache.refer(1)
cache.refer(4)
cache.refer(5)
cache.Display()
}
时间复杂度: O(1),我们使用Linked HashSet数据结构来实现缓存。Linked HashSet 为添加元素和检索元素提供恒定的时间复杂度。
辅助空间: O(n),我们需要在缓存中存储n个元素,所以空间复杂度为O(n)。
阅读量:186
点赞量:0
收藏量:0