稀疏陣列和佇列
1.稀疏陣列
所謂稀疏陣列就是當陣列中大部分的內容值都未被使用(或都為零),在陣列中僅有少部分的空間使用。因此造成記憶體空間的浪費,為了節省記憶體空間,並且不影響陣列中原有的內容值,我們可以使用稀疏陣列去壓縮資料。OK,如果你不明白,那我們來看一個例子。
在一個五子棋中,有存檔和續上盤的功能
分析問題:因為該二維陣列的很多預設值是 0,因此記錄了很多沒有意義的資料 > 稀疏陣列
1.1 解決方法
思路
記錄陣列一共有幾行幾列,有多少個不同的值
把具有不同值的元素的行列及值記錄在一個小規模的陣列中,從而縮小程式的範圍
應用例項
- 使用稀疏陣列,來保留類似前面的二維陣列(棋盤、地圖等等)
- 把稀疏陣列存檔,並且可以從新恢復為原來的二維陣列
- 整體思路
1.2 程式碼實現
public class SparseArray {
public static void main(String[] args) {
//建立一個二維陣列
//0:表示沒有棋子 1表示黑子 2表示藍子
int chessArr[][] = new int[11][10];
chessArr[1][2] = 1;
chessArr[2][3] = 2;
for(int[] row:chessArr){
for(int data:row){
System.out.printf("%d\t",data);
}
System.out.println();
}
int[][] array = getSparseArray(chessArr);
System.out.println("-------");
for(int i = 0 ; i< array.length;i++){
System.out.printf("%d\t%d\t%d\t\n",array[i][0],array[i][1],array[i][2]);
}
System.out.println("--------");
int[][] startArr = recovery(array);
for(int[] row:startArr){
for(int data:row){
System.out.printf("%d\t",data);
}
System.out.println();
}
}
/**
* 將普通陣列轉換為稀疏陣列
* @param chessArr
* @return
*/
public static int[][] getSparseArray(int[][] chessArr){
if(!checkIsRight(chessArr)){
return null;
}
//1.拿到陣列後 首先獲取元素的個數,然後才能建立稀疏陣列
int sum = 0;
for(int[] arr:chessArr){
for(int i:arr){
if(i != 0){
sum++;
}
}
}
//2.建立稀疏陣列
int[][] sparseArr = new int[sum+1][3];
sparseArr[0][0] = chessArr.length; //行
sparseArr[0][1] = chessArr[0].length;//列
sparseArr[0][2] = sum; //元素個數
//3.陣列存放
int count = 0;
for(int i = 0; i <chessArr.length; i++ ){
for(int j = 0; j <chessArr[i].length;j++ ){
if(chessArr[i][j] != 0){
sparseArr[++count][0] = i;//行
sparseArr[count][1] = j;//列
sparseArr[count][2] = chessArr[i][j];
}
}
}
return sparseArr;
}
/**
* 將稀疏陣列轉回普通陣列
* @param sparseArr
* @return
*/
public static int[][] recovery(int[][] sparseArr){
if(!checkIsRight(sparseArr)){
return null;
}
//獲取原陣列的 行數和列數 並建立原陣列
int arr[][] = new int[sparseArr[0][0]][sparseArr[0][1]];
for(int i = 1; i < sparseArr.length;i++){
arr[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
}
return arr;
}
public static boolean checkIsRight(int[][] arr){
if(arr == null || arr.length <= 1 ){
return false;
}
return true;
}
}
2. 佇列
- 佇列是一個有序列表,可以用陣列或連結串列來實現
- 遵循先入先出的原則
2.1 陣列模擬佇列
- 佇列本身是有序列表,若使用陣列的資料結構來儲存佇列的資料,則佇列的陣列宣告如上圖,其中maxSize是該佇列的最大容量
- 因為佇列的輸出、輸入分別從頭尾端來處理,因此需要兩個變數front及rear分別記錄佇列頭尾端的下標,front會隨著資料輸出而改變,而rear會隨著佇列的輸入而改變
- 當我們將資料輸入佇列時稱為
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(10);
queue.addQueue(1);
queue.addQueue(2);
queue.addQueue(3);
queue.addQueue(4);
queue.getQueue();
queue.showQueue();
}
}
//使用陣列模擬佇列-編寫一個ArrayQueue類
class ArrayQueue{
private int maxSize; //表示陣列的最大容量
private int front;//佇列頭
private int rear;//佇列尾
private int[] arr; //該陣列用於存放資料,模擬佇列
public ArrayQueue(int maxSize){
this.maxSize = maxSize;
arr = new int[maxSize];
this.front = -1;
this.rear = -1;
}
//判斷佇列是否已滿
public boolean isFull(){
return rear == maxSize - 1;
}
//判斷佇列是否為空
public boolean isEmpty(){
return front == rear;
}
//新增資料到佇列
public void addQueue(int n){
//判斷佇列是否滿
if(isFull()){
System.out.println("佇列已滿");
return;
}
arr[++rear] = n;
}
//獲取佇列的資料,出佇列
public int getQueue(){
if(isEmpty()){
throw new RuntimeException("佇列已空");
}
return arr[++front];
}
//顯示佇列所有資料
public void showQueue(){
if(isEmpty()){
System.out.println("佇列已空");
return;
}
for(int i = front+1 ;i <= rear;i++){
System.out.printf("arr[%d]=%d\n",i,arr[i]);
}
}
}
2.2 陣列模擬環形佇列
之前實現的佇列存在一個明顯的問題,就是陣列使用一次就不能再用了,出佇列資料的位置始終空在那,沒有達到一個複用的效果,因此我們要對這個佇列進行一次優化,將此佇列變成一個環形佇列
思路
front 變數的含義做一個調整:front就指向佇列的第一個元素,也就是 arr[front] 就代表佇列的第一個元素,
front初始值 = 0rear 的變數含義做一個調整:rear指向最後一個元素的後一個位置,因為希望空出一個空間作為約定,rear的初始值 = 0
當佇列滿時,條件是 (rear + 1) % maxSize == front 【滿】
當佇列為空的條件,rear == front 空
當我們這樣分析,佇列中有效的資料的個數 (rear + maxSize - front) % maxSize
我們就可以在原來的佇列上修改得到 一個環形佇列
程式碼實現
public class CircleArrayQueueDemo {
public static void main(String[] args) {
//測試一把
System.out.println("測試陣列模擬環形佇列的案例");
//建立一個環形佇列 說明設定4,其佇列資料最大是3
CircleArray queue = new CircleArray(4);
char key = ' ';//接收使用者輸入
Scanner scanner = 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):檢視佇列頭的資料");
key = scanner.next().charAt(0);
switch (key) {
case 's':
queue.showQueue();
break;
case 'a':
System.out.println("輸出一個數字");
int value = scanner.nextInt();
queue.addQueue(value);
break;
case 'g':
try {
int res = queue.getQueue();
System.out.printf("取出的資料是%d\n",res);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'h'://檢視佇列頭的資料
try {
int res = queue.headQueue();
System.out.printf("佇列頭的資料是%d\n",res);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'e'://退出
scanner.close();
loop = false;
break;
default:
break;
}
}
}
}
class CircleArray{
private int maxSize;
private int front;
private int rear;
private int[] arr;
public CircleArray(int maxSize){
this.maxSize = maxSize;
arr = new int[maxSize];
}
//判斷佇列是否已滿
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("佇列為空,不能取資料");
}
int value = arr[front];
front = (front+1)%maxSize;
return value;
}
//顯示佇列的所有資料
public void showQueue(){
//遍歷
if(isEmpty()){
System.out.println("佇列為空,沒有資料");
return;
}
for(int i = front; i < front + size() ; i++){
System.out.printf("arr[%d]=%d\n",i%maxSize,arr[i%maxSize]);
}
}
//求出當前佇列有效資料的個數
public int size(){
//加上maxSize 防止模出負數 因為這是一個環形佇列
return (rear + maxSize - front)%maxSize;
}
//顯示佇列的頭資料
public int headQueue(){
//判斷
if(isEmpty()){
throw new RuntimeException("佇列是空的,~沒有資料");
}
return arr[front];
}
}