✅作者简介:热爱Java后端开发的一名学习者,大家可以跟我一起讨论各种问题喔。
🍎个人主页:Hhzzy99
🍊个人信条:坚持就是胜利!
💞当前专栏:Java数据结构与算法
🥭本文内容:稀疏数组和数组实现队列和环形队列
文章目录
- 稀疏数组
- 队列
- 环形队列
- 结语
稀疏数组
基本介绍
当一个数组大部分元素为0,或者为同一个值的数组时,可以使用稀疏数组来保存该数组。
稀疏数组的处理方法:
- 记录数组一 共有几行几列,有多少个不同的值。
- 把具有不同值的元素的行列及值记录在一个小规模的数组中,从而 缩小程序 的规模。
二维数组转稀疏数组的思路
1.遍历原始的二维数组,得到有效数据的个数sum
2.根据sum就可以创建稀疏数组sparseArr int[sum+ 1][3]
3.将二维数组的有效数据存入到稀疏数组
稀疏数组转原始的二维数组思路
1.先读取稀疏数组的第一行,根据第一行的数据,创建原始的二维数组
2.再读取稀疏数组后几行的数据,并赋给原始的二维数组即可。
代码实现
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] = 2;
//输出原始的二维数组
System.out.println("原始的二维数组");
for (int[] row :
chessArr1) {
for (int data:
row) {
System.out.printf("%d\t",data);
}
System.out.println();
}
//将二维数组转换成稀疏数组
//1.先遍历二维数组 得到非零的值
int sum = 0;
for (int[] row :
chessArr1) {
for (int data:
row) {
if(data != 0)
sum++;
}
}
//2.创建对应的稀疏数组
int sparseArr[][] = new int[sum+1][3];
//给稀疏数组赋值
sparseArr[0][0] = chessArr1.length;
sparseArr[0][1] = chessArr1[0].length;
sparseArr[0][2] = sum;
//3.遍历二维数组,将非零的值存放到稀疏数组中
int count = 0;//用于记录第几个非零数据
for (int i = 0; i < chessArr1.length; i++) {
for (int j = 0; j < chessArr1[0].length; j++) {
if(chessArr1[i][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\n",sparseArr[i][0],sparseArr[i][1],sparseArr[i][2]);
}
//将稀疏数组恢复成原始数组
int chessArr2[][] = new int[sparseArr[0][0]][sparseArr[0][1]];
//读取稀疏数组后几行的数据,赋值给原始数组
for (int i = 1; i < sparseArr.length; i++) {
chessArr2[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
}
//恢复后的二维数组
System.out.println();
System.out.println("恢复后的二维数组");
for (int[] row :
chessArr2) {
for (int data:
row) {
System.out.printf("%d\t",data);
}
System.out.println();
}
}
}
队列
队列介绍
- 队列是一个有序列表 ,可以用数组或者链表来实现
- 遵循先入先出的原则。
- 示意图:(数组实现)
- 队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图,其中 maxSize 是该队列的最大容量。
- 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量front 及 rear分别记录队列前后端的下标,front会随着数据输出而改变,而rear 则是随着数据输入而改变,如图所示:
- 当我们将数据存入队列时称为
addQueue
,addQueue
的处理需要有两个步骤:
1)将尾指针往后移: rear+1,当front == rear
【空】
2)若尾指针rear小于队列的最大下标maxSize-1
,则将数据存入rear所指的数组元素中,否则无法存入数据。rear==maxSize-1
[队列满]
代码实现:
public class ArrayQueue {
public static void main(String[] args) {
//测试,创建一个队列
ArrayQueue1 arrayQueue1 = new ArrayQueue1(3);
char key = ' ';//接受用户输入
Scanner scanner = new Scanner(System.in);
boolean loop = true;
//输出一个菜单
while (loop){
System.out.println("s(show):显示队列");
System.out.println("e(show):退出程序");
System.out.println("a(show):添加数据");
System.out.println("g(show):从队列取出数据");
System.out.println("h(head):查看队列头数据");
key = scanner.next().charAt(0);//接受一个字符
switch(key){
case 's'://显示数据
arrayQueue1.showQueue();
break;
case 'a'://添加数据
System.out.println("请输入一个数");
int value = scanner.nextInt();
arrayQueue1.addQueue(value);
break;
case 'g'://取出数据
try {
int res = arrayQueue1.getQueue();
System.out.printf("取出的数据是%d\n",res);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'h'://查看队列头的数据
try{
int res = arrayQueue1.headQueue();
System.out.printf("队列头的数据是%d\n",res);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'e'://退出
scanner.close();
loop = false;
break;
}
}
System.out.println("程序退出");
}
}
//使用数组模拟队列-编写一个ArrayQueue类
class ArrayQueue1{
private int maxsize;//表示数组最大容量
private int front;//队列头
private int rear;//队列尾
private int[] arr;//该数据用于存放数据,模拟队列
//创建队列的构造器
public ArrayQueue1(int arrMaxSize){
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++;//让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];
}
}
问题与优化
1)目前数组使用一次就不能用,没有达到复用的效果
2)将这个数组使用算法,改进成一个 环形的队列 取模:%
环形队列
对前面的数组模拟队列的优化,充分利用数组。因此将数组看做是一个环形的。(通过取模的方式来实现即可)
分析:
1)尾索引的下一个为头索引时表示队列满,即将队列容量空出一个作为约定,这个在做判断队列满的时候需要注意(rear + 1) % maxSize == front
【满】
2)rear == front
【空】
3)分析示意图:
思路如下:
- front变量的含义做一个调整:front就指向队列的第一个元素,也就是说
arr[front]
就是队列的第一个元素front的初始值=0 - rear变量的含义做一个调整:rear指向队列的最后一个元素的后一个位置。因为希望空出一个空间散为约定.rear的初始值=0
- 当队列满时,条件是
(rear +1) %maxSize = front
【满】 - 对队列为空的条件,
rear== front
【空】 - 当我们这样分析,队列中有效的数据的个数
(rear + maxSize - front)%maxSize
// rear = 1 front =06。 - 我们就可以在原来的队列上修改得到一个环形队列
代码实现
public class CircleArrayQueue {
public static void main(String[] args) {
//测试
System.out.println("测试数组模拟环形队列的案例");
//创建一个环形队列
CircleArray arrayQueue1 = new CircleArray(4);//说明设置4,其队列的有效数据最大值是3
char key = ' ';//接受用户输入
Scanner scanner = new Scanner(System.in);
boolean loop = true;
//输出一个菜单
while (loop){
System.out.println("s(show):显示队列");
System.out.println("e(show):退出程序");
System.out.println("a(show):添加数据");
System.out.println("g(show):从队列取出数据");
System.out.println("h(head):查看队列头数据");
key = scanner.next().charAt(0);//接受一个字符
switch(key){
case 's'://显示数据
arrayQueue1.showQueue();
break;
case 'a'://添加数据
System.out.println("请输入一个数");
int value = scanner.nextInt();
arrayQueue1.addQueue(value);
break;
case 'g'://取出数据
try {
int res = arrayQueue1.getQueue();
System.out.printf("取出的数据是%d\n",res);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'h'://查看队列头的数据
try{
int res = arrayQueue1.headQueue();
System.out.printf("队列头的数据是%d\n",res);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'e'://退出
scanner.close();
loop = false;
break;
}
}
System.out.println("程序退出");
}
}
class CircleArray{
private int maxsize;//表示数组最大容量
private int front;//就是指向队列的第一个元素,arr[front] //初始值为0
private int rear;//指向队列的最后一个元素位置再+1 //初始值0
private int[] arr;//该数据用于存放数据,模拟队列
public CircleArray(int arrMaxSize){
maxsize = arrMaxSize;
arr = new int[maxsize];
front = 0;
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;
}
//直接将数据加入
arr[rear] = n;
//将rear后移,这里必须考虑取模
rear = (rear + 1) % maxsize;
}
//获取队列的数据,出队列
public int getQueue(){
//判断队列是否空
if(isEmpty()){
//通过抛出异常
throw new RuntimeException("队列为空,不能取出数据!");
}
// 这里需要分析出front是指向队列的第一个元素
//1.先把front对应的值保存到一个临时变量
//2.将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++) {
System.out.printf("arr[%d]=%d\n",i % maxsize,arr[i % maxsize]);
}
}
//求出当前队列有效数据的个数
public int size(){
return (rear - front + maxsize) % maxsize;
}
//显示队列的头数据,注意不是取出数据
public int headQueue(){
//判断
if (isEmpty())
throw new RuntimeException("队列为空");
return arr[front];
}
}
结语
本文的内容主要是Java数据结构与算法中的稀疏数组和队列,大家如果感兴趣可以点点赞,关注一下,你们的支持是我最强大的动力,非常感谢您的阅读(❁´◡`❁)