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();
}
}