3.1稀疏sparsearray数组
3.1.1关于稀疏数组的实际需求
3.1.2基本介绍
1.当一个数组中大部分元素为0,或者为同一个值的数组时,可以使用稀疏数组来保存该数组。
2.稀疏数组的处理方式是:
(1)记录数组一共有几行几列,有多少个不同的值
(2)把具有不同值的元素的行列及值记录在一个小规模的数组中,从而缩小程序的规模
3.1.3应用实例
(1)使用稀疏数组,来保存类似前面的二维数组(棋盘、地图等等)
(2)把稀疏数组存盘,并且可以从新恢复原来的二维数组数
(3)整体思路分析
二维数组 转 稀疏数组的思路:
(1)遍历原始的二维数组,得到有效的数据的个数sum
(2)根据sum就可以创建稀疏数组sparseArr int[sum + 1] [3]
(3)将二维数组的有效数据存入到稀疏数组
稀疏数组转原始的二维数组的思路
(1)先读取稀疏数组的第一行,根据第一行的数据,创建原始的二维数组,比如上面的 chessArr2=int [11] [11]
(2)在读取稀疏数组后几行的数据,并赋值给原始的二维数组即可。
(4)代码演示
public class SparseArray {
public static void main(String[] args) {
//一、创建原始数组
//1表示黑子,2表示蓝子
int chessArray[][]=new int[11][11];
chessArray[1][2]=1;
chessArray[2][3]=2;
//打印原始数组
System.out.println("输出原始数组");
for (int[] array: chessArray){
for (int data: array){
System.out.printf("%d\t",data);
}
System.out.println();
}
System.out.println();
//二、将原始数组转为稀疏sparseArray数组
//1.遍历原始数组有几个有效数字
int sum=0; //使用sum作为标识
for (int[] array: chessArray){
for (int data: array){
if (data!=0){
sum++;
}
}
}
//2.创建稀疏数组
int sparseArray[][]=new int[sum+1][3];
sparseArray[0][0]=chessArray.length;
sparseArray[0][1]=chessArray[0].length;
sparseArray[0][2]=sum;
//3.遍历原始数组并给稀疏数组赋值
int dataSum=0; //标识当前是第几个值
for (int i=0;i<chessArray.length;i++){
for (int j=0;j<chessArray[i].length;j++){
if (chessArray[i][j]!=0){
dataSum++;
sparseArray[dataSum][0]=i;
sparseArray[dataSum][1]=j;
sparseArray[dataSum][2]=chessArray[i][j];
}
}
}
//4.打印稀疏数组
System.out.println("稀疏数组为:");
for (int i=0;i<sparseArray.length;i++){
System.out.printf("%d\t%d\t%d\t\n",sparseArray[i][0],sparseArray[i][1],sparseArray[i][2]);
System.out.println();
}
//三、将稀疏数组转为原始数组
//1.先读取第一行创建chessArray2数组
int chessArray2[][]=new int[sparseArray[0][0]][sparseArray[0][1]];
//2.从第二行开始读取信息
for (int i=1;i<sparseArray.length;i++){
chessArray2[sparseArray[i][0]][sparseArray[i][1]]=sparseArray[i][2];
}
//打印稀疏数组转换后的数组
System.out.println("稀疏数组---> 原始数组");
for (int[] array2: chessArray2){
for (int data2: array2){
System.out.printf("%d\t",data2);
}
System.out.println();
}
}
}
(5)结果演示(结果下面没截全)
3.2 队列
3.2.1队列的一个使用场景
银行排队的案例:
3.2.2 队列的介绍
(1)队列是一个有序列表,可以用数组或是链表来实现
(2)遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出。
(3)用数组模拟队列的示意图如下:
3.2.3数组模拟队列的思路
(1)队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图,其中maxSize是该队列的最大容量
(2)因为队列的输出、输入是分别从前后端来处理,因此需要两个变量front及rear分别记录队列前后端的下标,front会随着数据输出而改变,而rear则是随着数据输入而改变,如图所示:
(3)当我们将数据存入队列时称为“addQueue”,addQueue的处理需要有两个步骤:思路分析
1.将尾部指针往后移:rear+1,当front==rear【空】
2.若尾指针rear小于队列的最大下标maxSize-1,则将数据存入rear所指的数组元素中,否则无法存入数据。rear == maxSize - 1【队列满】
代码如下:
public class ArrayQueueDemo {
public static void main(String[] args) {
//测试数组实现的队列
ArrayQueue queue = new ArrayQueue(3);
char ch = ' '; //存放用户的输入字符
boolean loop=true;
Scanner sc = new Scanner(System.in);
//创建菜单测试队列
while (loop){
System.out.println("s(show):展示队列数据");
System.out.println("a(add):向队列中添加信息");
System.out.println("d(delete):弹出队列的数据");
System.out.println("h(head):展示队列的头部信息");
System.out.println("e(exit):退出程序");
ch= sc.next().charAt(0); //接收控制台的字符
switch (ch){
case 's': //展示队列元素
queue.showQueue();
break;
case 'a': //添加元素
System.out.println("请输入要添加的值");
int i = sc.nextInt();
queue.addQueue(i);
break;
case 'd': //弹出元素
try {
int i1 = queue.deleteQueue();
System.out.printf("弹出元素为:%d\n",i1);
break;
}catch (Exception e){
e.getMessage();
}
case 'h': //查看队列头元素
try {
int i2 = queue.headQueue();
System.out.printf("头部元素为:%d\n",i2);
break;
}catch (Exception e){
e.getMessage();
}
case 'e':
sc.close(); //关闭资源
loop =false;
break;
default:
break;
}
}
System.out.println("退出菜单~~~");
}
}
//使用数组来实现队列
class ArrayQueue{
private int maxSize; //队列的最大容量
private int front; //指向队列的头部的前一个位置(不包括头部的第一个数据)
private int rear; //指向队列的尾部数据(包括尾部的数据)
private int[] arr; //使用此数组实现队列
//使用构造方法初始化成员变量,创建此队列时需传入队列容量的大小
public ArrayQueue(int arrMaxSize){
maxSize = arrMaxSize;
front = -1; //初始化front,指向队列头部的前一个位置
rear = -1; //初始化rear,指向队列头部前一个位置
arr = new int[arrMaxSize]; //初始化数组
}
//判断队列是否满
public boolean isFull(){
return rear == maxSize-1;
}
//判断队列是否为空
public boolean isEmpty(){
return rear==front;
}
//向队列中添加元素
public void addQueue(int data){
if (isFull()){
System.out.println("队列已满,添加数据失败!");
return; //return;的作用相当于break;用于中断循环的作用(常用于返回值为void中)
}
rear++; //队列从尾部添加数据,所以rear后移
arr[rear]=data;
}
//向队列中取出元素
public int deleteQueue(){
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)目前数组使用一次就不能用,没有达到复用的效果
(2)将这个数组使用算法,改进成一个环形的队列 取模: %
3.2.4数组模拟环形队列
对前面的数组模拟队列的优化,充分利用数组,因此将数组看做是一个环形的。(通过取模的方式来实现即可)
@分析说明:
(1)尾索引的下一个为头索引时表示队列满,即将队列容量空出一个作为约定,这个在做判断队列满的时候需要注意(rear+1)%maxSize == front【满】
(2)rear = = front【空】
(3)分析示意图:
代码如下:
public class CircleQueueDemo {
public static void main(String[] args) {
//测试数组实现的队列
CircleQueue queue = new CircleQueue(4); //容量为4,但此时是环形队列,所以有效数为3个
char ch = ' '; //存放用户的输入字符
boolean loop=true;
Scanner sc = new Scanner(System.in);
//创建菜单测试队列
while (loop){
System.out.println("s(show):展示队列数据");
System.out.println("a(add):向队列中添加信息");
System.out.println("d(delete):弹出队列的数据");
System.out.println("h(head):展示队列的头部信息");
System.out.println("e(exit):退出程序");
ch= sc.next().charAt(0); //接收控制台的字符
switch (ch){
case 's': //展示队列元素
queue.showQueue();
break;
case 'a': //添加元素
System.out.println("请输入要添加的值");
int i = sc.nextInt();
queue.addQueue(i);
break;
case 'd': //弹出元素
try {
int i1 = queue.deleteQueue();
System.out.printf("弹出元素为:%d\n",i1);
break;
}catch (Exception e){
e.getMessage();
}
case 'h': //查看队列头元素
try {
int i2 = queue.headQueue();
System.out.printf("头部元素为:%d\n",i2);
break;
}catch (Exception e){
e.getMessage();
}
case 'e':
sc.close(); //关闭资源
loop =false;
break;
default:
break;
}
}
System.out.println("退出菜单~~~");
}
}
//创建环形队列(解决上述代码的bug)
class CircleQueue{
private int maxSize; //队列的最大容量
private int front; //front指向队列的第一个元素,也就是说arr【front】就是队列的第一个元素
//front的初始值=0
private int rear; //rear指向队列的最后一个元素的后一个位置,因为希望空出一个空间作为约定
//rear的初始值=0
private int[] arr; //使用此数组实现队列
//使用构造方法初始化成员变量,创建此队列时需传入队列容量的大小
public CircleQueue(int arrMaxSize){
maxSize = arrMaxSize;
arr = new int[arrMaxSize]; //初始化数组
}
//判断队列是否满
public boolean isFull(){
return (rear+1)%maxSize == front;
}
//判断队列是否为空
public boolean isEmpty(){
return rear==front;
}
//向队列中添加元素
public void addQueue(int data){
if (isFull()){
System.out.println("队列已满,添加数据失败!");
return; //return;的作用相当于break;用于中断循环的作用(常用于返回值为void中)
}
//直接在尾部添加数据即可
arr[rear]=data;
rear=(rear+1)%maxSize; //取模,防止越界
}
//向队列中取出元素
public int deleteQueue(){
if (isEmpty()){
throw new RuntimeException("队列没有数据,取出数据失败"); //抛出异常
}
//1.首先用临时变量将需取出元素存起来
//2.移动front的位置 ,需要取模
//3.返回临时变量
int variable=arr[front];
front=(front+1)%maxSize;
return variable;
}
//展示队列中的元素
public void showQueue(){
if (isEmpty()){
System.out.println("队列没有数据,展示数据失败");
return;
}
//此时使用环形队列,不可对整个数组进行遍历
for (int i=front;i<front+(rear+maxSize-front)%maxSize;i++){
System.out.printf("arr[%d]=%d\n",i%maxSize,arr[i%maxSize]);
}
}
//展示队列的头部元素(注意是展示,不是取出)
public int headQueue(){
if (isEmpty()){
throw new RuntimeException("队列没有数据,展示数据失败");
}
return arr[front];
}
}