java数据结构和算法03(队列和优先级队列)


  什么是队列呢?其实队列跟栈很像,我们可以把栈的底部给弄开,这样数据就可以从下面漏出来了,我们就从下面拿就好了。

   可以看到队列是新进先出,就跟我们显示生活中的排队一样,买火车票,飞机票等一样,先去的肯定是先上车;但是数据在出来的时候,难道我们要把上面所有的数据都往下移动一个位置吗?我们知道假如一个队列非常大,里面的数据量很多的时候我们这种要把所有的数据都移动一个位置这种行为是很坑爹的,于是我们想着用两个指针来表示这个队列中的所有数据,在这两个指针之外的数据,我们就当做什么也没看到;

  画个简单的图,由此可见队列中的指针是会随着数据的变化移动的,所以我们可以知道队列中的数据的索引可能不是从0开始的,这点和栈有很大的不同(注意,索引的话最下面最小为0,最上面最大为队列最大值减一)

  

  这里我们考虑一下,假如尾指针不断的向上移动到队列最尾部了,这个时候还要添加数据会怎么样?其实很简单,可以在头指针的下面(前提是经过删除操作,头指针下面有位置)的地方赋值,并且把尾指针指过来,我们把上面这个图进行改造一下:

 

1.队列的简单实现:  

  这种方式就叫做循环队列,或者叫缓冲环,下面我们也基于数组的形式用代码来简单实现这种队列的方式;

package com.wyq.thread;

import java.util.Arrays;

public class MyQueue {
    private Object[] arr;
    //队列最大数量
    private int maxlength;
    //队列头指针
    private int before;
    //队列尾指针
    private int after;
    //队列中实际数据的个数
    private int size;
    
    public MyQueue(){
        this(5);
    }
    public MyQueue(int len){
        arr = new Object[len];
        this.maxlength = len;
        before = 0;//刚开始头指针指向队列最下面的位置
        after = -1;//尾指针等于-1,相当于此时尾指针没有指向任何位置,等到有数据之后尾指针就会向上移动
        size = 0;//刚开始一个数据都没有
    }
    //判断当前队列中数据是不是满的
    public boolean isFull(){
        return (maxlength==size);
    }
    //向队列中增加数据
    public int add(Object obj){
        //假如队列已经满了,就抛出异常
        if (isFull()) {
            try {
                throw new Exception("不好意思队列满了,请删除一些东西再说!");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return 0;
        }else{
            //队列中数据没有满的话,判断此时尾指针是不是指向最上面,是的话首先将尾指针指向最下面
            if (after==maxlength-1) {
                after = -1;
            }
            //尾指针向上移动一个位置,并且放入数据
            arr[++after] = obj;
            //由于增加了数据,所以队列中的实际数据的数量增加
            size++;
            return 1;
        }
        
    }
    //删除数据
    public int delete(){
        //如果队列不是一个空队列,那么我们就删除头指针指向的数据,其实就是将这个数据变为null,然后头指针向上移动
        if (size>0) {
            arr[before++]=null;
            //这里进行判断假如头指针移动到最顶端,那么就把头指针重新移动到索引为0的位置
            if (before==maxlength) {
                before=0;
            }
            //删除了数据,那么队列中的实际数量减少
            size--;
            return 1;
        }
        return 0;
    }
    //查看头指针指向的数据
    public Object peek(){
        return arr[before];
    }
    //队列中数据的实际数量
    public int size(){
        return size;
    }
    //展示队列中的所有数据
    public void display(){
        System.out.println(Arrays.toString(arr));
    }
    
    public static void main(String[] args) {
        MyQueue myQueue = new MyQueue();
        for (int i = 0; i < 5; i++) {
            myQueue.add(i);
        }
        
        myQueue.delete();
        myQueue.delete();
        myQueue.display();
        
        myQueue.add(100);
        myQueue.display();
        
        System.out.println(myQueue.peek());
        System.out.println("队列中数据的数量:"+myQueue.size());
    }

}

 

 

 2.优先级队列

   什么又是优先级队列呢?你看,虽然我们上面实现的队列基本的功能是可以使用的,但是插入的数据是没有顺序的,比如我插入的顺序是3,5,1,4,,在队列中是顺序就是3514,但是假如是优先级队列的话,在往队列中插入数据的时候会进行一个排序,就会变成5431这样的顺序;

  那可能会有人想问这有什么用啊?例如在抢先式多任务操作系统中程序就是排列在优先级队列中,这样优先级最高的队列就会优先执行。

  我还是简单的画一个图;

  

  我们就用一个Integert数组实现一下这个原理,很明显插入数据是比较麻烦的,我们重点应该是add方法;

package com.wyq.thread;

import java.util.Arrays;

public class MyPriorityQueue {
    //由于删除的时候需要将要删除的位置赋值为null,所以就用Integer[]数组了
    private Integer[] priorityArr;
    //优先级队列最大数量
    private int maxlength;
    //优先级队列中实际数据的个数
    private int size;
    
    public MyPriorityQueue(){
        this(5);
    }
    public MyPriorityQueue(int len){
        priorityArr = new Integer[len];
        this.maxlength = len;
        size = 0;//刚开始一个数据都没有
    }
    //判断当前队列中数据是不是满的
    public boolean isFull(){
        return (maxlength==size);
    }
    //向队列中增加数据,数据从上到下依次增大
    public int add(int value){
        int i;
        //假如队列中一个数据都没有,那就将数据添加到第一个位置
        if (size==0) {
            priorityArr[size++] = value;
            return 1;
        }else{
            //如果队列中数据已经满了,那就抛出异常
            if (size==maxlength) {
                try {
                    throw new Exception("优先级队列满了,还存,存你妹啊!");
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return 0;
            }
            //此处可以说是这个方法的核心,先用指针i指向队列的最上面,将我们要添加的数据和最上面的那个数据进行比较,比它大的话就将顶端数据向上移动一个位置
            //然后将指针i向下移动一个位置,此时由于在while循环中,就会继续将要插入的数据和i指向的数据进行比较,重复这个步骤,直到要插入的数据比指针指向
            //的数据要小,就将数据插入这个数的上面,即最后的位置即是i+1
            i = size-1;
            while(i>=0 && value > priorityArr[i]) {
                priorityArr[i+1] = priorityArr[i];
                i--;
            }
            priorityArr[i+1]=value;
            size++;
            return 1;
            
            
        }
        
    }
    //删除数据,由于数据从上到下依次增大,我们可以按照从小到大删除或者从大到小删除
    //我们这里就实现从大到小删除,只需在这个方法中设置一个指向最上面的指针即可;
    //假如要实现从小到大删除,则需要设置一个类变量为最下面的头指针
    public int delete(){
        int after = size-1;
        int value = priorityArr[after];
        priorityArr[after]=null;
        size--;
        return value;
    }
    //查看头指针指向的数据
    public int peekMin(){
        return priorityArr[size-1];
    }
    //队列中数据的实际数量
    public int size(){
        return size;
    }
    //展示队列中的所有数据
    public void display(){
        System.out.println(Arrays.toString(priorityArr));
    }
    
    public static void main(String[] args) {
        MyPriorityQueue priority = new MyPriorityQueue();
        priority.add(3);
        priority.add(10);
        priority.add(8);
        priority.add(1);
        priority.add(100);
        priority.display();
        
        priority.delete();
        priority.delete();
        priority.display();
        
        System.out.println("队列中最小的元素为:"+priority.peekMin());
        System.out.println("队列中实际的数据的个数为:"+priority.size());

    }

}

 

  其实在这里我们也可以看到int数组和Integer数组的区别,最大的区别就是可不可以用null,在delete方法中,假如我们用的是int[]数组,那删除的位置我们用什么表示呢?用0?还是-1?假如用0表示,那我们在用队列的时候插入数据0,那么,此时队列中的0就有两种意思,一种是没有数据,一种是数据0,于是用Integer就没有这个烦恼


作者:java小新人,发布于:2019/04/30
原文:https://www.cnblogs.com/wyq1995/p/10791478.html