初识栈和队列

本文深入介绍了线性表的概念,包括栈和队列这两种特殊形式的线性表。栈遵循先进后出(FILO)原则,常用于表达式求值等;队列遵循先进先出(FIFO)原则,适用于任务调度。文章详细讲解了循环队列、双端队列和优先级队列(堆)的实现及操作,如堆的上浮、下沉和堆化,并展示了如何使用优先级队列解决TopK问题。此外,还讨论了堆的性质和在数据排序中的应用。
摘要由CSDN通过智能技术生成

1、什么是线性表?:
一次保存单个同类型元素,多个元素之间逻辑上连续

【栈和队列是操作受限的线性表】
【FILO,先进后出,仅能从栈顶增加或者删除元素】
1、栈的实现:
基于数组实现的栈 --顺序栈
在这里插入图片描述

队列

【FIFO,先进先出,元素从“队尾”添加,元素从“队首”删除】
常用: 链式队列
在这里插入图片描述

1、队列的子类:
FIFO、双端队列Deque、循环队列LoopQueue、优先级队列 PriorityQueue
[由于子类较多,实现Queue接口]

public interface Queue<E> {
    void offer(E val);//入队
    E poll();//出队
    E peek();//查看队首
    boolean isEmpty();//判空
}

循环队列

例:os生产者消费者模型,MySQL数据库的InnoDB存储引擎中的redo日志

使用长度固定的数组实现,数组头部就是队首(head),数组末尾就是队尾(tail),数组[head,tail)是有效元素
【tail一直指向循环队列有效元素的后一个位置】
【所谓循环队列,就是当head或者tail引用走到末尾时,下一次再继续向后移动,其实就是返回数组的头部继续操作】

步骤:

1、当" tail + 1 "= head;就认为队列已满
2、head和tail的移动:使用取模操作 ---------> tail = (tail +1) %n;

在这里插入图片描述

双端队列

Deque -> Queue的子接口
既可以尾插头出,也可以头插尾出
在这里插入图片描述

【注】无论使用栈还是接口,统一使用双端队列不推荐使用Stack类,被淘汰了

在这里插入图片描述

优先级队列

1、什么是优先级队列(堆):
按照优先级的大小动态出队
2、基于二叉树的堆(二叉堆,应用最广泛的堆)
①特点1、:完全二叉树,基于数组存储( 元素都是靠左排列,数组中存储不会浪费空间)
2、堆中根节点的值>= 子树节点中的值(最大堆)-------堆中根节点的值<=子树中节点的值(最小堆)
【PriorityQueue 默认基于最小堆实现】
在这里插入图片描述


3、堆中基本操作

【基于int型最大堆】
①上浮siftUp–添加一个元素
不断将索引K和其父节点大小对比,若大于则交换顺序,直到<=父节点

/**
     * 元素上浮
     *
     * @param k
     */
    private void siftUp(int k) {
        while (k > 0 && elementData.get(k) > elementData.get(parent(k))) {
            swap(k, parent(k));
            k = parent(k);
        }
    }

    private void swap(int k, int parent) {
        int childVal = elementData.get(k);
        int parentVal = elementData.get(parent);
        elementData.set(k, parentVal);
        elementData.set(parent, childVal);
    }

②下沉–取出最大值(若一直取最大值,降序)

/**
     * 元素下沉
     */
    private void siftDown(int k) {
        //还有左子树,
        while (leftChild(k) < size) {
            int j = leftChild(k);
            if (j + 1 < size && elementData.get(j) < elementData.get(j + 1)) {
                j++;
            }
            if (elementData.get(k) >= elementData.get(j)) {
                break;
            } else {
                swap(k, j);
                k = j;
            }
        }
    }

③heapify-堆化
方法一:建立新堆,依次添加元素(nlongn)
方法二:原地堆化–从最后一个非叶子节点开始进行元素siftDown操作(n)

/**
     * 将任意整型数组调整为优先级队列--堆
     *
     * @param arr
     */
    public MaxHeap(int[] arr) {
        elementData = new ArrayList<>(arr.length);
        //1、所有元素入堆
        for (int i : arr) {
            elementData.add(i);
        }
        size = elementData.size();
        //2、从最后一个非叶子节点开始siftDowm操作
        for (int i = parent(size-1); i >=0 ; i--) {
            siftDown(i);
        }
    }
    //时间复杂度O(n)

堆的TopK问题

【取小用大/取大用小】
在这里插入图片描述
在这里插入图片描述

import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Queue;

/**
 * @work: 面试题 17.14. 最小K个数
 */
public class Offer17_14 {
    //方法一:排序,时间复杂度高O(n^2);
    public int[] smallestK(int[] arr, int k) {
        Arrays.sort(arr);
        int[] arr1 = new int[k];
        for (int i = 0; i < k; i++) {
            arr1[i] = arr[i];
        }
        return arr1;
    }

    //方法二:优先级队列
    //取大用小
    //取小用大
    public int[] smallestK1(int[] arr, int k) {
        int[] ret = new int[k];
        if (arr.length == 0 || k == 0) {
            return ret;
        }
        //JDK默认最小堆--(o1, o2) -> o2 - o1改造成最大堆
        Queue<Integer> queue = new PriorityQueue<>((o1, o2) -> o2 - o1);
        //1、队列中先保存K个元素
        for (int i = 0; i < arr.length; i++) {
            if (queue.size() < k) {
                queue.offer(arr[i]);
            } else {
                int max = queue.peek();
                if (max > arr[i]) {
                    queue.poll();
                    queue.offer(arr[i]);
                }
            }
        }
        int i = 0;
        while (!queue.isEmpty()) {
            ret[i++] = queue.poll();
        }
        return ret;
    }
}

在这里插入图片描述
在这里插入图片描述

public class Num347 {
    public int[] topKFrequent(int[] nums, int k) {
        int[] ret = new int[k];
        Map<Integer, Integer> map = new HashMap<>();
        for (int i : nums) {
            if (map.containsKey(i)) {
                int time = map.get(i);
                map.put(i, time + 1);
            } else {
                map.put(i, 1);
            }
        }
        Queue<Freq> queue = new PriorityQueue<>();
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            if (queue.size() < k) {
                queue.offer(new Freq(entry.getKey(), entry.getValue()));
            } else {
                Freq freq = queue.peek();
                if (entry.getValue() > freq.val) {
                    queue.poll();
                    queue.offer(new Freq(entry.getKey(), entry.getValue()));
                }
            }
        }
        int i = 0;
        while (!queue.isEmpty()) {
            ret[i++] = queue.poll().key;
        }
        return ret;
    }
}

class Freq implements Comparable<Freq> {
    int key;//元素
    int val;//频次

    public Freq(int key, int val) {
        this.key = key;
        this.val = val;
    }


    @Override
    public int compareTo(Freq o) {
        return this.val - o.val;
    }
}

在这里插入图片描述

public class Num347 {
    public int[] topKFrequent(int[] nums, int k) {
        int[] ret = new int[k];
        Map<Integer, Integer> map = new HashMap<>();
        for (int i : nums) {
            if (map.containsKey(i)) {
                int time = map.get(i);
                map.put(i, time + 1);
            } else {
                map.put(i, 1);
            }
        }
        Queue<Freq> queue = new PriorityQueue<>();
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            if (queue.size() < k) {
                queue.offer(new Freq(entry.getKey(), entry.getValue()));
            } else {
                Freq freq = queue.peek();
                if (entry.getValue() > freq.val) {
                    queue.poll();
                    queue.offer(new Freq(entry.getKey(), entry.getValue()));
                }
            }
        }
        int i = 0;
        while (!queue.isEmpty()) {
            ret[i++] = queue.poll().key;
        }
        return ret;
    }
}

class Freq implements Comparable<Freq> {
    int key;//元素
    int val;//频次

    public Freq(int key, int val) {
        this.key = key;
        this.val = val;
    }


    @Override
    public int compareTo(Freq o) {
        return this.val - o.val;
    }
}

堆的原地排序

在这里插入图片描述

java中比较两个元素的大小

1、equals
2、类继承Comparable接口,覆写comparaTo方法
3、comparator比较器接口(一个类若实现了此接口表示天生为了别的类大小关系来服务)

package interface_practice;

import java.util.Arrays;
import java.util.Comparator;

/**
 * @work: 练习comparator接口使用方法。
 */
public class ComparatorTest {
    public static void main(String[] args) {
        Student[] students = new Student[]{
                new Student(18, "小张"),
                new Student(20, "小李"),
                new Student(30, "小王")
        };
        Arrays.sort(students, new StudentSec());
        System.out.println(Arrays.toString(students));//[Student{age=18, name='小张'}, Student{age=20, name='小李'}, Student{age=30, name='小王'}]
        Student[] student1 = new Student[]{
                new Student(18, "小张小"),
                new Student(20, "小"),
                new Student(30, "小王把把")
        };
        Arrays.sort(student1, new StudentName());
        System.out.println(Arrays.toString(student1));//[Student{age=20, name='小'}, Student{age=18, name='小张小'}, Student{age=30, name='小王把把'}]
    }
}

class Student {
    private int age;
    private String name;

    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

class StudentSec implements Comparator<Student> {//比较器接口,为别的类服务

    @Override
    public int compare(Student o1, Student o2) {
        return o1.getAge() - o2.getAge();
    }
}

class StudentName implements Comparator<Student> {

    @Override
    public int compare(Student o1, Student o2) {
        return o1.getName().length() - o2.getName().length();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值