稀疏数组和队列
稀疏sparsearray数组
需求:
编写的五子棋程序中,有存盘退出和续上盘的功能。
分析问题:
因为该二维数组的很多值是默认值 0, 因此记录了 很多没有意义的数据.-> 稀疏数组。
基本介绍
当一个数组中大部分元素为0,或者为同一个值的数组时,可以使用稀疏数组来保存该数组。
稀疏数组的处理方法是:
- 记录数组 一共有几行几列,有多少个不同的值
- 把具有不同值的元素的行列及值记录在一个小规模的数组中,从而 缩小程序的规模
应用实例
- 使用稀疏数组,来保留类似前面的二维数组(棋盘、地图等等)
- 把稀疏数组存盘,并且可以从新恢复原来的二维数组数
- 整体思路分析
代码实现:
package com.hspedu.sparsearray_;
import java.io.*;
/**
* @ClassName
* @Description
* @Author zxk
* @DateTime 2022-02-15-23:58
* @Version 稀疏数组
*/
public class SparseArray {
public static void main(String[] args) {
int[][] chessArr1=new int[11][11];
//此处添加棋子的坐标即数组元素
chessArr1[1][2]=1;
chessArr1[2][3]=2;
chessArr1[5][6]=1;
//遍历原始二维数组
for (int[] row : chessArr1) {
for (int i : row) {//打印行
System.out.print("\t"+i);
}
System.out.println();//换行
}
//声明稀疏数组
int count=0;
for (int i = 0; i < chessArr1.length; i++) {
for (int j = 0; j < chessArr1[i].length; j++) {
if (chessArr1[i][j] != 0) {
count++;
}
}
}
System.out.println();
int[][] sparseArr=new int[count+1][3];
//初始化稀疏数组
sparseArr[0][0] = 11;//row
sparseArr[0][1] = 11;//column
sparseArr[0][2]=count;//count
int num=0;
for (int i = 0; i < chessArr1.length; i++) {
for (int j = 0; j < chessArr1[i].length; j++) {
if (chessArr1[i][j] != 0) {
num++;
sparseArr[num][0]=i;
sparseArr[num][1]=j;
sparseArr[num][2]=chessArr1[i][j];
}
}
}
//遍历稀疏数组
System.out.println("得到稀疏数组:");
for (int[] row : sparseArr) {
for (int i : row) {//打印行
System.out.print("\t"+i);
}
System.out.println();//换行
}
//调用存盘
keep(sparseArr);
//调用读取
int[][] backSparseArr=get();
//恢复后的稀疏数组
System.out.println("恢复后的稀疏数组:");
for (int[] row : backSparseArr) {
for (int i : row) {//打印行
System.out.print("\t"+i);
}
System.out.println();//换行
}
///将稀疏数组 --》 恢复成 原始的二维数组
int[][] chessArr02=new int[backSparseArr[0][0]][backSparseArr[0][1]];
for (int i = 1; i < backSparseArr.length; i++) {
chessArr02[backSparseArr[i][0]][backSparseArr[i][1]]=backSparseArr[i][2];
}
//遍历恢复后的二维数组
System.out.println("由稀疏数组恢复后的二维数组:");
for (int[] row : chessArr02) {
for (int i : row) {//打印行
System.out.print("\t"+i);
}
System.out.println();//换行
}
}
//写入方法
public static void keep(int[][] arr) {
BufferedWriter bw=null;
try {
bw = new BufferedWriter(new FileWriter("f:\\io\\sparseArr.txt"));
bw.write(""+arr[0][2]);//存入原始二维数组非零个数,待恢复稀疏数组时声明行数时使用
bw.newLine();
for (int i = 0; i < arr.length; i++) {
bw.write(arr[i][0] + " " + arr[i][1] + " " + arr[i][2]);//写入每行元素
bw.newLine();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bw != null) {
bw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//读取方法
public static int[][] get() {
String data;
BufferedReader br=null;
int row=0;//用于表示数组行数但并非行数
int count=0;//表示读到第几行
int[][] arr=null;
try {
br = new BufferedReader(new FileReader("f:\\io\\sparseArr.txt"));
row=Integer.parseInt(br.readLine());//数组非零个数用于表示行数
arr=new int[row+1][3];
while ((data = br.readLine()) != null) {
String[] s = data.split(" ");
arr[count][0]=Integer.parseInt(s[0]);
arr[count][1]=Integer.parseInt(s[1]);
arr[count][2]=Integer.parseInt(s[2]);
count++;//刷新数组的行数
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return arr;
}
}
队列
使用场景
银行排队:
队列介绍
- 队列是一个 有序列表,可以用 数组或是 链表来实现。
- 遵循 先入先出的原则。即: 先存入队列的数据,要先取出。后存入的要后取出
- 示意图:(使用数组模拟队列示意图)
数组模拟(直型)队列
思路:
- 队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图, 其中 maxSize 是该队列的最大容量。
- 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量 front 及 rear 分别记录队列前后端的下标,front 会随着数据输出而改变,而 rear 则是随着数据输入而改变,如图所示:
- 当我们将数据存入队列时称为”addQueue”,addQueue 的处理需要有两个步骤:思路分析
- 将尾指针往后移:rear+1 , 当 front == rear 【空】
- 若尾指针 rear 小于队列的最大下标 maxSize-1,则将数据存入 rear 所指的数组元素中,否则无法存入数据。
rear == maxSize - 1[队列满]
代码实现:
public class ArrayQueueDemo {
public static void main(String[] args) {
ArrayQueue arrayQueue = new ArrayQueue(3);
char key = ' ';
Scanner sc = new Scanner(System.in);
boolean loop = true;
while (loop) {//菜单
System.out.println("s(show):显示队列");
System.out.println("e(exit):退出程序");
System.out.println("a(add):添加数据到队列");
System.out.println("g(get):取出数据");
System.out.println("h(head):查看队列头部数据");
System.out.println("请输入指令:");
key = sc.next().charAt(0);
switch (key) {
case 's':
arrayQueue.show();
break;
case 'e':
loop = false;
break;
case 'a':
System.out.println("输入一个数");
int value = sc.nextInt();
arrayQueue.add(value);
break;
case 'g':
try {
int num = arrayQueue.get();
System.out.println("取出的数为:"+num);
} catch (Exception e) {
e.printStackTrace();
}
break;
case 'h':
try {
int head = arrayQueue.head();
System.out.println("头部为:"+head);
} catch (Exception e) {
e.printStackTrace();
}
default:
break;
}
}
System.out.println("程序退出~");
}
}
//提示:指针只有在添加数据或取出数据时才后移
class ArrayQueue {
private int maxSize;//容量
private int rear;//尾
private int front;//头
private int[] arr;//
public ArrayQueue(int num) {
rear = -1;
front = -1;
maxSize = num;
arr = new int[maxSize];
}
//判断数组已满
public boolean isFull() {
return rear==maxSize-1;
}
//判断数组为空
public boolean isEmpty() {
return front==rear;
}
//添加方法
public void add(int num) {
if (isFull()) {
System.out.println("满了,添加失败");
return;
}
rear++;
arr[rear]=num;
}
//取出方法
public int get() throws Exception {
if (isEmpty()) {
System.out.println("空的,没法取出");
throw new Exception("数组为空,无法取出");
}
front++;
return arr[front];
}
//遍历方法
public void show() {
if (isEmpty()) {
System.out.println("数组为空");
return;
}
for (int i = 0; i < arr.length; i++) {
System.out.println("arr[" + i+"]=" + arr[i]);
}
}
//显示头部数据方法
public int head() throws Exception {
if (isEmpty()) {
throw new Exception("数组为空");
}
return arr[front+1];
}
}
问题分析并优化:
- 目前数组使用一次就不能用, 没有达到复用的效果
- 将这个数组使用算法,改进成一个列 环形的队列 取模:%
数组模拟(环形)队列
对前面的数组模拟队列的优化,充分利用数组. 因此将数组看做是一个环形的。(通过 取模的方式来实现即可)
分析说明:
- 尾索引的下一个为头索引时表示队列满,即将队列容量空出一个作为约定,这个在做判断队列满的
时候需要注意 (rear + 1) % maxSize == front 满] - rear == front [空]
- 分析示意图:
代码实现:
//数组模拟环形队列
@SuppressWarnings({"all"})
public class QueueDemo {
public static void main(String[] args) {
CircleQueue circleQueue = new CircleQueue(3);
char key = ' ';
Scanner sc = new Scanner(System.in);
boolean loop = true;
while (loop) {//菜单
System.out.println("s(show):显示队列");
System.out.println("e(exit):退出程序");
System.out.println("a(add):添加数据到队列");
System.out.println("g(get):取出数据");
System.out.println("h(head):查看队列头部数据");
System.out.println("请输入指令:");
key = sc.next().charAt(0);
switch (key) {
case 's':
circleQueue.show();
break;
case 'e':
loop = false;
break;
case 'a':
System.out.println("输入一个数");
int value = sc.nextInt();
circleQueue.add(value);
break;
case 'g':
try {
int num = circleQueue.get();
System.out.println("取出的数为:"+num);
} catch (Exception e) {
e.printStackTrace();
}
break;
case 'h':
try {
int head = circleQueue.head();
System.out.println("头部为:"+head);
} catch (Exception e) {
e.printStackTrace();
}
default:
break;
}
}
System.out.println("程序退出~");
}
}
//提示:指针只有在添加数据或取出数据时才后移
class CircleQueue {
private int maxSize;//容量
private int rear;//尾
private int front;//头
private int[] arr;//
public CircleQueue(int num) {
maxSize = num;
arr = new int[maxSize];
}
//判断数组已满
public boolean isFull() {
return (rear+1)%maxSize==front;
}
//判断数组为空
public boolean isEmpty() {
return front==rear;
}
//添加方法
public void add(int num) {
if (isFull()) {
System.out.println("满了,添加失败");
return;
}
arr[rear] = num;
rear = (rear + 1) % maxSize;
}
//取出方法
public int get() throws Exception {
if (isEmpty()) {
System.out.println("空的,没法取出");
throw new Exception("数组为空,无法取出");
}
int value= arr[front];
front = (front + 1) % maxSize;
return value;
}
//遍历方法
public void show() {
if (isEmpty()) {
System.out.println("数组为空");
return;
}
for (int i = front; i < front+(rear+maxSize-front)%maxSize; i++) {
System.out.println("arr[" + i%maxSize+"]=" + arr[i%maxSize]);
}
}
//显示头部数据方法
public int head() throws Exception {
if (isEmpty()) {
throw new Exception("数组为空");
}
return arr[front];
}
}