leetcode探索之旅(队列)

首先,题目都是出自leetcode的探索里的初级算法的课程。

嘿嘿,有心想学的请自行前往学习,光看是没用的。

队列:先入先出的数据结构

show code

// "static void main" must be defined in a public class.
// java的入口函数 static void main 必须定义在一个public类里

// 队列实体
class MyQueue {
    // store elements
    // 存储队列内容的List
    private List data; 
    // a pointer to indicate the start position
    // 指向头结点的标识
    private int p_start;
    // 队列实体的构造函数,初始化list和p_start
    public MyQueue() {
        data = new ArrayList();
        p_start = 0;
    }
    /** Insert an element into the queue. Return true if the operation is successful. */
    // 类中方法,添加一个元素。添加方法:data.add(),list的add方法向数组后面添加一个元素
    public boolean enQueue(int x) {
        data.add(x);
        return true;
    };
    /** Delete an element from the queue. Return true if the operation is successful. */
    // 移除一个元素
    // 首先通过isEmpty()方法,如果首元素大于等于list长度,判断队列为空
    // 对于队列,p_start指向队尾元素时=length-1,当最后一个元素出列,p_start=length。>=为一种保险的写法,我并未想出>的情景
    // 如果队列非空,p_start自加。
    // 这里有个问题,list并未移除任何元素。以p_start元素判断队列是否清空。
    public boolean deQueue() {
        if (isEmpty() == true) {
            return false;
        }
        // 是否可以在这里加入list的移除
        // list.remove(p_start);
        p_start++;
        return true;
    }
    /** Get the front item from the queue. */
    // 获取队列的首元素
    public int Front() {
        return data.get(p_start);
    }
    /** Checks whether the queue is empty or not. */
    // 判断队列是否为空
    public boolean isEmpty() {
        return p_start >= data.size();
    }     
};

public class Main {
    public static void main(String[] args) {
        // 初始化队列
        MyQueue q = new MyQueue();
        q.enQueue(5);
        q.enQueue(3);
        // 队列不为空,输出首元素,这里应该输出5
        if (q.isEmpty() == false) {
            System.out.println(q.Front());
        }
        // 移除5
        q.deQueue();
        // 输出3
        if (q.isEmpty() == false) {
            System.out.println(q.Front());
        }
        // 移除3
        q.deQueue();
        // if(false)
        // p_start = 2
        // data => [5,3]
        if (q.isEmpty() == false) {
            System.out.println(q.Front());
        }
    }
}

由此引出一个更高效的算法——循环队列。

// 循环队列——使用固定大小的数组和两个指针来指示起始位置和结束位置。目的是重用我们之前提到的被浪费的存储。
// 思路:首先关键点有:
// 1、front和rear指针移动
// 问题:对于指定长度的数组,当我们的下标移动的时候必然会遇到下标越界,此时理论上讲超过数组范围的下标应该移动到0号索引处
// 解决方法:进行移动的时候,(front + 1) % queue.length就可获得正确的下标,rear同理
// 2、队空与队满的判断(思路是使用一个实际存储长度标识 size来参考判断)
// 因为我们用size做判断符号且数组长度加一以防止下标指向空指针
// 所以判空 size == 0;判满 size == length - 1。
// 3、入队和出队操作
// 入队操作
// 入队-》判断队满-》入参赋值-》rear后移-》size++
// 出队操作
// 出队-》判断队空-》front后移-》size--
// 4、返回队首和队尾索引
// 队首很简单:queue[front] 也可以 front == rear
// 队尾很奇怪: queue[rear]会在某些情况下失效,举个栗子:[1,2,3,null],length = 4,front = 0,rear = 3,size = 3,此时的rear-1;[1,2,3,4],length = 3,front = 1,rear = 0,size = 3,此时的rear - 1 = -1,显然是不行的,此时队尾应该是4,所以使用(rear-1 + length) % length;

class MyCircularQueue {

    private int [] queue;
    private int front;
    private int rear;
    // 记录实际队列长度的数值
    private int size;
    
    /** Initialize your data structure here. Set the size of the queue to be k. */
    public MyCircularQueue(int k) {
        queue = new int [k + 1];
        front = 0;
        rear = 0;
        size = 0;
    }
    
    /** Insert an element into the cir3W2WXcular queue. Return true if the operation is successful. */
    public boolean enQueue(int value) {
        if (isFull()) {
            return false;
        }
        // 尾元素进1,如果超出数组长度,取mod(求余)
        queue[rear] = value;
        rear = (rear + 1) % queue.length;
        size++;
        return true;
    }
    
    /** Delete an element from the circular queue. Return true if the operation is successful. */
    public boolean deQueue() {
        if (isEmpty()) {
            return false;
        }
        // front移动,可不变更
        front = (front + 1) % queue.length;
        size--;
        return true;
    }
    
    /** Get the front item from the queue. */
    public int Front() {
        if (isEmpty()) return -1;
        return queue[front];
    }
    
    /** Get the last item from the queue. */
    public int Rear() {
        if (isEmpty()) return -1;
        return queue[(rear - 1 + queue.length) % queue.length];
    }
    
    /** Checks whether the circular queue is empty or not. */
    public boolean isEmpty() {
        return size == 0;
    }
    
    /** Checks whether the circular queue is full or not. */
    public boolean isFull() {
        return size == queue.length - 1;
    }
}

应用:

题目(数据流中的移动平均值):
给定一个整数数据流和一个窗口大小,根据该滑动窗口的大小,计算其所有整数的移动平均值。

测试用例:
MovingAverage m = new MovingAverage(3); // size = 3
m.next(1) // 1  [1] average = 1/1
m.next(10) // (1 + 10) / 2 [1,10] average = (1+10)/2
m.next(3) // (1 + 10 + 3) / 3 [1,10,3] average = (1+10+3)/2
m.next(5) // (10 + 3 + 5) / 3 [10,3,5] average = (10+3+5)/2

若依次得到测定值(X1,X2,X3,...,Xn)时,按顺序取一定个数所做的全部算术平均值。 例如(X1+X2+X3)/3,(X2+X3+X4)/3,(X3+X4+X5)/3,(X4+X5+X6)/3...等是移动平均值。

// 刚开始看这道题的时候,我去,我题目都没看懂。。。
// 后来百度了下移动平均数,然后又找了个题解看了下实现过程
class MovingAverage {
    // 这是初始化的窗口大小
    int size = 0;
    // 申明一个队列,使用LinkedList初始化,这是collection中的队列
    Queue queue;
    // 使用sum提前记录总数,方便取平均数
    // 注意这里最好定义为double类型,如果是整数不注意就会精度丢失
    double sum = 0;
    /** Initialize your data structure here. */
    public MovingAverage(int size) {
        this.size = size;
        // 初始化队列
        queue = new LinkedList<>();
    }
    
    public double next(int val) {
        // 入队
        queue.offer(val);
        // 求和
        sum += val;
        // 如果超出屏幕
        if (queue.size() > size) {
            Integer head = queue.poll();
            sum -= head;
        }
        // 如果queue小于size ,取queue的长度
        return sum / queue.size();
    }
}

面对过现实才知道自己多渺小
我毕业也半年了,一个专升本文凭又因为自己的胆怯、随遇而安的性格错过了校招,进了学长的一个公司。忙忙碌碌、松松垮垮,经历了一个多月的高强度加班,也经历了松懈的下班玩游戏、看小说、上班划水。慢慢的自己也感觉到着急,回顾了自己的所学:android半吊子(垃圾码农水准)、java半吊子(码农水准)、vue半吊子(垃圾码农水准)、flutter初学者(码农都算不上)。毕业一个月的时候,隔三差五的突然想学些什么,学了个把天又置之脑后。我的自制力本来垃圾,又没一个目标,浑浑噩噩下来一事无成。我不想这样直到我失去冲劲、失去追求前进的力气。我现在在这定个目标:以java工程师的身份进到阿里,或许这是个永远吃不到的萝卜,至少我有了明确的前进目标。——2020-3-12(感觉我心理这个日期已经出问题了。。。)

后面如果有实际使用队列的情景就加载下方——2020-3-12

我突然发现我之前的内心好像出问题了,我完全不用这么纠结,以进某厂为目标有什么意思?在写CRUD的路上,我觉得最求技术就行,了解感兴趣的技术,了解想知道的知识,追求完成作品的乐趣,他不香么?结果不是必然,探索永无止境。——2020-3-31

你可能感兴趣的:(leetcode探索之旅(队列))