數據結構與算法—稀疏數組和隊列


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會隨着隊列的輸入而改變
  • 當我們將數據輸入隊列時稱為 addQueueaddQueue 的處理有兩個步驟:思路分析

(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 數組模擬環形隊列

之前實現的隊列存在一個明顯的問題,就是數組使用一次就不能再用了,出隊列數據的位置始終空在那,沒有達到一個復用的效果,因此我們要對這個隊列進行一次優化,將此隊列變成一個環形隊列

思路

  1. front 變量的含義做一個調整:front就指向隊列的第一個元素,也就是 arr[front] 就代表隊列的第一個元素,
    front初始值 = 0

  2. rear 的變量含義做一個調整:rear指向最后一個元素的后一個位置,因為希望空出一個空間作為約定,rear的初始值 = 0

  3. 當隊列滿時,條件是 (rear + 1) % maxSize == front 【滿】

  4. 當隊列為空的條件,rear == front 空

  5. 當我們這樣分析,隊列中有效的數據的個數 (rear + maxSize - front) % maxSize

  6. 我們就可以在原來的隊列上修改得到 一個環形隊列

 

代碼實現

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];
	}
	
	
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM