JAVA數據結構與算法(一)

數據結構

數據結構包括:性結構非線性結前端

性結構java

1)性結構做爲最經常使用的數據結構,其特色是數據元素之間存在一對一的線性關系(一維數組)node

2) 線性結 構有 兩種不一樣的存儲結構,即順序存儲結構(數組)和鏈式存儲結構(鏈表)。順序存儲的線性表稱爲順序表,順序表中的存儲元素是連續
3) 式存儲的線性表稱爲鏈表,鏈表中的存儲元素不必定是連續的,元素節點中存放數據元素以及相鄰元素的地址信
4) 線性結 見的有: 隊列、鏈表和棧。
 

稀疏數組sparsearray

當一個數組中大部分元素爲0,或者爲同一個值的數組時,可使用稀疏數組來保存該數組。面試

疏數組的處理方法是:算法

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. 先遍歷二維數組 獲得非0數據的個數
		int sum = 0;
		for (int i = 0; i < 11; i++) {
			for (int j = 0; j < 11; j++) {
				if (chessArr1[i][j] != 0) {
					sum++;
				}
			}
		}
		// 2. 建立對應的稀疏數組
		int sparseArr[][] = new int[sum + 1][3];
		// 給稀疏數組賦值
		sparseArr[0][0] = 11;
		sparseArr[0][1] = 11;
		sparseArr[0][2] = sum;	
		// 遍歷二維數組,將非0的值存放到 sparseArr中
		int count = 0; //count 用於記錄是第幾個非0數據
		for (int i = 0; i < 11; i++) {
			for (int j = 0; j < 11; 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\t\n", sparseArr[i][0], sparseArr[i][1], sparseArr[i][2]);
		}
		System.out.println();

	}

}

//======================================================================
		//將稀疏數組 --》 恢復成 原始的二維數組
		/*
		 *  1. 先讀取稀疏數組的第一行,根據第一行的數據,建立原始的二維數組
			2. 在讀取稀疏數組後幾行的數據,並賦給 原始的二維數組 便可.
		 */
		
		//1. 先讀取稀疏數組的第一行,根據第一行的數據,建立原始的二維數組
		
		int chessArr2[][] = new int[sparseArr[0][0]][sparseArr[0][1]];
		
		//2. 在讀取稀疏數組後幾行的數據(從第二行開始),並賦給 原始的二維數組 便可
		
		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();
		}

隊列

列是一個有序列表,能夠用 數組 或是 鏈表 來實現
先入先出 的原則。即 :先 入隊 列的數據,要先取出。後存入的要後取

用數組模擬隊列

// 使用數組模擬隊列-編寫一個ArrayQueue類
class ArrayQueue {
	private int maxSize; // 表示數組的最大容量
	private int front; // 隊列頭
	private int rear; // 隊列尾
	private int[] arr; // 該數據用於存放數據, 模擬隊列

// 建立隊列的構造器
	public ArrayQueue(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 static void main(String[] args) {
		//建立一個隊列
		ArrayQueue queue = new ArrayQueue(3);
       addQueue(3);
       addQueue(5);
       addQueue(7);
      showQueue();


}

數組模擬環形隊列

對前面的數組模擬隊列的優化,充分利用數組,所以將數組看作是一個環形的。小程序

思路以下:數組

1.front的含義作一個調整:front就指向隊列的第一個元素,也就是說arr【front】就是隊列的第一個元素。數據結構

2.rear變量的含義作一個調整:rear指向隊列的最後一個元素的後一個位置。由於但願空出一個空間作約定。ide

3.當隊列滿時,(隊尾下標+1)%數組長度=隊頭下標測試

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

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

鏈表

表是有序的列表,可是它在內存中是存儲以下

:

1) 鏈表是以節點的方式來存儲 , 是鏈式存儲
2) 個節點包含 data 域, next 域:指向下一個節點 .
3) 圖:發現鏈表的 各個節點不必定是連續存儲 .
4) 表分帶頭節點的鏈表和沒有頭節點的鏈表,根據實際的需求來肯定

單向鏈表的代碼實現

使用帶head頭的單向鏈表實現 水滸英雄排行榜管

1)在添加英雄時,直接添加到鏈表的尾部

 
//定義HeroNode , 每一個HeroNode 對象就是一個節點
class HeroNode {
	public int no;
	public String name;
	public String nickname;
	public HeroNode next; //指向下一個節點
	//構造器
	public HeroNode(int no, String name, String nickname) {
		this.no = no;
		this.name = name;
		this.nickname = nickname;
	}
	//爲了顯示方法,咱們從新toString
	@Override
	public String toString() {
		return "HeroNode [no=" + no + ", name=" + name + ", nickname=" + nickname + "]";
	}
	
}
//定義SingleLinkedList 管理咱們的英雄
class SingleLinkedList {
	//先初始化一個頭節點, 頭節點不要動, 不存放具體的數據
	private HeroNode head = new HeroNode(0, "", "");
	
	
	//返回頭節點
	public HeroNode getHead() {
		return head;
	}

	//添加節點到單向鏈表
	//思路,當不考慮編號順序時
	//1. 找到當前鏈表的最後節點
	//2. 將最後這個節點的next 指向 新的節點
	public void add(HeroNode heroNode) {
		
		//由於head節點不能動,所以咱們須要一個輔助遍歷 temp
		HeroNode temp = head;
		//遍歷鏈表,找到最後
		while(true) {
			//找到鏈表的最後
			if(temp.next == null) {//
				break;
			}
			//若是沒有找到最後, 將將temp後移
			temp = temp.next;
		}
		//當退出while循環時,temp就指向了鏈表的最後
		//將最後這個節點的next 指向 新的節點
		temp.next = heroNode;
	}

單鏈表面試題

1)求單鏈表中有效節點的個數

/**
* @param head 鏈表的頭結點
* @return 返回的就是有效節點的個數
*/

public static int getLength(HeroNode head){
   if(head.next == null) { //空鏈表
			return 0;
		}
		int length = 0;
		//定義一個輔助的變量, 這裏咱們沒有統計頭節點
		HeroNode cur = head.next;
		while(cur != null) {
			length++;
			cur = cur.next; //遍歷
		}
		return length;
	}
}

2)將單鏈表反轉

public static void reversetList(HeroNode head) {
//若是當前鏈表爲空,或者只有一個節點,無需反轉,直接返回
		if(head.next == null || head.next.next == null) {
			return ;
		}
//定義一個輔助的指針(變量),幫助咱們遍歷原來的鏈表
		HeroNode cur = head.next;
        HeroNode next = null;// 指向當前節點[cur]的下一個節點
        HeroNode reverseHead = new HeroNode(0, "", "");

//遍歷原來的鏈表,每遍歷一個節點,就將其取出,並放在新的鏈表reverseHead 的最前端
        while(cur != null) { 
			next = cur.next;//先暫時保存當前節點的下一個節點,由於後面須要使用
			cur.next = reverseHead.next;//將cur的下一個節點指向新的鏈表的最前端
			reverseHead.next = cur; //將cur 鏈接到新的鏈表上
			cur = next;//讓cur後移
		}
//將head.next 指向 reverseHead.next , 實現單鏈表的反轉
		head.next = reverseHead.next;
	}

單項環形鏈表應用場景

Josephu(約瑟夫、約瑟夫環問題

Josephu  問題爲:設編號爲12… nn我的圍坐一圈,約定編號爲k1<=k<=n)的人從1開始報數,數到m 的那我的出列,它的下一位又從1開始報數,數到m的那我的又出列,依次類推,直到全部人出列爲止,由此產生一個出隊編號的序列

:用一個不帶頭結點的循環鏈表來處理Josephu 問題:先構成一個有n個結點的單循環鏈表,而後由k結點起從1開始計數,計到m時,對應結點從鏈表中刪除,而後再從被刪除結點的下一個結點又從1開始計數,直到最後一個結點從鏈表中刪除算法結束。

構建一個單向的環形鏈表思路

1. 先建立第一個節點, first 指向該節點,並造成環形

2. 後面當咱們每建立一個新的節點,就把該節點,加入到已有的環形鏈表中便可.

 

遍歷環形鏈表

1. 先讓一個輔助指針(變量) curBoy,指向first節點

2. 而後經過一個while循環遍歷 該環形鏈表便可 curBoy.next  == first 結束

// 建立一個Node類,表示一個節點
class Node{
	private int no;// 編號
	private Node next; // 指向下一個節點,默認null

	public Node(int no) {
		this.no = no;
	}

	public int getNo() {
		return no;
	}

	public void setNo(int no) {
		this.no = no;
	}

	public Node getNext() {
		return next;
	}

	public void setNext(Node next) {
		this.next = next;
	}

}
// 建立一個環形的單向鏈表
class CircleSingleLinkedList {
	// 建立一個first節點,當前沒有編號
	private Node first = null;

	// 添加小孩節點,構建成一個環形的鏈表
	public void addNode(int nums) {
		// nums 作一個數據校驗
		if (nums < 1) {
			System.out.println("nums的值不正確");
			return;
		}
		Node curNode = null; // 輔助指針,幫助構建環形鏈表
		// 使用for來建立咱們的環形鏈表
		for (int i = 1; i <= nums; i++) {
			// 根據編號,建立小孩節點
			Node node = new Node(i);
			// 若是是第一個小孩
			if (i == 1) {
				first = node;
				first.setNext(first); // 構成環
				curNode = first; // 讓curNode指向第一個節點
			} else {
				curNode.setNext(node);//
				boy.setNext(first);//
				curNode = node;
			}
		}
	}

	// 遍歷當前的環形鏈表
	public void showNode() {
		// 判斷鏈表是否爲空
		if (first == null) {
			System.out.println("沒有任何節點~~");
			return;
		}
		// 由於first不能動,所以咱們仍然使用一個輔助指針完成遍歷
		Node curNode = first;
		while (true) {
			System.out.printf("節點的編號 %d \n", curNode.getNo());
			if (curNode.getNext() == first) {// 說明已經遍歷完畢
				break;
			}
			curNode = curNode.getNext(); // curNode後移
		}
	}

	// 根據用戶的輸入,計算出小孩出圈的順序
	/**
	 * 
	 * @param startNo
	 *            表示從第幾個小孩開始數數
	 * @param countNum
	 *            表示數幾下
	 * @param nums
	 *            表示最初有多少小孩在圈中
	 */
	public void countNode(int startNo, int countNum, int nums) {
		// 先對數據進行校驗
		if (first == null || startNo < 1 || startNo > nums) {
			System.out.println("參數輸入有誤, 請從新輸入");
			return;
		}
		// 建立要給輔助指針,幫助完成小孩出圈
		Node helper = first;
		// 需求建立一個輔助指針(變量) helper , 事先應該指向環形鏈表的最後這個節點
		while (true) {
			if (helper.getNext() == first) { // 說明helper指向最後小孩節點
				break;
			}
			helper = helper.getNext();
		}
		//節點報數前,先讓 first 和  helper 移動 k - 1次
		for(int j = 0; j < startNo - 1; j++) {
			first = first.getNext();
			helper = helper.getNext();
		}
		//當節點報數時,讓first 和 helper 指針同時 的移動  m  - 1 次, 而後出圈
		//這裏是一個循環操做,知道圈中只有一個節點
		while(true) {
			if(helper == first) { //說明圈中只有一個節點
				break;
			}
			//讓 first 和 helper 指針同時 的移動 countNum - 1
			for(int j = 0; j < countNum - 1; j++) {
				first = first.getNext();
				helper = helper.getNext();
			}
			//這時first指向的節點,就是要出圈的節點
			System.out.printf("節點%d出圈\n", first.getNo());
			//這時將first指向的節點出圈
			first = first.getNext();
			helper.setNext(first); //
			
		}
		System.out.printf("最後留在圈中的編號%d \n", first.getNo());
		
	}
}

 測試:

public static void main(String[] args) {
        // 測試一把看看構建環形鏈表,和遍歷是否ok
        CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
        circleSingleLinkedList.addNode(125);// 加入5個小孩節點
        circleSingleLinkedList.showNode();

        //測試一把小孩出圈是否正確
        circleSingleLinkedList.countNode(10, 5, 25); 
        
    }

 

 

經典算法面試題

字符串匹配問題::

1) 有一個字符串 str1= "" bbc abcdab abcdabcdabcde "" ,和一個子串 str2="abcdabc "
2) 如今要判斷 str1 是否含有 str2 , 若是存在,就返回第一次出現的位置 , 若是沒有,則返回 - 1
3) 求用最快的速度來完成匹配
4) 的思路是什麼?
答:KMP算法

 

塔遊戲, 請完成漢諾塔遊戲的代碼: 要求:

1) A塔的全部圓盤移動到C塔。而且規定,小圓盤上不能放大圓盤,在三根柱子之間一次只能移動一個圓

答:分治算法.

 

 

皇后問題,是一個古老而著名的問題,是回溯算法的典型案例。該問題是國際西洋棋棋手馬克斯·貝瑟爾於1848年提出:在8×8格的國際象棋上擺放八個皇后,使其不能互相攻擊,即:意兩個皇后都不能處於同一行、同一列或同一斜線上,問有多少種擺法【92】

答:回溯算法

 

踏棋盤算法介紹和遊戲演示

1) 踏棋盤算 被稱爲騎 士周遊問
2) 馬隨機放在國際象棋的 8×8 棋盤 Board[0 7][0 7] 的某個方格中,馬按走棋規 ( 馬走日字 ) 行移動。要求每一個方格只進入一次,走遍棋盤上所有 64 個方
答:圖的深度優化遍歷算法(DFS) + 貪心算法優化
相關文章
相關標籤/搜索