數據結構包括:線性結構和非線性結構前端
線性結構java
1)線性結構做爲最經常使用的數據結構,其特色是數據元素之間存在一對一的線性關系(一維數組)node
當一個數組中大部分元素爲0,或者爲同一個值的數組時,可使用稀疏數組來保存該數組。面試
稀疏數組的處理方法是:算法
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
鏈表是有序的列表,可是它在內存中是存儲以下
小結:
使用帶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 問題爲:設編號爲1,2,… n的n我的圍坐一圈,約定編號爲k(1<=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) 將A塔的全部圓盤移動到C塔。而且規定,小圓盤上不能放大圓盤,在三根柱子之間一次只能移動一個圓盤
答:分治算法.
八皇后問題,是一個古老而著名的問題,是回溯算法的典型案例。該問題是國際西洋棋棋手馬克斯·貝瑟爾於1848年提出:在8×8格的國際象棋上擺放八個皇后,使其不能互相攻擊,即:任意兩個皇后都不能處於同一行、同一列或同一斜線上,問有多少種擺法。【92】
答:回溯算法
馬踏棋盤算法介紹和遊戲演示