007 稀疏数组的应用场景
基本介绍:
当一个数组中大部分元素为0,或者为同一个数值的数组时,可以稀疏数组来保存该数组
稀疏数组的处理方法是:
- 记录数组一共有几行几列,有多少个不同的值
- 把具有不同值的元素的行列及值记录在一个小规模的数组中,从而缩小程序的规模
008 稀疏数组转换的思路分析
应用实例
- 使用稀疏数组,来保留类似前面的二维数组(棋盘、地图等等)
- 把稀疏数组存盘,并且可以重新恢复原来的二维数组数
- 整体思路分析
二维数据转稀疏数组的思路
- 遍历原始的二维数组,得到有效数据的个数 sum
- 根据 sum 就可以创建稀疏数组 sparseArr in[sum + 2] [3]
- 将二维数组的有效数据存入到稀疏数组中
稀疏数组转原始二维数组
- 读取稀疏数组的第一行,根据第一行,创建原始的二维数组,比如上面的 chessArr2 = ing[11][11]
- 在读取稀疏数组后几行的数据,并赋给原始的二维数组
009 稀疏数组的代码实现
package com.old.sparseArray;
public class SparseArray {
public static void main(String[] args) {
//创建一个原始的二维数组 11 * 11
//0:表示没有苊,1表示白子 2 表示黑子
int chessArr1[][] = new int[11][11];
chessArr1[1][2] = 1;
chessArr1[2][3] = 2;
chessArr1[4][5] = 1;
//输出原始的二维数据
for (int[] row : chessArr1) {
for (int data : row) {
System.out.printf("%d\t", data);
}
System.out.println();
}
//二维数组转稀疏数组
//1.遍历二维数组得到非0数据的个数
int sum = 0;
for (int i = 0; i < chessArr1.length; i++) {
int[] ints = chessArr1[i];
for (int j = 0; j < ints.length; j++) {
if (ints[j] != 0) {
sum++;
}
}
}
System.out.println("总数:" + sum);
//2.创建对应的稀疏数组 sum + 1是因为数组是从0开始,所有需要加1
int sparseArr[][] = new int[sum + 1][3];
/**
* 给稀疏数组赋值 这里为什么这样还未明白
* 这里是为了记录原始数组的行和列的长度,而最后一个值是用于保存
* 原始数组的有效值个数
*/
sparseArr[0][0] = 11;
sparseArr[0][1] = 11;
sparseArr[0][2] = sum;
//遍历二维数组,将非0的值存放到稀疏数组中
int count = 0;//用于记录是第几个非0数据
for (int i = 0; i < chessArr1.length; i++) {
int[] ints = chessArr1[i];
for (int j = 0; j < ints.length; j++) {
if (ints[j] != 0) {
count++;
sparseArr[count][0] = i;//记录原始数组的行
sparseArr[count][1] = j;//记录原始数组的列
sparseArr[count][2] = chessArr1[i][j];//记录原始数组对应位置的值
}
}
}
//输出稀疏数组的形式
System.out.println();
System.out.println("得到的稀疏数组为:");
for (int i = 0; i < sparseArr.length; i++) {
System.out.printf("%d\t%d\t%d\t\n",
sparseArr[i][0], sparseArr[i][1], sparseArr[i][2]);
}
//将稀疏数组恢复成原始的二维数组
//1.读取稀疏数组的第一行,
int cheessArr2[][] = new int[sparseArr[0][0]][sparseArr[0][1]];
//输出恢复后的二维数组
System.out.println();
//从稀疏数组的第二行开始
for (int i = 1; i < sparseArr.length; i++) {
int[] ints = sparseArr[i];
for (int j = 0; j < ints.length; j++) {
cheessArr2[ints[0]][ints[1]] = sparseArr[i][2];
}
}
System.out.println("恢复后的原始数组");
for (int[] row : cheessArr2) {
for (int data : row) {
System.out.printf("%d\t", data);
}
System.out.println();
}
}
}
010 队列的应用场景
队列介绍
- 队列是一个有序列表,可以用数组或是链表实现
- 遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出
010 数组模拟队列的思路分析
数组模拟队列
- 队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图,其中 maxSize 是该队列的最大容量
- 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量 front 及 rear 分别记录队列前后端的下标,front 会随着数据输出而改变,而 rear 则是随着数据输入而改变
011 数组模拟队列
- 将数据存入队列时称为 “addQueue” ,addQueue 的处理需要有两个步骤:
- 将尾指针往后移: rear + 1 , 当 front == rear 【空】
- 若尾指针 rear 小于队列的最大下标 maxSize - 1 ,则将数据存入 rear 所指的数组元素中,否则无法存入数据。 rear == maxSize -1 【队列满】
012 数组模拟队列代码实现
package com.old.queue;
import java.util.Arrays;
import java.util.Scanner;
public class ArrayQueueDemo {
public static void main(String[] args) {
/**
队列是一种特殊的线性表,它只允许在表的前端进行删除操作,而在表的后端进行插入操作。
一开始创建的头和尾的指针都是-1,
当添加数据时,队列尾加1
当输出数据时,队列头加1
两个属性,front 和 rear 一个代表存到了哪,一个代表取到了哪,如果存和取是等于,代表队列中没有数据,如果 头 大于等于 队列的最大容量,就表示队列已经满了。
数组是有顺序的,存入的第一个就在数组的 0 下标的位置,存入的第二个就在数组的 1 下标的位置,而加入数据一直对 队列尾属性+1,这样这个属性也代表着数组当前存入的数据的位置。
取出数据则是对,队列头属性+1,这个属性就代表着当前数组取出数据的位置。
因为队列只能一边存,一边取,所以存是从队列尾中加入,所以存入就队列尾加1,取出就是队列头加1
*/
ArrayQueue arrayQueue = new ArrayQueue(3);
Scanner scanner = new Scanner(System.in);
boolean loop = true;
while (loop) {
System.out.println("1.显示队列");
System.out.println("2.退出程序");
System.out.println("3.添加数据到队列");
System.out.println("4.从队列取出数据");
System.out.println("5.查看头数据");
String s = scanner.nextLine();
try {
switch (s) {
case "1":
arrayQueue.showQueue();
break;
case "2":
scanner.close();
System.out.println("扫描器已关闭");
loop = false;
break;
case "3":
System.out.println("请输入数字:");
String value = scanner.nextLine();
arrayQueue.addQueue(Integer.valueOf(value));
break;
case "4":
System.out.println("取出:" + arrayQueue.getQueue());
break;
case "5":
System.out.println("取出:" + arrayQueue.headQueue());
break;
}
} catch (Exception e) {
System.out.println(e.getCause());
}
System.out.println();
System.out.println("队列头,fron:" + arrayQueue.getFront());
System.out.println("队列尾,rear:" + arrayQueue.getRear());
System.out.println();
}
}
}
//使用数组模拟队列
class ArrayQueue {
private int maxSize;//数组最大容量
/**
* 队列头
*/
private int front;
/**
* 队列尾
*/
private int rear;
private int[] arr;//该数组用于存放数据,模拟队列
//创建队列的构造器
public ArrayQueue(int arrMaxSize) {
this.maxSize = arrMaxSize;
arr = new int[maxSize];
front = -1;//队列头部,分析出,front 是指向队列头的前一个位置
rear = -1;//指向队列尾,指向队列尾的数据(即就是队列最后一个数据)
}
//判断队列是否满
public boolean isFull() {
//如果头一个等于最大容量
return rear == maxSize - 1;
}
//判断队列是否为空
public boolean isEmpty() {
return rear == front;
}
//添加数据到队列
public void addQueue(int n) {
//判断是否满,
if (isFull()) {
System.out.println("队列满,不能加入数据");
return;
}
rear++;//让 rear 后移
arr[rear] = n;
}
//获取队列数据 出队列
public int getQueue() {
//判断队列是否为空
if (isEmpty()) {
throw new RuntimeException("队列中没有数据");
}
front++;
return arr[front];
}
//显示队列的所有数据
public void showQueue() {
//遍历:
if (isEmpty()) {
System.out.println("队列空的,没有数据");
return;
}
for (int i = 0; i < arr.length; i++) {
System.out.printf("arr[%d]=%d\n", i, arr[i]);
}
}
//显示队列的头数据,不是取出数据
public int headQueue() {
if (isEmpty()) {
throw new RuntimeException("队列中没有数据");
}
return arr[front + 1];
}
public int getFront() {
return front;
}
public int getRear() {
return rear;
}
}
问题分析并优化
- 目前数组使用一次就不能用,没有达到利用的效果
- 将这个数组使用算法,改进成一个环形的数组 取模:%
014 数组模拟环形队列的思路分析
对前面的数组模拟队列的优化,充分利用数组,因此将数组看做是一个环形的。(通过取模的方式来实现)
分析说明:
- 尾索引的下一个为头索引时表示队列满,即将队列容量空出一个作为约定,这个在做判断队列满的时候需要注意 (rear + 1 ) % maxSize == fron 满
- rear == front 空
015 数组模拟环形队列实现
将数组看做是一个环形。(通过取模的方式来实现)
分析说明:
- 尾索引的下一个为头索引时表示队列满,即将队列容量空出一个作为约定,这个在做判断队列满的时候需要注意 (rear + 1) % maxSize == front 即满
- rear == front 空
package com.old.queue;
import java.util.Arrays;
import java.util.Scanner;
/**
* 环形数组队列
*/
public class CircleArrayQueue {
public static void main(String[] args) {
CircleArray queue = new CircleArray(3);
Scanner scanner = new Scanner(System.in);
boolean loop = true;
while (loop) {
System.out.println("1.显示队列");
System.out.println("2.退出程序");
System.out.println("3.添加数据到队列");
System.out.println("4.从队列取出数据");
System.out.println("5.查看头数据");
String s = scanner.nextLine();
try {
switch (s) {
case "1":
queue.showQueue();
break;
case "2":
scanner.close();
System.out.println("扫描器已关闭");
loop = false;
break;
case "3":
System.out.println("请输入数字:");
String value = scanner.nextLine();
queue.addQueue(Integer.valueOf(value));
break;
case "4":
System.out.println("取出:" + queue.getQueue());
break;
case "5":
System.out.println("取出:" + queue.headQueue());
break;
}
} catch (Exception e) {
System.out.println(e.getCause());
}
System.out.println();
System.out.println("队列头,fron:" + queue.getFront());
System.out.println("队列尾,rear:" + queue.getRear());
System.out.println();
}
}
}
//使用数组模拟队列
class CircleArray {
private int maxSize;//数组最大容量
/**
* 队列头
* front 变量的含义做一个调整:front 就指向队列的第一个元素,也就是说
* arr[front] front 的初始值 = 0
*/
private int front;
/**
* 队列尾
* front 变量的含义做一个调整: rear 指向队列的最后一个元素的后一个位置,
* 因为希望突出一个
* rear 的初始值 = 0
*/
private int rear;
private int[] arr;//该数组用于存放数据,模拟队列
//创建队列的构造器
public CircleArray(int arrMaxSize) {
this.maxSize = arrMaxSize;
arr = new int[maxSize];
front = 0;//队列头部,分析出,front 是指向队列头的前一个位置
rear = 0;//指向队列尾,指向队列尾的数据(即就是队列最后一个数据)
}
//判断队列是否满
public boolean isFull() {
//如果头一个等于最大容量
return (rear + 1) % maxSize == front;
}
//判断队列是否为空
public boolean isEmpty() {
return rear == front;
}
//添加数据到队列
public void addQueue(int n) {
//判断是否满,
if (isFull()) {
System.out.println("队列满,不能加入数据");
return;
}
//因为 rear 本身就指向下一个元素,所以直接将数据加入
arr[rear] = n;
rear = (rear + 1) % maxSize;
}
//获取队列数据 出队列
public int getQueue() {
//判断队列是否为空
if (isEmpty()) {
throw new RuntimeException("队列中没有数据");
}
/**
* front 本身就指向了队列的第一个元素
* 1. 先把 front 对应的值保留到一个临时变量
* 2. 将 front 后移,考虑取模. front++; 不能直接++
* 3. 将临时保存的变量返回
*/
int value = arr[front];
front = (front + 1) % maxSize;
return value;
}
//显示队列的所有数据
public void showQueue() {
//遍历:
if (isEmpty()) {
System.out.println("队列空的,没有数据");
return;
}
/**
* 从 front 开始遍历,遍历多少个元素
*/
for (int i = front; i < front + size(); i++) {
int index = i % maxSize;
System.out.printf("arr[%d]=%d\n", index, arr[index]);
}
}
//求出当前队列有效数据的个数
public int size() {
return (rear + maxSize - front) % maxSize;
}
//显示队列的头数据,不是取出数据
public int headQueue() {
if (isEmpty()) {
throw new RuntimeException("队列中没有数据");
}
return arr[front];
}
public int getFront() {
return front;
}
public int getRear() {
return rear;
}
}