一、稀疏数组
完整代码:
SparseArray.java
实际需求
稀疏数组的介绍说明
作用说明:就是对于一些大部分元素为0的数组,可以转化为稀疏数组来记录原数组的信息,从而达到减少空间存储的功能。
数组描述:稀疏数组分为多行三列,第一行记录的是数组的行数、列数以及不为0的元素个数,接着将不为0的元素的行列位置以及元素值依次放在第一行之后的012位置上。
应用实例以及代码思路
代码过程(说明+整体过程)
说明:
① 这个类具有创建初始数组的方法,将数组打印的方法,将稀疏数组转换为二维数组打印的方法,对于打印数组可以重复进行利用。
② 整体就是,二维数组 —> 稀疏数组 —> 二维数组 打印输出。
③ 对于其中的存储到硬盘使用到了ObjectOutputStream、ObjectInputStream来进行存储与读取,使用到了IO流中序列化与反序列化的操作。
package com.mjj.test;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SparseArray {
public static void main(String[] args) throws Exception{
int chessarr[][] = createArr();
//System.out.println("初始创建的数组:");
//printArr(chessarr);
//使用num来获取二维数组中非0的个数
int num=0;
for(int []a:chessarr) {
for(int ele:a) {
if(ele!=0) {
num++;
}
}
}
//创建稀疏数组:开辟空间
int [][]sparseArray = new int[num+1][3];
//给稀疏数组的第一行赋值:原始数组行数、列数、以及不为0数
sparseArray[0][0] = 11;
sparseArray[0][1] = 11;
sparseArray[0][2] = num;
int len = 0;
for(int i=0;i<chessarr.length;i++) {
for(int j=0;j<chessarr[i].length;j++) {
if(chessarr[i][j]!=0) {
len++;
sparseArray[len][0] = i;
sparseArray[len][1] = j;
sparseArray[len][2] = chessarr[i][j];
}
}
}
System.out.println("打印稀疏数组:");
printArr(sparseArray);
System.out.println();
//保存到磁盘
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("sparseArr.data"));
oos.writeObject(sparseArray);
//从磁盘中读取稀疏数组
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("sparseArr.data"));
Object readObject = ois.readObject();
int [][]sparseArr2 = (int[][]) readObject;
System.out.println("打印一下从磁盘转换的数组:");
printArr(sparseArr2);
// //获取稀疏数组转回的二维数组并打印
// int[][] chessarr2 = transferArr(sparseArray);
// System.out.println("打印出由稀疏数组转化的棋盘:");
// printArr(chessarr2);
}
//创建原来数组
public static int[][] createArr() {
int[][] chessarr = new int[11][11];
chessarr[1][2] = 1;
chessarr[2][3] = 2;
chessarr[4][5] = 2;
return chessarr;
}
// 将二维数组打印
public static void printArr(int arr[][]) {
for (int[] a : arr) {
for (int ele : a) {
System.out.print(ele + "\t");
}
System.out.println();
}
}
//将传入的稀疏数组转为对应数组传出
public static int[][] transferArr(int sparseArr[][]){
//开辟新的数组
int arr[][] = new int[sparseArr[0][0]][sparseArr[0][1]];
for(int i=1;i<=sparseArr[0][2];i++) {
arr[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
}
return arr;
}
}
二、队列
队列的介绍
、
① 数组模拟队列思路(实现+问题)
完整代码:
ArrayQueue.java
数组队列说明以及代码实现
说明:
① 对于队列有四个成员变量,开辟最大空间值maxsize、入列指向rear、出队指向front以及数组。(初始rear、front为-1)
② 对于队列中的方法有判断队列为空、判断队列已满、入队操作、出队操作、获取队头、展示数组信息。
判断队列为空**:rear==front
判断队列已满:rear==maxsize-1
入队操作:判断是否已满,rear+1,数组中对应下标赋值
出队操作:判断是否为空,front+1,返回下标元素
获取队头:**判断是否为空,通过front+1获取队头。
package com.mjj.test;
import java.util.Scanner;
public class ArrayQueue {
public static void main(String[] args) {
//首先创建队列实例
MyQueue queue = new MyQueue(3);
//用于读入数据
Scanner scanner = new Scanner(System.in);
char c;
boolean flag = true;
while(flag) {
System.out.println("输入以下字符进行队列操作:");
System.out.println("s/S (show):展示队列中的内容");
System.out.println("a/A (add):进行入队操作");
System.out.println("o/O (out):进行出队操作");
System.out.println("g/G (get):获取队头");
System.out.println("e/E (exit):退出程序");
c = scanner.next().toUpperCase().charAt(0);
switch (c) {
case 'S'://展示队列内容
queue.showQueue();
break;
case 'A'://进行入队操作
int nextInt = scanner.nextInt();
queue.addQueue(nextInt);
break;
case 'O'://进行出队操作
try {
int outQueue = queue.outQueue();
System.out.println(outQueue);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'G'://获取队头
try {
int queueHead = queue.getQueueHead();
System.out.println(queueHead);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'E'://退出程序
try {
scanner.close();
} catch (Exception e) {
// TODO: handle exception
}
flag = false;
System.out.println("感谢您使用,再见!");
break;
default:
break;
}
}
}
}
//使用数组来进行模拟队列
class MyQueue{
private int maxsize;//用于指定数组的最大容量
private int front = -1;//用于指向出队元素的下标
private int rear = -1;//用于指向入队的下标
private int[] queue;//使用数组来进行模拟队列
//依据传入值,来对数组进行开辟空间
public MyQueue(int maxsize) {
this.maxsize = maxsize;
queue = new int[maxsize];//开辟数组控件
}
//判断队列是否为空
public boolean isEmpty() {
return front==rear;//如果两个指标都相等说明队列
}
//判断队列是否为满
public boolean isFull() {
return rear==maxsize-1;//根据开辟的最大空间与rear是否相等
}
//进行入队操作
public void addQueue(int element) {
//判断队列是否已满
if(isFull()) {
System.out.println("队列已满,无法添加数据!!!");
return;
}
rear++;
queue[rear] = element;
}
//进行出队操作
public int outQueue() {
if(isEmpty()) {
throw new RuntimeException("队列为空,无法取出数据!!!");
}
front++;
return queue[front];
}
// 查看队列中所有数据
public void showQueue() {
System.out.print("队列中的所有数据如下:");
for (int i = 0; i < maxsize; i++) {
System.out.print(queue[i] + " ");
}
System.out.println();
}
//取出队头
public int getQueueHead() {
//判断是否为空
if(isEmpty()) {
throw new RuntimeException("队列为空,无法取出队头!!!");
}
return queue[front+1];
}
}
存在空间问题(需要靠环形解决)
对于数组的空间没有能够很好的利用到,出队到一定数量时,对于之前已经出队的位置不能够很好的利用。
② 数组模拟环形队列(解决空间问题)
环形队列说明(预留不预留问题、参数问题,图解问题)
对于数组解决环形队列的问题,存在着空间预留不预留的问题。
空间预留:就是说你要开辟4个空间的数组,实际上只能存放三个数据。
空间不预留:开辟4个空间,能够真实存放四个数据。
环形队列参数说明:
maxsize:开辟数组空间数量。
rear:相当于数组下标记录入队,初始设置为0,每次入队时判断队列是否已满,再插入rear位置的数据,最后加1,需要考虑数组越界问题,(rear+1)%maxsize。
front:也是数组下标记录出队,初始设置为0,每次出队时先判断队列是否为空,再移除front位置上的数据,最后+1,也要考虑数组越界问题,(front+1)%maxsize
arrr[]:也就是数组。
为什么要说预留不预留的问题,并且有什么关键点?
① 我们来看下面不预留的情况:
队列满以及队列空的说明
② 我们来看下面预留的情况:解决不预留判断队列满或空的问题
队列满以及队列空的说明
总结:通过图解,我们可以看到使用预留的方式可以解决不预留时判断队列满与空式子相同的问题。
**
如何解决不预留时判断队列满与空的问题?
例如使用计数器:入队时count++,出队时count—,判断队列满if(count>0),队列空if(count==0)
代码实现:(空间预留解决)
方法说明:判断队列满,判断队列空,入队,出队,获取队列中的数量、展示队列中的信息以及获取队列头部数据。
其中获取队列中的数量size()方法:(rear-front+maxsize)%maxsize
见下图:
其余的思考下即可理解。
package com.mjj.test;
import java.util.Scanner;
public class AnnularArrayQueue {
public static void main(String[] args) {
// 首先创建队列实例
MyAnnularQueue queue = new MyAnnularQueue(4);
// 用于读入数据
Scanner scanner = new Scanner(System.in);
char c;
boolean flag = true;
while (flag) {
System.out.println("输入以下字符进行队列操作:");
System.out.println("s/S (show):展示队列中的内容");
System.out.println("a/A (add):进行入队操作");
System.out.println("o/O (out):进行出队操作");
System.out.println("g/G (get):获取队头");
System.out.println("e/E (exit):退出程序");
c = scanner.next().toUpperCase().charAt(0);
switch (c) {
case 'S':// 展示队列内容
queue.showQueue();
break;
case 'A':// 进行入队操作
int nextInt = scanner.nextInt();
queue.addQueue(nextInt);
break;
case 'O':// 进行出队操作
try {
int outQueue = queue.outQueue();
System.out.println(outQueue);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'G':// 获取队头
try {
int queueHead = queue.getQueueHead();
System.out.println("队头数据为:" + queueHead);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'E':// 退出程序
try {
scanner.close();
} catch (Exception e) {
// TODO: handle exception
}
flag = false;
System.out.println("感谢您使用,再见!");
break;
default:
break;
}
}
}
}
class MyAnnularQueue{
private int maxsize;//用于指定数组的最大容量
private int front = 0;//用于指向出队元素的下标,这是环形所以设置为1
private int rear = 0;//用于指向入队的下标
private int[] arr;//使用数组来进行模拟队列
////依据传入值,来对数组进行开辟空间
public MyAnnularQueue(int maxsize) {
this.maxsize = maxsize;
arr = new int[maxsize];
}
//判断队列是否为空
public boolean isEmpty() {
return rear==front;
}
//判断队列是否已满
public boolean isFull() {
return (rear+1)%maxsize == front;
}
// 进行入队操作
public void addQueue(int element) {
if (isFull()) {
System.out.println("队列已满,无法入队!!!");
return;
}
arr[rear] = element;
// 需要考虑数组越界问题
rear = (rear + 1) % maxsize;
System.out.println("element:" + element + "插入成功");
}
//进行出队操作
public int outQueue() {
if(isEmpty()) {
throw new RuntimeException("队列已空,无法出队");
}
//获取出队元素,并且该位置变为0
int num = arr[front];
arr[front] = 0;
//考虑到数组越界的问题
front = (front+1)%maxsize;
return num;
}
//获取队列中已有的元素
public int size() {
//其中+maxsize是为了解决rear-front是负数的原因
return (rear-front+maxsize)%maxsize;
}
// 查看队列中所有数据
public void showQueue() {
if(isEmpty()) {
System.out.println("队列为空,无法查看数据!");
return;
}
System.out.println("队列数据如下:");
for(int i=front;i<front+size();i++) {
System.out.printf("a[%d]=%d\t",i%maxsize,arr[i%maxsize]);
}
System.out.println();
}
//取出队头
public int getQueueHead() {
if(isEmpty()) {
throw new RuntimeException("队列中数据为空,无法取出!");
}
return arr[front];
}
}