Chapter 2 稀疏数组和队列
2.1 稀疏数组
2.1.1 基本介绍
当一个数组中大部分元素为0,或者为同一个值得数组时,可以使用稀疏数组来保存该数组(压缩冗余数据)
稀疏数组的表示方法:
- 第一行记录一共有几行几列,有多少个不同的值(相对于冗余的值)
- 把具有不同值的元素的行列及值记录在一个小规模的数组中,从而缩小程序的规模
2.1.2 应用案例
二维数组转稀疏数组的思路:
- 遍历原始的二维数组,得到有效数据的个数sum
- 根据sum构建稀疏数组sparseArr int[sum+1][3]
- 将二维数组的有效数据存入稀疏数组
稀疏数组转二维数组的思路:
- 读取稀疏数组的第一行,创建原始的二维数组
- 读取稀疏数组的后几行的数据,赋给原始的二维数组
public class SparseArray {
public static void main(String[] args) {
//构建原始棋盘数组
int[][] chessArr = new int[11][11];
chessArr[1][2]=1;
chessArr[2][3]=2;
//打印棋盘数组
for(int[] row : chessArr){
for(int item : row){
System.out.printf("%d\t", item);
}
System.out.println();
}
//循环统计棋盘数组的有效数据个数
int sum = 0;
for(int[] row:chessArr){
for(int item:row){
if(item!=0) sum++;
}
}
//根据sum构建稀疏数组
int[][] sparseArr = new int[sum+1][3];
sparseArr[0][0] = 11;
sparseArr[0][1] = 11;
sparseArr[0][2] = sum;
//再次循环棋盘数组,将有效数据存入稀疏数组
int cntels = 0;//统计第n个有效数据
for(int i=0;i< chessArr.length;++i){
for(int j=0;j<chessArr[i].length;++j){
if(chessArr[i][j]!=0){
cntels++;
sparseArr[cntels][0] = i;
sparseArr[cntels][1] = j;
sparseArr[cntels][2] = chessArr[i][j];
}
}
}
//打印稀疏数组
for(int[] row : sparseArr ){
for(int item : row){
System.out.printf("%d\t", item);
}
System.out.println();
}
//构建待还原的棋盘数组
int[][] newChessArr = new int[sparseArr[0][0]][sparseArr[0][1]];
//根据稀疏矩阵的后几行数据还原棋盘数组
for(int i=1;i< sparseArr.length;++i){
newChessArr[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
}
//打印还原后的棋盘数组
for(int[] row : chessArr){
for(int item : row){
System.out.printf("%d\t", item);
}
System.out.println();
}
}
}
2.2 队列
2.2.1 队列介绍
- 队列是一个有序列表,可以用数组或是链表来实现
- 遵循先进先出的原则
2.2.2 用数组模拟顺序队列
思路:有一个maxSize来指示队列的最大容量,用front表示队列的头(队列头部元素的前一个位置),用rear表示队列的尾(队列尾部的元素)
实现:
/**
* The type Array queue test.
* @author ybs
*/
public class ArrayQueueTest {
public static void main(String[] args) {
int size=0;
char operation;
boolean loop = true;
Scanner in = new Scanner(System.in);
System.out.print("输入列表长度:");
size = in.nextInt();
ArrayQueue queue = new ArrayQueue(size);
while(loop){
System.out.println("e(exit):退出程序");
System.out.println("a(add):添加元素到队列");
System.out.println("g(get):获取队列元素");
System.out.println("l(list):打印队列");
System.out.println("请输入操作:");
operation = in.next().charAt(0);
switch (operation) {
case 'a':
int element;
System.out.print("输入添加的元素:");
element = in.nextInt();
queue.add(element);
break;
case 'g':
try {
System.out.println("取出的元素为:" + queue.getElement());
}catch (RuntimeException e){
System.out.println(e.getMessage());
}
break;
case 'l':
queue.listQueue();
break;
case 'e':
in.close();
loop = false;
break;
}
}
System.out.println("程序退出");
}
}
class ArrayQueue{
/**
* maxSize 队列最大长度
* front 队头指针
* rear 队尾指针
* arr 数组存放队列数据,模拟队列
*/
private int maxSize;
private int front;
private int rear;
private int[] arr;
/**
* 数组列表构造器
*/
public ArrayQueue(int maxSize){
this.maxSize = maxSize;
arr = new int[maxSize];
front=-1;
rear=-1;
}
/**
* 判断列表是否为空
*/
private boolean isNull(){
return front == rear;
}
private boolean isFull(){
return rear == maxSize-1;
}
/**
* 添加列表元素
*/
public void add(int num){
if(isFull()){
System.out.println("队满,无法加入!");
return ;
}
rear++;
arr[rear]=num;
System.out.println("添加成功!");
}
/**
* 取出队头元素
* @return 队头元素
*/
public int getElement(){
if (isNull()){
throw new RuntimeException("空列表,无法获取元素");
}
front++;
return arr[front];
}
/**
* 打印队列
*/
public void listQueue(){
if (isNull()){
System.out.println("空列表");
}
for(int i=0;i<=rear;++i){
System.out.printf("arr[%d]=%d\n",i,arr[i]);
}
}
}
问题分析:存在假溢出问题,可以采用循环队列解决
2.2.3 用数组模拟循环队列
通过取模的方式将数组看做是一个循环队列
思路:front指向队列的第一个元素,rear指向队列最后一个元素的后一位,front和rear的初始值都为0,队列满时 (rear+1)%maxSize=front 成立,队列空时 rear=front 成立,循环队列的有效数据个数为 (rear+maxSize-front)%maxSize
实现:
/**
* The type Circle array queue.
* @author ybs
*/
public class CircleArrayQueueTest {
public static void main(String[] args) {
int size;
char operation;
boolean loop = true;
Scanner in = new Scanner(System.in);
System.out.print("输入列表长度:");
size = in.nextInt();
CircleArrayQueue queue = new CircleArrayQueue(size);
while(loop){
System.out.println("e(exit):退出程序");
System.out.println("a(add):添加元素到队列");
System.out.println("g(get):获取队列元素");
System.out.println("l(list):打印队列");
System.out.println("请输入操作:");
operation = in.next().charAt(0);
switch (operation) {
case 'a':
int element;
System.out.print("输入添加的元素:");
element = in.nextInt();
queue.add(element);
break;
case 'g':
try {
System.out.println("取出的元素为:" + queue.getElement());
}catch (RuntimeException e){
System.out.println(e.getMessage());
}
break;
case 'l':
queue.listQueue();
break;
case 'e':
in.close();
loop = false;
break;
default:
break;
}
}
System.out.println("程序退出");
}
}
class CircleArrayQueue{
/**
* maxSize 队列最大长度
* front 队头指针
* rear 队尾指针
* arr 数组存放队列数据,模拟队列
*/
private int maxSize;
private int front;
private int rear;
private int[] arr;
public CircleArrayQueue(int maxSize){
this.maxSize = maxSize;
arr = new int[maxSize];
front = 0;
rear = 0;
}
private boolean isNull(){
return rear == front;
}
private boolean isFull(){
return ((rear + 1) % maxSize) == front;
}
public void add(int num){
if (isFull()) {
System.out.println("队满,无法插入");
return;
}
//rear本身指向了队列尾的后一个元素,所以先赋值再移动指针
arr[rear]=num;
rear = (rear + 1) % maxSize;
System.out.println("添加成功");
}
public int getElement(){
if (isNull()){
throw new RuntimeException("队满,无法获取");
}
//front本身指向了队头元素,所以先取出再移动指针
int getNum = arr[front];
front = (front + 1) % maxSize;
return getNum;
}
public void listQueue(){
if(isNull()){
System.out.println("队空,无法遍历");
return;
}
//此时无法按照队列长度遍历,使用从front往后遍历多少个数来解决
for (int i = front; i < front + elementsSize(); i++) {
System.out.printf("arr[%d]=%d\n",i%maxSize,arr[i%maxSize]);
}
}
private int elementsSize(){
return (rear+maxSize-front)%maxSize;
}
}