栈:一种遵循后进先出(Last-In-First-Out,LIFO)原则的线性数据结构,在Java中,Stack 是一个类,它继承自 Vector 类,不过实际开发中也常使用实现了 Deque 接口的 LinkedList 类来充当栈的角色,它们都提供了处理栈。


使用Stack

 Stack<Integer> stack = new Stack<>();
Character.isDigit(字符名)判断是否是数字
Character.isLetter(字符名)和Character.isAlphabetic(字符名),前者判断字符是否是字母,后者判断字符是否是字母表中的字符(前者子集,比前者少了看起来像字母或数字与一些特殊格式的字母变体,比如罗马数字、带圈字母、计量单位den)
Character.isUpperCase(字符名)、Character.isLowerCase(字符名)判断是否是大写或小写字符
!Character.isLetterOrDigit(字符名) && !Character.isWhitespace(字符名)判断字符不是数字、字母、空白符
 Stack 基于动态数组实现,数组在内存中是连续存储的,每个元素可以通过索引直接访问。栈遵循后进先出(LIFO)的原则,主要操作有入栈(push)、出栈(pop)、查看栈顶元素(peek)等。
时间复杂度:
 入栈操作-push(E item):
  正常情况下,当数组还有剩余空间时,直接将元素添加到数组末尾,时间复杂度为O(1)。当数组空间不足时,需要进行扩容操作,即创建一个更大的新数组,并将原数组中的元素复制到新数组中,这个过程的时间复杂度为O(n),但扩容操作不是每次入栈都会发生,平均情况下,入栈操作的时间复杂度仍可看作O(1)。
 出栈操作-pop():
  正常情况下,直接移除并返回数组的最后一个元素,时间复杂度为O(1)。因为栈顶元素就是数组的最后一个元素,可直接访问和移除。
 查看栈顶元素-peek():
  直接返回数组的最后一个元素,不进行移除操作,时间复杂度为O(1)。由于数组的最后一个元素位置固定,可直接访问。
 根据索引访问元素-elementAt(int index):
  由于数组支持随机访问,可通过索引直接访问指定位置的元素,时间复杂度为O(1)。例如要访问栈中第 index 个元素,可直接通过数组的索引定位到该元素。
 查找指定元素-search(Object o):
  需要从栈顶开始依次遍历数组,查找指定元素第一次出现的位置。最坏情况下要遍历整个数组,时间复杂度为O(n)。因为在遍历过程中,需要逐个比较元素是否与目标元素相等。
 删除指定对象-removeElement(Object obj):
  首先需要遍历数组找到该元素,最坏情况下要遍历整个数组,时间复杂度为O(n)。找到元素后,还需要将该元素后面的所有元素向前移动一位,以填补删除元素后的空缺,这个移动操作的时间复杂度也为O(n),所以总体时间复杂度为O(n)。

Stack相关方法:

入栈:
 push(E item):将指定的元素压入栈顶,并返回该元素。
出栈:
 pop():移除栈顶元素并返回该元素。如果栈为空,会抛出EmptyStackException异常。
查看栈顶元素:
 peek():返回栈顶元素,但不移除它。如果栈为空,会抛出EmptyStackException异常。
判断栈是否为空:
 empty():判断栈是否为空,如果栈中没有元素,返回true;否则返回false。
返回在栈顶的位置:
 search(Object o):返回指定元素在栈中的位置,栈顶元素的位置为 1。如果元素不在栈中,返回 -1。(LinkedList中没有)
查看栈中元素数量:
 size():返回栈中元素数量。
清空栈:
 clear():清空栈,该方法会移除栈中的所有元素,使栈变为空栈

使用LinkedList

 LinkedList<Integer> stack = new LinkedList<>();
 LinkedList 基于双向链表实现,链表中的节点在内存中是离散存储的,每个节点包含数据以及指向前一个节点和后一个节点的引用。当使用LinkedList实现栈时,遵循后进先出(LIFO)的原则。
时间复杂度:
 入栈操作-push(E e) 或 addFirst(E e):
  正常情况下,将元素添加到链表的头部。由于链表可以直接通过头节点引用进行操作,不需要遍历链表,时间复杂度为O(1)。
 出栈操作-pop() 或 removeFirst():
  直接移除并返回链表的头部元素,因为可以直接通过头节点引用进行操作,不需要遍历链表,时间复杂度为O(1)。
 查看栈顶元素-peek() 或 getFirst():
  直接返回链表的头部元素,不进行移除操作,由于可以直接通过头节点引用访问元素,时间复杂度为O(1)。
 根据索引访问元素-get(int index):
  由于链表不支持随机访问,需要从链表的头节点或尾节点开始依次遍历,直到找到第 index 个节点,平均时间复杂度为O(n)。
 查找指定元素-indexOf(Object o):
  需要从链表头开始依次遍历链表,查找指定元素第一次出现的位置。最坏情况下要遍历整个链表,时间复杂度为O(n)。
 删除指定对象-remove(Object o):
  首先需要遍历链表找到该元素,最坏情况下要遍历整个链表,时间复杂度为O(n)。找到元素后将其从链表中移除,移除操作本身时间复杂度是O(1),所以总体该方法时间复杂度是O(n)。

LinkedList相关方法:

入栈:
 push(E item):将指定的元素压入栈顶,并返回该元素。
 addFirst(E e):将指定元素插入此列表的开头。虽然它不是专门为栈操作设计的,但同样可以实现入栈的效果。
出栈:
 pop():移除栈顶元素并返回该元素。如果栈为空,会抛出EmptyStackException异常。
 removeFirst():移除并返回此列表的第一个元素。如果列表为空,该方法会抛出NoSuchElementException异常。
查看栈顶元素:
 peek():返回栈顶元素,但不移除它。如果栈为空,会抛出EmptyStackException异常。
 getFirst():返回此列表的第一个元素。如果列表为空,该方法会抛出NoSuchElementException异常。
判断栈是否为空:
 isEmpty():判断栈是否为空,如果栈中没有元素,返回true;否则返回false。
查看栈中元素数量:
 size():返回栈中元素数量。
清空栈:
 clear():清空栈,该方法会移除栈中的所有元素,使栈变为空栈

使用ArrayDeque

 ArrayDeque<Integer> stack = new ArrayDeque<>();
 ArrayDeque 基于可调整大小的循环数组实现,通过维护头指针和尾指针来高效地支持双端操作。当使用ArrayDeque实现栈时,遵循后进先出(LIFO)的原则。
时间复杂度:
 入栈操作-push(E e) 或 addFirst(E e):
  正常情况下,将元素添加到数组的前端(模拟栈顶)。如果数组还有空间,直接在当前头指针位置插入元素,然后更新头指针,时间复杂度为O(1)。当数组空间不足时,需要进行扩容操作,即创建一个更大的新数组,并将原数组中的元素复制到新数组中,这个过程的时间复杂度为O(n)。但扩容操作不是每次入栈都会发生,平均情况下,入栈操作的时间复杂度仍可看作 O(1)。
出栈操作-pop() 或 removeFirst():
  直接移除并返回数组前端(栈顶)的元素,然后更新头指针。由于可以直接通过头指针访问和移除元素,不需要遍历数组,时间复杂度为O(1)。
 查看栈顶元素-peek() 或 peekFirst():
  直接返回数组前端(栈顶)的元素,不进行移除操作。通过头指针可以直接访问元素,时间复杂度为O(1)。
 根据索引访问元素-get(int index):
  由于 ArrayDeque 是基于数组的,虽然是循环数组,但仍然支持随机访问。可以通过计算索引在循环数组中的实际位置,直接访问指定位置的元素,时间复杂度为O(1)。
 查找指定元素-indexOf(Object o):
  需要从数组的前端开始依次遍历数组,查找指定元素第一次出现的位置。最坏情况下要遍历整个数组,时间复杂度为O(n)。
 删除指定对象-remove(Object o):
  首先需要遍历数组找到该元素,最坏情况下要遍历整个数组,时间复杂度为O(n)。找到元素后,还需要移动后续元素来填补空缺,这也需要O(n) 的时间复杂度(因为可能需要移动多个元素),所以总体时间复杂度为O(n)。

ArrayDeque相关方法:

入栈:
 push(E item):将指定的元素压入栈顶,并返回该元素。
 addFirst(E e):将指定元素插入此列表的开头。虽然它不是专门为栈操作设计的,但同样可以实现入栈的效果。
出栈:
 pop():移除栈顶元素并返回该元素。如果栈为空,会抛出EmptyStackException异常。
 removeFirst():移除并返回此列表的第一个元素。如果列表为空,该方法会抛出NoSuchElementException异常。
查看栈顶元素:
 peek():返回栈顶元素,但不移除它。如果栈为空,会抛出EmptyStackException异常。
 getFirst():返回此列表的第一个元素。如果列表为空,该方法会抛出NoSuchElementException异常。
判断栈是否为空:
 isEmpty():判断栈是否为空,如果栈中没有元素,返回true;否则返回false。
查看栈中元素数量:
 size():返回栈中元素数量。
清空栈:
 clear():清空栈,该方法会移除栈中的所有元素,使栈变为空栈

 想用栈 (LIFO):就严格使用 push() + pop() + peek()。
 想用队列 (FIFO):就严格使用 offer() (或 add()) + poll() (或 remove()) + peek()。
双端队列高级用法:offerFirst(e) / offerLast(e) pollFirst() / pollLast() peekFirst() / peekLast()

单调栈

 单调栈是一种特殊的栈结构,它要求栈中的元素保持单调递增或单调递减的顺序,单调递增栈是指栈内元素从栈底到栈顶是单调递增的,单调递增栈是指栈内元素从栈底到栈顶是单调递减的。当新元素入栈时,如果破坏了栈的单调性,则需要将栈顶元素出栈,直到满足单调性为止。

// 单调递增栈示例
public class MonotonicIncreasingStack {
    public static void main(String[] args) {
        int[] arr = {3, 1, 4, 2};
        // 声明一个基于 ArrayDeque 实现的栈
        ArrayDeque<Integer> stack = new ArrayDeque<>();

        for (int num : arr) {
            // 当栈不为空且栈顶元素大于当前元素时,出栈
            while (!stack.isEmpty() && stack.peek() > num) {
                stack.pop();
            }
            // 将当前元素入栈
            stack.push(num);
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容