赞
踩
一系列有序元素的集合。列表中的元素可以是任意类型,允许重复。
了解:Java中提供了多种实现列表的类,如:
- ArrayList:基于动态数组实现的列表,支持快速随机访问和插入/删除操作(需要移动大量元素--在开头或中间插入/删除时,被插入/删除位置之后的所有元素都要往前/后移位)。
- LinkedList:基于链表实现的列表,支持快速插入/删除(较高效--只需要修改指针即可),不支持随机访问。
- Vector:与ArrayList类似,但是线程安全。
- Stack(先进后出(FILO)):基于Vector实现的栈。与LinkedList相比,Vector在访问元素时更高效--根据索引值直接获取元素,不需要遍历整个列表。
综上所述,ArrayList适合需要频繁随机访问元素,但不要求线程安全,并且数据量相对较小的场景。LinkedList适合频繁进行插入/删除操作而不需要随机访问元素,常出现在需要处理大量数据的场景。Vector适合需要频繁随机访问元素,并要求线程安全的同时具备动态扩容能力的场景,插入和删除操作少。Stack适合需要按照先进后出原则进行操作的场景,对于多线程并发场景应当使用线程安全的Stack实现类,如ConcurrentLinkedStack。
Java中的基本数据类型(int、double、boolean等)不能直接添加到列表中(列表只能包含对象类型),为了解决这个问题,Java提供了对应的包装类(Integer、Double、Boolean等),实现了将基本数据类型转化为对象类型,使得基本数据类型也能被添加至列表。
一种用于存储对象的容器,并提供一些常用的操作方法,如添加、删除、查找、遍历、排序等。其中,List接口用于存储有序元素的列表。
- /* 初始化列表 */
- List<Integer> list1=new ArrayList<>();
-
- Integer[] nums=new Integer[] {1,2,3,4,5};
- List<Integer> list=new ArrayList<>(Arrays.asList(nums));
- /* 访问与更新元素 */
- int num=list.get(1);
- list.set(1,0);
- /* 在列表中添加、删除元素 */
-
-
- list.clear();/* 清空列表 */
-
- list.add(element);/* 向列表中添加单个个元素 */
- list.add(index, element);/* 指定元素位置进行添加 */
- /* 举例:list.add(3,6); //在第4个元素的位置插入6 */
-
- list.remove(index);/* 删除指定位置 */
- list.remove(element);/* 删除指定元素 */
- /* 使用注意看看大佬这篇博客: https://blog.csdn.net/qq_36412715/article/details/84071160 */
- /* 遍历列表 */
- for(int n:list){
- n
- }
- /* 拼接两个列表 */
- List<Integer> list1=new ArrayList<>(Arrays.asList(new Integer[] {1,2,3,4,5}));
- list2.addAll(list1);
- /* 排序列表 */
- Collections.sort();/* 对列表进行升序排序 */
- /* 简易实现 */
- class MyList{
- private int[] nums;
- private int capa=10;
- private int size=0;
- private int ratio=2;
-
- public MyList(){}
-
- size;
-
- public nums[index]
-
- nums[index]=s;
- copyOf
- }
- public class MyArrayList<E> {
- private Object[] elements; // 用数组实现列表
- private int size; // 列表大小
-
- // 构造方法
- public MyArrayList(int initialCapacity) {
- elements = new Object[initialCapacity];
- size = 0;
- }
-
- // 默认构造方法
- public MyArrayList() {
- this(10);
- }
-
- // 添加元素到列表末尾
- public void add(E e) {
- if (size == elements.length) {
- resize();
- }
- elements[size] = e;
- size++;
- }
-
- // 在指定位置添加元素
- public void add(int index, E e) {
- if (index < 0 || index > size) {
- throw new IndexOutOfBoundsException();
- }
- if (size == elements.length) {
- resize();
- }
- System.arraycopy(elements, index, elements, index + 1, size - index);
- elements[index] = e;
- size++;
- }
-
- // 获取指定位置的元素
- @SuppressWarnings("unchecked")
- public E get(int index) {
- if (index < 0 || index >= size) {
- throw new IndexOutOfBoundsException();
- }
- return (E) elements[index];
- }
-
- // 删除指定位置的元素
- @SuppressWarnings("unchecked")
- public E remove(int index) {
- if (index < 0 || index >= size) {
- throw new IndexOutOfBoundsException();
- }
- E e = (E) elements[index];
- System.arraycopy(elements, index + 1, elements, index, size - index - 1);
- elements[size - 1] = null;
- size--;
- return e;
- }
-
- // 删除指定元素
- public boolean remove(E e) {
- for (int i = 0; i < size; i++) {
- if (elements[i].equals(e)) {
- remove(i);
- return true;
- }
- }
- return false;
- }
-
- // 修改指定位置的元素
- public void set(int index, E e) {
- if (index < 0 || index >= size) {
- throw new IndexOutOfBoundsException();
- }
- elements[index] = e;
- }
-
- // 获取列表大小
- public int size() {
- return size;
- }
-
- // 判断列表是否为空
- public boolean isEmpty() {
- return size == 0;
- }
-
- // 判断列表中是否包含指定元素
- public boolean contains(E e) {
- for (int i = 0; i < size; i++) {
- if (elements[i].equals(e)) {
- return true;
- }
- }
- return false;
- }
-
- // 清空列表
- public void clear() {
- for (int i = 0; i < size; i++) {
- elements[i] = null;
- }
- size = 0;
- }
-
- // 扩容
- private void resize() {
- Object[] newElements = new Object[elements.length * 2];
- System.arraycopy(elements, 0, newElements, 0, size);
- elements = newElements;
- }
- }
方法 | 描述 | 时间复杂度 |
push() | 压入栈顶元素 | O(1) |
pop() | 栈顶元素出栈 | O(1) |
peek() | 访问栈顶元素 | O(1) |
- /* 常用操作 */
- Stack<Integer> stack=new Stack<>();
-
- stack.push(1);
-
- isEmpty();//判断栈是否为空;
链表实现栈,主要需要考虑链表节点的插入和删除问题。
具体过程:每次插入新元素时,创建一个新节点并将其插入链表头部;每次弹出栈顶元素时,删除链表头部元素并返回其值即可。如下图解:
- /* linkedlist_stack */
- class LinkedListStack{
- private ListNode stackPeek;
- private int stkSize=0;
-
- public void push(int num){
- ListNode node=new ListNode(num);
- node.next=stackPeek;
- stackPeek=node;
- stkSize++;
- }
- public void pop{
- int num=peek();
- stackPeek=stackPeek.next;
- stkSize--;
- return num;
- }
- }
- public class LinkedListStack {
- private Node top = null; // 栈顶指针
-
- public void push(int value) {
- Node newNode = new Node(value);
- if (top == null) top = newNode; // 如果栈为空,将新节点作为栈顶指针
- else {
- newNode.next = top; // 将新节点放在栈顶,并更改栈顶指针
- top = newNode;
- }
- }
-
- public int pop() {
- if (top == null) return -1; // 栈为空
- int value = top.value;
- top = top.next; // 修改栈顶指针
- return value;
- }
-
- private static class Node {
- private int value;
- private Node next;
-
- public Node(int value) {
- this.value = value;
- }
- }
- }
数组实现栈,主要需要考虑如何实现栈的扩容问题。
具体过程是:当数组中元素个数已满时,开辟一个新的数组,并将原来数组中的元素全部复制到新数组中,然后再把新元素加入新数组。
数组栈的主要优势在于插入和删除都可以通过索引直接访问元素。
- /* array_stack */
- add()
-
- return stack.remove(size()-1);
- public class ArrayStack {
- private int[] items; // 存放元素的数组
- private int top = -1; // 栈顶指针
- private int capacity; // 数组容量
-
- public ArrayStack(int capacity) {
- this.capacity = capacity;
- items = new int[capacity];
- }
-
- public boolean push(int item) {
- if (top == capacity - 1) return false; // 栈已满
- items[++top] = item; // 先将栈指针+1,再赋值
- return true;
- }
-
- public int pop() {
- if (top == -1) return -1; // 栈为空
- int item = items[top--]; // 先取值,再将栈指针-1
- return item;
- }
- }
都为O(1),推荐链表栈
数组栈需要预先分配一定大小的内存空间,会常用扩容操作。而链表栈不需要预先分配内存,通过动态增长、缩减空间,相比之下空间利用率更高。
使用数组作为底层实现可以快速插入和删除元素,但是需要考虑扩容问题;而使用链表作为底层实现则不需要考虑扩容问题,但是需要额外的空间存储指针信息。
在实际应用中,根据具体场景选择不同的底层实现方式能够更好地兼顾性能和空间效率。
假设有一个字符串,其中包含小括号、中括号和大括号3种类型的括号,请编写一个函数判断该字符串中的括号是否匹配。
代码:
- import java.util.Stack;
-
- public class BracketMatch {
- public static boolean isMatch(String s) {
- // 使用 Stack<Character>类型的栈来存储遍历到的括号
- Stack<Character> stack = new Stack<>();
- // 当遍历到左括号时,将其压入栈中;
- // 当遍历到右括号时,判断栈顶元素是否与之匹配,如果匹配则弹出栈顶元素,否则返回 false。
- // 最后判断栈是否为空即可。
- for (char c : s.toCharArray()) {
- if (c == '(' || c == '[' || c == '{') stack.push(c);
- else if (c == ')' && !stack.isEmpty() && stack.peek() == '(') stack.pop();
- else if (c == ']' && !stack.isEmpty() && stack.peek() == '[') stack.pop();
- else if (c == '}' && !stack.isEmpty() && stack.peek() == '{') stack.pop();
- else return false; // 遇到非法字符直接返回 false
- }
- return stack.isEmpty(); // 如果栈为空,则说明所有括号都匹配成功
- }
-
- public static void main(String[] args) {
- String s1 = "()[]{}"; // true
- String s2 = "([)]"; // false
- String s3 = "({[]})"; // true
- System.out.println(isMatch(s1));
- System.out.println(isMatch(s2));
- System.out.println(isMatch(s3));
- }
- }
假设有一个表达式字符串,其中包含加减乘除四种运算符和括号,请编写一个函数计算该表达式的结果。
代码:
- import java.util.Stack;
-
- public class ExpressionEvaluation {
- public static int evaluate(String s) {
- Stack<Integer> operands = new Stack<>(); // 存储操作数的栈
- Stack<Character> operators = new Stack<>(); // 存储运算符的栈
- for (char c : s.toCharArray()) {
- if (c >= '0' && c <= '9') operands.push(c - '0'); // 如果是数字,则直接将其压入操作数栈中
- else if (c == '+' || c == '-' || c == '*' || c == '/') { // 如果是运算符
- while (!operators.isEmpty() && precedence(c) <= precedence(operators.peek())) { // 弹出优先级比当前运算符高或相等的运算符
- char operator = operators.pop();
- int b = operands.pop(), a = operands.pop(); // 取出栈顶的两个操作数
- operands.push(calculate(a, b, operator)); // 计算结果并压回栈中
- }
- operators.push(c); // 将当前运算符入栈
- }
- else if (c == '(') operators.push(c); // 如果是左括号,则直接入栈
- else if (c == ')') { // 如果是右括号,则弹出运算符并计算结果,直到遇到左括号
- while (!operators.isEmpty() && operators.peek() != '(') {
- char operator = operators.pop();
- int b = operands.pop(), a = operands.pop();
- operands.push(calculate(a, b, operator));
- }
- operators.pop(); // 弹出左括号
- }
- else throw new IllegalArgumentException("Invalid character: " + c); // 非法字符,抛出异常
- }
- while (!operators.isEmpty()) { // 计算剩余的运算符
- char operator = operators.pop();
- int b = operands.pop(), a = operands.pop();
- operands.push(calculate(a, b, operator));
- }
- return operands.pop(); // 返回最后的结果,即为表达式的值
- }
-
- private static int precedence(char operator) { // 定义运算符的优先级
- if (operator == '*' || operator == '/') return 2;
- else if (operator == '+' || operator == '-') return 1;
- else throw new IllegalArgumentException("Invalid operator: " + operator); // 非法运算符,抛出异常
- }
-
- private static int calculate(int a, int b, char operator) { // 计算两个操作数的结果
- switch (operator) {
- case '+': return a + b;
- case '-': return a - b;
- case '*': return a * b;
- case '/': return a / b;
- default: throw new IllegalArgumentException("Invalid operator: " + operator); // 非法运算符,抛出异常
- }
- }
-
- public static void main(String[] args) {
- String s1 = "3+4*2/(1-5)+6/2"; // 7
- String s2 = "2*(3+4)-5/2"; // 11
- String s3 = "(1+2)*(3+4)"; // 21
- System.out.println(evaluate(s1));
- System.out.println(evaluate(s2));
- System.out.println(evaluate(s3));
- }
- }
在上述代码中,我们使用了两个栈:一个存储操作数(整型数字),一个存储运算符。
遍历字符串时:
最后将剩余的操作数和运算符依次计算并返回即可。
即可。
方法名 | 描述 | 时间复杂度 |
push() | 元素入队,即将元素添加至队尾 | O(1) |
poll() | 队首元素出队 | O(1) |
front() | 访问队首元素 | O(1) |
size() | 获取队列的长度 | O(1) |
isEmpty() | 判断队列是否为空 | O(1) |
- /* linkedlist_queue */
- ListNoe front,rear;
- queSize=0;
-
- push(int num){
- ListNode node=new ListNode(num);
- if(front==null){
- front=node;
- rear=node;
- }
- else{
- rear.next=node;
- rear=node;
- }
- }
-
- pop(){
- int num=peek();
- front=front.next;
- queSize--;
- return num;
- }
- public class LinkedListQueue {
- private Node head = null; // 队首指针
- private Node tail = null; // 队尾指针
-
- public void enqueue(int value) {
- Node newNode = new Node(value);
- if (tail == null) { // 如果队列为空,头尾指针都指向新节点
- head = newNode;
- tail = newNode;
- }
- else {
- tail.next = newNode; // 将新节点作为尾节点
- tail = tail.next; // 修改尾节点指针
- }
- }
-
- public int dequeue() {
- if (head == null) return -1; // 队列为空
- int value = head.value;
- head = head.next; // 修改头节点指针
- if (head == null) tail = null; // 如果队列中只有一个元素,则需要同时修改尾节点指针
- return value;
- }
-
- private static class Node {
- private int value;
- private Node next;
- public Node(int value) {
- this.value = value;
- }
- }
- }
- /* array_queue */
- class sss{
- int[] num;
- int front;
- int queSize;
-
- push(int num){
- if
- int rear=(front+queSize)%capa;
- nums[rear]=num;
- queSize++;
- }
- pop(){
- peek;
- front=(front+1)%capa;
- queSize--;
- }
- };
- public class ArrayQueue {
- private int[] items; // 存放元素的数组
- private int head = 0; // 队头指针
- private int tail = 0; // 队尾指针
- private int capacity; // 数组容量
-
- public ArrayQueue(int capacity) {
- this.capacity = capacity;
- items = new int[capacity];
- }
-
- public boolean enqueue(int item) {
- if (tail == capacity) return false; // 队列已满
- items[tail++] = item;
- return true;
- }
-
- public int dequeue() {
- if (head == tail) return -1; // 队列为空
- int item = items[head++];
- return item;
- }
- }
都为O(1)。
跟栈的说法相似。数组实现的队列需要预先分配一定大小的内存空间,在使用过程中如果超过了这个空间限制就需要进行扩容。而链表实现的队列不需要预先分配内存,可以动态增长或者缩小空间,所以空间利用率更高。
关于栈和队列的一道例题:Problem - 1702
hdu 1702 “Acboy needs your help again!”
时间限制:1000/1000 MS(Java/其他) 内存限制:32768/32768 K (Java/其他)
问题描述
阿童被绑架了!!
他非常想念他的母亲,现在非常害怕。你无法想象他被关进的房间有多暗,:(那么可怜。
作为一个聪明的ACMer,你想让ACboy走出怪物的迷宫。但当你到达迷宫的大门时,怪物说:“我听说你很聪明,但如果不能解决我的问题,你会和ACboy一起死去。
怪物的问题显示在墙上:
每个问题的第一行是一个整数N(命令的数量),和一个单词“FIFO”或“FILO”。(你很高兴,因为你知道“FIFO”代表“先进先出”,“FILO”的意思是“先进后出”)。
而接下来的N行,每行是“IN M”或“OUT”,(M代表一个整数)。
而问题的答案是一扇门的通行证,所以如果你想拯救ACboy,请仔细回答问题!
输入
输入包含多个测试用例。
第一行有一个整数,表示测试用例的数量。
每个子问题的输入如上所述。
输出
对于每个命令“OUT”,您应该输出一个整数,具体取决于单词“FIFO”或“FILO”,或者如果没有任何整数,则应输出单词“None”。
示例输入
4 4 FIFO IN 1 IN 2 OUT OUT 4 FILO IN 1 IN 2 OUT OUT 5 FIFO IN 1 IN 2 OUT OUT OUT 5 FILO IN 1 IN 2 OUT IN 3 OUT
示例输出
1 2 2 1 1 2 None 2 3
源
模拟栈和队列,栈是FILO,队列是FIFO。
--分析:分别用栈和队列模拟--
C++代码实现:
- #include<bits/stdc++.h> //C++
- using namespace std;
- int main(){
- int t,n,temp;
- cin>>t;//测试的次数
- while(t--){
- string str,str1;
- queue<int> Q;//定义一个队列
- stack<int> S;//定义一个栈
- cin>>n>>str;//输入每组要输入的个数,以及存储方式
- for(int i=0;i<n;i++){
- if(str=="FIFO"){//当为 FIFO时,表示队列
- cin>>str1;//输入 IN或者是 OUT
- if(str1=="IN"){
- cin>>temp;
- Q.push(temp);
- }
- else if(str1=="OUT"){
- if(Q.empty()) cout<<"None"<<endl;//队列为空,输出 None
- else{
- cout<<Q.front()<<endl;//访问该队首
- Q.pop();//删除队首元素
- }
- }
- }
- else{//当为 FILO,表示存储方式是栈
- cin>>str1;
- if(str1=="IN"){
- cin>>temp;
- S.push(temp);
- }
- else if(str1=="OUT"){
- if(S.empty()) cout<<"None"<<endl;
- else{
- cout<<S.top()<<endl;
- S.pop();
- }
- }
- }
- }
- }
- return 0;
- }
//热爱C++的每一天
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。