数据结构——队列

目录

普通队列

双端队列

阻塞队列

优先级队列

循环队列

 一、试题 算法训练 JOE的早餐(优先队列)


  • 队列(queue)是一种先进先出(First In First Out,FIFO)的线性表。允许插入的一端称为队尾,允许删除的一端称为队头。
  • 队列有两种存储方式:顺式存储和链式存储
  • 队列分类:
    • 普通队列(FIFO 队列)

    • 双端队列(Deque)

    • 阻塞队列

    • 优先级队列

    • 循环队列

  • 数据结构——队列_第1张图片

普通队列

  • 普通队列是最常见的队列类型,元素按照先进先出(FIFO)的顺序排列。
  • Java 中提供了 Queue 接口和它的实现类,比如 LinkedList 和 PriorityQueue,用于实现普通队列。

普通队列的示例代码: 

import java.util.Queue;
import java.util.LinkedList;

public class QueueExample {
    public static void main(String[] args) {
        // 创建一个 LinkedList 实例来实现队列
        Queue queue = new LinkedList<>();

        // 添加元素到队列尾部
        queue.offer("Apple");
        queue.offer("Banana");
        queue.offer("Cherry");

        // 输出队列头部的元素,并移除该元素
        System.out.println(queue.poll()); // Output: Apple
        System.out.println(queue.poll()); // Output: Banana

        // 获取队列头部的元素,但不移除该元素
        System.out.println(queue.peek()); // Output: Cherry

        // 检查队列是否为空
        System.out.println("Is queue empty? " + queue.isEmpty()); // Output: false

        // 获取队列中元素的个数
        System.out.println("Queue size: " + queue.size()); // Output: 1
    }
}

双端队列

  • 双端队列是一种具有队列和栈特性的数据结构,可以在队列的两端进行添加和移除元素。
  • Java 中提供了 Deque 接口和它的实现类,比如 ArrayDeque 和 LinkedList,用于实现双端队列。

双端队列的示例代码:

import java.util.LinkedList;
import java.util.Deque;

public class DequeExample {
    public static void main(String[] args) {
        Deque deque = new LinkedList<>();

        // 在队列尾部添加元素
        deque.addLast(1);
        deque.addLast(2);
        deque.addLast(3);

        // 在队列头部添加元素
        deque.addFirst(0);

        // 访问头部和尾部的元素
        System.out.println("头部元素:" + deque.getFirst());
        System.out.println("尾部元素:" + deque.getLast());

        // 遍历队列
        System.out.println("遍历队列:");
        for (Integer element : deque) {
            System.out.print(element + " ");
        }
        System.out.println();

        // 移除头部和尾部的元素
        deque.removeFirst();
        deque.removeLast();

        // 获取队列大小
        System.out.println("队列大小:" + deque.size());
    }
}

阻塞队列

  • 阻塞队列是在队列已满或为空时会阻塞线程的特殊类型队列。
  • Java 中提供了 BlockingQueue 接口和它的实现类,比如 ArrayBlockingQueue 和 LinkedBlockingQueue,用于实现阻塞队列。

阻塞队列的示例代码:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ArrayBlockingQueue;

public class BlockingQueueExample {
    public static void main(String[] args) {
        // 创建一个容量为3的ArrayBlockingQueue
        BlockingQueue queue = new ArrayBlockingQueue<>(3);

        // 生产者线程
        Thread producer = new Thread(() -> {
            try {
                queue.put("1");
                System.out.println("Produced: 1");
                queue.put("2");
                System.out.println("Produced: 2");
                queue.put("3");
                System.out.println("Produced: 3");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        // 消费者线程
        Thread consumer = new Thread(() -> {
            try {
                Thread.sleep(2000); // 模拟消费者消费的时间
                System.out.println("Consumed: " + queue.take());
                Thread.sleep(2000);
                System.out.println("Consumed: " + queue.take());
                Thread.sleep(2000);
                System.out.println("Consumed: " + queue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        // 启动生产者和消费者线程
        producer.start();
        consumer.start();
    }
}

优先级队列

  • 优先级队列是根据元素的优先级来进行排序的队列,元素按照优先级的顺序排列。Java 中提供了 PriorityQueue 类用于实现优先级队列。
  • 通常情况下,优先级队列是通过堆(heap)这种数据结构来实现的。(堆是最值在顶部)

    • 当从优先级队列中弹出元素时,会弹出具有最高优先级的元素,这通常是堆顶的元素。然后会调整剩余元素的位置以确保堆的特性得以保持。

    • 可以自定义比较器,确定元素的弹出顺序

import java.util.PriorityQueue;

public class PriorityQueueExample {
    public static void main(String[] args) {
        // 创建一个优先级队列
        PriorityQueue priorityQueue = new PriorityQueue<>();

        // 添加元素到优先级队列
        priorityQueue.add(10);
        priorityQueue.add(30);
        priorityQueue.add(20);

        // 从优先级队列中获取元素
        System.out.println("优先级队列的顶部元素:" + priorityQueue.peek());

        // 删除优先级最高的元素
        while (!priorityQueue.isEmpty()) {
            System.out.println("从优先级队列中删除的元素:" + priorityQueue.poll());
        }
    }
}

循环队列

  • 循环队列中,队列的头部和尾部可以相邻,实现循环的队列结构。

  • 对于循环队列来说,入队和出队操作在一个固定大小的数组上执行。当队列尾部指针到达数组末尾时,会循环回到数组的起始位置,实现了循环队列的队列结构。这样可以有效地利用数组空间,能够有效地解决普通队列的"假溢出"问题,避免因为队列头部出现空闲空间而导致空间浪费的问题。

循环队列的示例代码:

public class CircularQueue {
    private int[] queue;
    private int front;
    private int rear;
    private int size;
    
    public CircularQueue(int size) {
        this.size = size;
        queue = new int[size];
        front = -1;
        rear = -1;
    }
    
    public boolean isEmpty() {
        return front == -1;
    }
    
    public boolean isFull() {
        return (front == 0 && rear == size - 1) || (rear == (front - 1) % (size - 1));
    }
    
    public void enQueue(int item) {
        if (isFull()) {
            System.out.println("Queue is full");
        } else {
            if (front == -1) {
                front = 0;
            }
            rear = (rear + 1) % size;
            queue[rear] = item;
            System.out.println(item + " enqueued to queue");
        }
    }
    
    public int deQueue() {
        int item;
        if (isEmpty()) {
            System.out.println("Queue is empty");
            return -1;
        } else {
            item = queue[front];
            if (front == rear) {
                front = -1;
                rear = -1;
            } else {
                front = (front + 1) % size;
            }
            return item;
        }
    }
}

 一、试题 算法训练 JOE的早餐(优先队列)

数据结构——队列_第2张图片

数据结构——队列_第3张图片

分析:

  • 处理大数,使用BigDecimal类,否则会溢出
    • 在使用 BigDecimal 时,你需要将结果存储在变量中,因为 BigDecimal 是不可变的,所以任何算术操作都会返回一个新的 BigDecimal 对象。

  • 如果直接把每一堆的重量都加入队列中,再开始合并,会运行超时
    •  1、把相同种类的堆先合并,找规律,使用公式
    • 比如合并【2,2,2,2,2】
      • 消耗的体力值:
        • 2*2
        • 4*2
        • 6*2
        • 8*2
        • 即为2*(2+4+6+8),即为2*2*(1+2+3+4),(1+2+……+(q-1))累加公式
        • 抽象成公式,即为 p*p*q*(q-1)/2
      • 合并后的堆重量即为 p*q
    • 2、然后把队列里不同种类的堆合并起来
  • 优先级队列根据元素的优先级弹出元素
  • 但下列代码只能拿90分,最后10分应该是相同种类合并时的问题,但实在找不到错误在哪
  • 数据结构——队列_第4张图片
import java.util.*;
import java.io.*;
import java.math.*;

class Main {
    public static void main(String[] args) throws NumberFormatException, IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(reader.readLine());
        BigDecimal result = BigDecimal.valueOf(0);// 初始化消耗的体力值
        PriorityQueue pq = new PriorityQueue<>();// 队列,存储每堆早餐的重量
        BigDecimal temp=new BigDecimal(0);// BigDecimal做算术运算时,需要一个临时对象来接收返回的内容
        for (int i = 0; i < n; i++) {
        	//读取数据
            String[] s = reader.readLine().split(" ");
            BigDecimal p = new BigDecimal(s[0]);// 重量为p的早餐有q堆
            BigDecimal q = new BigDecimal(s[1]);

            // 合并相同种类的早餐
            if (q.compareTo(BigDecimal.valueOf(1))>0) {// q>1
                temp = p.multiply(p).multiply(q).multiply(q.subtract(BigDecimal.valueOf(1))).divide(BigDecimal.valueOf(2));// p*p*q*(q-1)/2
            } else{// q<=1,上条公式不适用
                temp = p.multiply(q);
            }
            result = result.add(temp);
            BigDecimal weight = p.multiply(q);// 合并后的堆的重量
            pq.offer(weight);//把当前种类早餐的合并后的堆加入队列中
        }
        while (pq.size() > 1) {// 合并不同种类的早餐
            BigDecimal a = pq.poll();
            BigDecimal b = pq.poll();
            
            //合并两堆消耗的体力值
            temp = a.multiply(b);
            result = result.add(temp);
            
            //合并两堆后的重量
            temp = a.add(b);
            pq.offer(temp);
        }
        System.out.println(result);
    }
}

 

你可能感兴趣的:(数据结构与算法(java版),数据结构,java,算法)