数据结构——队列

目录

普通队列

双端队列

阻塞队列

优先级队列

循环队列

JOE的早餐(优先队列)

23、合并K个升序链表


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

    • 双端队列(Deque)

    • 阻塞队列

    • 优先级队列

    • 循环队列

普通队列

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

普通队列的示例代码: 

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

public class QueueExample {
    public static void main(String[] args) {
        // 创建一个 LinkedList 实例来实现队列
        Queue<String> 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<Integer> 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<String> 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<Integer> 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的早餐(优先队列)

分析:

  • 处理大数,使用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分应该是相同种类合并时的问题,但实在找不到错误在哪
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<BigDecimal> 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);
    }
}

23、合并K个升序链表

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

示例 2:

输入:lists = []
输出:[]

示例 3:

输入:lists = [[]]
输出:[]

提示:

  • k == lists.length
  • 0 <= k <= 10^4
  • 0 <= lists[i].length <= 500
  • -10^4 <= lists[i][j] <= 10^4
  • lists[i] 按 升序 排列
  • lists[i].length 的总和不超过 10^4

 

package com.study.mergeKLists;

import com.study.ListNode;

import java.util.*;

public class MergeKLists {
    public ListNode mergeKLists(ListNode[] lists) {
        // 创建优先队列(最小堆)
        PriorityQueue<ListNode> pq = new PriorityQueue<>((a, b) -> a.val - b.val);

        // 将所有链表的头节点添加到优先队列中
        for (ListNode list : lists) {
            if (list != null) {
                pq.add(list);
            }
        }

        // 创建一个哑节点,用作结果链表的头部
        ListNode dummy = new ListNode(0);
        ListNode current = dummy;

        // 不断从优先队列中取出最小的节点,加入结果链表
        while (!pq.isEmpty()) {
            ListNode node = pq.poll();
            current.next = node;
            current = current.next;

            // 如果被取出的节点有下一个节点,将下一个节点加入优先队列
            if (node.next != null) {
                pq.add(node.next);
            }
        }

        // 返回合并后的有序链表
        return dummy.next;
    }

    public static void main(String[] args) {
        // 你可以在这里测试你的代码
        ListNode[] lists = new ListNode[3];

        lists[0] = new ListNode(1);
        lists[0].next = new ListNode(4);
        lists[0].next.next = new ListNode(5);

        lists[1] = new ListNode(1);
        lists[1].next = new ListNode(3);
        lists[1].next.next = new ListNode(4);

        lists[2] = new ListNode(2);
        lists[2].next = new ListNode(6);

        MergeKLists solution = new MergeKLists();
        ListNode result = solution.mergeKLists(lists);

        // 输出结果链表
        while (result != null) {
            System.out.print(result.val + " ");
            result = result.next;
        }
    }
}

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

戏拈秃笔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值