数据结构简介
-
栈(Stack):
- 概念:栈是一种后进先出(Last-In-First-Out,LIFO)的数据结构,类似于现实生活中的堆叠物品。
- 实现:在Java中,可以使用数组或链表来实现栈。数组实现的栈称为顺序栈,链表实现的栈称为链式栈。
- 常见操作:
- 入栈(push):将元素添加到栈顶。
- 出栈(pop):从栈顶移除一个元素,并返回该元素。
- 获取栈顶元素(peek):获取栈顶元素,但不进行删除操作。
- 应用场景:方法调用栈、表达式求值、括号匹配等。
-
队列(Queue):
- 概念:队列是一种先进先出(First-In-First-Out,FIFO)的数据结构,类似于现实生活中排队的情况。
- 实现:在Java中,可以使用数组或链表来实现队列。数组实现的队列称为顺序队列,链表实现的队列称为链式队列。
- 常见操作:
- 入队(enqueue):将元素添加到队尾。
- 出队(dequeue):从队头移除一个元素,并返回该元素。
- 获取队头元素(peek):获取队头元素,但不进行删除操作。
- 应用场景:任务调度、消息传递、广度优先搜索等。
-
优先队列(Priority Queue):
- 概念:优先队列是一种特殊的队列,其中每个元素都有一个优先级,优先级高的元素先出队。
- 实现:在Java中,可以使用二叉堆、斐波那契堆等数据结构来实现优先队列。
- 常见操作:
- 插入元素(insert):将元素按照优先级插入队列。
- 删除最高优先级元素(remove):删除并返回具有最高优先级的元素。
- 获取最高优先级元素(peek):获取具有最高优先级的元素,但不进行删除操作。
- 应用场景:任务调度、最小/最大K个元素查找、Dijkstra算法等。
-
双端队列(Deque):
- 概念:双端队列是一种允许在两端进行插入和删除操作的队列。
- 实现:在Java中,可以使用数组或链表来实现双端队列。数组实现的双端队列称为顺序双端队列,链表实现的双端队列称为链式双端队列。
- 常见操作:
- 头部插入(addFirst):将元素插入到队头。
- 尾部插入(addLast):将元素插入到队尾。
- 头部删除(removeFirst):删除并返回队头元素。
- 尾部删除(removeLast):删除并返回队尾元素。
- 获取队头元素(peekFirst):获取队头元素,但不进行删除操作。
- 获取队尾元素(peekLast):获取队尾元素,但不进行删除操作。
min-stack
题目描述
设计一个支持 push
,pop
,top
操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack
类:
MinStack()
初始化堆栈对象。void push(int val)
将元素val推入堆栈。void pop()
删除堆栈顶部的元素。int top()
获取堆栈顶部的元素。int getMin()
获取堆栈中的最小元素。
示例
示例 1:
输入: ["MinStack","push","push","push","getMin","pop","top","getMin"] [[],[-2],[0],[-3],[],[],[],[]] 输出: [null,null,null,null,-3,null,0,-2] 解释: MinStack minStack = new MinStack(); minStack.push(-2); minStack.push(0); minStack.push(-3); minStack.getMin(); --> 返回 -3. minStack.pop(); minStack.top(); --> 返回 0. minStack.getMin(); --> 返回 -2.
提示
-231 <= val <= 231 - 1
pop
、top
和getMin
操作总是在 非空栈 上调用push
,pop
,top
, andgetMin
最多被调用3 * 104
次
题解
思路
用两个栈实现,一个存储原数据,一个存储最小值数据。
代码
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 val) {
xStack.push(val);
minStack.push(Math.min(minStack.peek(), val));
}
public void pop() {
xStack.pop();
minStack.pop();
}
public int top() {
return xStack.peek();
}
public int getMin() {
return minStack.peek();
}
}
largest-rectangle-in-histogram
题目描述
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
示例
示例 1:
输入:heights = [2,1,5,6,2,3] 输出:10 解释:最大的矩形为图中红色区域,面积为 10
示例 2:
输入: heights = [2,4] 输出: 4
提示
1 <= heights.length <=105
0 <= heights[i] <= 104
题解
思路
找到每个柱子的最左面小于它的柱子,记录下标,找到右边小于它的柱子记录下标。
栈内元素和当前柱子高度比较,大于当前柱子弹出。小于当前柱子记录其下标
代码
class Solution {
public int largestRectangleArea(int[] heights) {
int n = heights.length;
int[] left = new int[n];
int[] right = new int[n];
Deque<Integer> mono_stack = new ArrayDeque<Integer>();
for (int i = 0; i < n; ++i) {
while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) {
mono_stack.pop();
}
left[i] = (mono_stack.isEmpty() ? -1 : mono_stack.peek());
mono_stack.push(i);
}
mono_stack.clear();
for (int i = n - 1; i >= 0; --i) {
while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) {
mono_stack.pop();
}
right[i] = (mono_stack.isEmpty() ? n : mono_stack.peek());
mono_stack.push(i);
}
int ans = 0;
for (int i = 0; i < n; ++i) {
ans = Math.max(ans, (right[i] - left[i] - 1) * heights[i]);
}
return ans;
}
}
sliding-window-maximum
题目描述
给你一个整数数组 nums
,有一个大小为 k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k
个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
示例
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3 输出:[3,3,5,5,6,7] 解释: 滑动窗口的位置 最大值 --------------- ----- [1 3 -1] -3 5 3 6 7 3 1 [3 -1 -3] 5 3 6 7 3 1 3 [-1 -3 5] 3 6 7 5 1 3 -1 [-3 5 3] 6 7 5 1 3 -1 -3 [5 3 6] 7 6 1 3 -1 -3 5 [3 6 7] 7
示例 2:
输入:nums = [1], k = 1 输出:[1]
提示
1 <= nums.length <= 105
-104 <= nums[i] <= 104
1 <= k <= nums.length
题解
思路
定义数组长度为n-k+1 保存结果。
定义双端队列保存窗口内数组下标
遍历数组,判断队列首元素值(下标)是否超出窗口大小 i - k +1;
判断队列尾元素值(下标)对应的数组元素值,和当前遍历的数组元素值比较大小,比队列元素大,弹出队列尾元素。
将数组下标添加到队列尾部。
代码
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
// Verify abnormal boundaries
if (nums == null || nums.length == 0 || k <= 0) {
return new int[0];
}
int n = nums.length;
int[] result = new int[n - k + 1];
int ri = 0;
// Use a deque to store indices of elements in the window in descending order
Deque<Integer> deque = new ArrayDeque<>();
for (int i = 0; i < nums.length; i++) {
// Remove indices that are out of the current window
while (!deque.isEmpty() && deque.peekFirst() < i - k + 1) {
deque.pollFirst();
}
// Remove indices of elements smaller than the current element from the deque
while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {
deque.pollLast();
}
// Add the current index to the deque
deque.offerLast(i);
// The first element in the deque is always the maximum in the window
if (i >= k - 1) {
result[ri++] = nums[deque.peekFirst()];
}
}
return result;
}
}