數據結構的介紹node
1) 數據結構是一門研究算法的學科,只從有了編程語言也就有了數據結構.學好數據結構能夠編寫出更加漂亮,更加有效率的代碼。算法
2) 要學習好數據結構就要多多考慮如何將生活中遇到的問題,用程序去實現解決.編程
3) 程序 = 數據結構 + 算法小程序
編寫的五子棋程序中,有存盤退出和續上盤的功能。後端
當一個數組中大部分元素爲0,或者爲同一個值的數組時,可使用稀疏數組來保存該數組。數組
稀疏數組的處理方法是:數據結構
1) 記錄數組一共有幾行幾列,有多少個不一樣的值(有效值的個數)app
2) 把具備不一樣值的元素的行列及值記錄在一個小規模的數組中,從而縮小程序的規模編程語言
代碼實現:ide
object SparseArrayDemo { def main(args: Array[String]): Unit = { //1. 先把原始的地圖建立(二維數組) val rows = 11 val cols = 11 val chessMap1 = Array.ofDim[Int](rows, cols) chessMap1(1)(2) = 1 //表示黑子 chessMap1(2)(3) = 2 //表示藍子 chessMap1(4)(5) = 2 chessMap1(5)(6) = 1 //輸出原始的地圖 println("原始的地圖以下..") for (item1 <- chessMap1) { for (item2 <- item1) { printf("%d\t", item2) } println() } //使用稀疏數組來對chessMap1進行壓縮 //思路 //1. 使用ArrayBuffer 來保存有效數據 //2. 每個數據使用Node 對象來表示 //3. Node 的結構 class Node(val row:Int, val col:Int,val value:Int) val sparseArray = ArrayBuffer[Node]() val node1 = new Node(rows, cols, 0) sparseArray.append(node1) //遍歷chessMap1, 每發現一個非0的值,就建立給Node ,並加入到sparseArray for (i <- 0 until chessMap1.length) { //行 for (j <- 0 until chessMap1(i).length) { //列 if (chessMap1(i)(j) != 0) { //說明是一個須要保存的數據 //Node 對象 val node2 = new Node(i, j, chessMap1(i)(j)) sparseArray.append(node2) } } } //輸出稀疏數組 println("稀疏數組的狀況") for (node <- sparseArray) { printf("%d\t%d\t%d\t\n", node.row, node.col, node.value) } //將稀疏數組,恢復成原始的地圖 //1. 先從sparseArray 讀取第一個node ,並建立新的地圖 val firstNode = sparseArray(0) val chessMap2 = Array.ofDim[Int](firstNode.row, firstNode.col) //2. 從sparseArray第二個數據開始遍歷,並將數組恢復到chessMap2 for (i <- 1 until sparseArray.length) { val node = sparseArray(i) chessMap2(node.row)(node.col) = node.value } //再次輸出恢復後的原始地圖 println("恢復後原始的地圖以下..") for (item1 <- chessMap2) { for (item2 <- item1) { printf("%d\t", item2) } println() } } } //每個數據使用Node 對象來表示 class Node(val row: Int, val col: Int, val value: Int)
1) 隊列是一個有序列表,能夠用數組或是鏈表來實現。數據結構就是研究數據組織形式,而且爲算法打基礎。
2) 遵循先入先出的原則。即:先存入隊列的數據,要先取出。後存入的要後取出
3) 示意圖:(使用數組模擬隊列示意圖)
4) 線性結構,一對一關係
數組模擬隊列
rear 表示隊列尾,rear 指向隊列的最後一個元素
front 表示隊列的頭,表示指向隊列的第一個元素的前一個位置
1) 將尾指針日後移:rear+1 , front == rear 【隊列空】
2) 若尾指引 rear 小於隊列的最大下標 MaxSize-1,則將數據存入 rear所指的數組元素中,不然沒法存入數據。 rear == MaxSize - 1[隊列滿]
代碼實現:
object ArrayQueueDemo { def main(args: Array[String]): Unit = { //建立一個隊列對象 val queue = new ArrayQueue(3) var key = "" //接收用戶的輸入 //簡單寫個菜單 while (true) { println("show : 顯示隊列") println("add: 添加數據到隊列") println("get: 從隊列頭取出元素") println("peek: 查看隊列頭元素") key = StdIn.readLine() key match { case "show" => queue.showQueue() case "add" => println("請輸入一個數吧") val value = StdIn.readInt() queue.addQueue(value) case "get" => val res = queue.getQueue()//取出 //判斷res 的類型 if(res.isInstanceOf[Exception]) { //輸出異常信息 println(res.asInstanceOf[Exception].getMessage) }else { printf("取出的隊列頭元素是%d", res) } case "peek" => val res = queue.peek()//查看 //判斷res 的類型 if(res.isInstanceOf[Exception]) { //輸出異常信息 println(res.asInstanceOf[Exception].getMessage) }else { printf("隊列頭元素是%d", res) } } } } } //定義一個類ArrayQueue 表隊列 //該類會實現隊列的相關方法 //數據結構 建立-遍歷-測試-修改-刪除 class ArrayQueue(arrMaxSize: Int) { val maxSize = arrMaxSize val arr = new Array[Int](maxSize) //隊列的數據存放的數組 var front = -1 // 表示指向隊列的第一個元素的前一個位置 var rear = -1 //表示指向隊列的最後個元素 //查看隊列的頭元素,可是不取出 def peek(): Any = { if(isEmpty()) { return new Exception("隊列空,沒有數據返回"); } return arr(front + 1) } //判斷隊列是否滿 def isFull(): Boolean = { rear == maxSize - 1 } //判斷隊列是空 def isEmpty(): Boolean = { rear == front } //從隊列中取出數據 //異常時能夠加入業務邏輯 def getQueue(): Any = { if(isEmpty()) { return new Exception("隊列空,沒有數據返回"); } //將front 後移一位 front += 1 return arr(front) } //給隊列添加數據 def addQueue(num: Int): Unit = { if (isFull()) { println("隊列滿,不能加入!!") return } //添加時調整rear //1. 先將rear 後移 rear += 1 arr(rear) = num } //遍歷隊列 def showQueue(): Unit = { if (isEmpty()) { println("隊列空!!") return } for (i <- (front + 1) to rear) { printf("arr(%d)=%d\t", i, arr(i)) } println() } }
1) 尾索引的下一個爲頭索引時表示隊列滿,即將隊列容量空出一個做爲約定,這個在作判斷隊列滿的時候須要注意 (rear + 1) % maxSize == front 滿] rear == front [空]
2) 測試示意圖:
3) 代碼實現
object CircleArrayQueueDemo { def main(args: Array[String]): Unit = { //建立一個隊列對象 val queue = new CircleArrayQueue(4) var key = "" //接收用戶的輸入 //簡單寫個菜單 while (true) { println("show : 顯示隊列") println("add: 添加數據到隊列") println("get: 從隊列頭取出元素") println("peek: 查看隊列頭元素") key = StdIn.readLine() key match { case "show" => queue.showQueue() case "add" => println("請輸入一個數吧") val value = StdIn.readInt() queue.addQueue(value) case "get" => val res = queue.getQueue()//取出 //判斷res 的類型 if(res.isInstanceOf[Exception]) { //輸出異常信息 println(res.asInstanceOf[Exception].getMessage) }else { printf("取出的隊列頭元素是%d", res) } case "peek" => val res = queue.peek()//查看 //判斷res 的類型 if(res.isInstanceOf[Exception]) { //輸出異常信息 println(res.asInstanceOf[Exception].getMessage) }else { printf("隊列頭元素是%d", res) } } } } } //定義一個類CircleArrayQueue 表環形隊列 //該類會實現隊列的相關方法 //數據結構 建立-遍歷-測試--刪除 class CircleArrayQueue(arrMaxSize: Int) { val maxSize = arrMaxSize val arr = new Array[Int](maxSize) //隊列的數據存放的數組 var front = 0 // front 初始化 = 0 , front 約定 指向隊列的頭元素 var rear = 0 // rear 初始化爲 = 0, rear 指向隊列的 最後元素的後一個位置, 由於須要空出一個位置作約定 //判斷隊列是否滿 def isFull(): Boolean = { (rear + 1) % maxSize == front } //判斷隊列是空,和前面同樣 def isEmpty(): Boolean = { rear == front } //從隊列中取出數據 //異常時能夠加入業務邏輯 def getQueue(): Any = { if(isEmpty()) { return new Exception("隊列空,沒有數據返回"); } //返回front指向的值 //1. 先把 arr(front) 保存到一個臨時變量 //2. front += 1 //3. 返回臨時變量 var temp = arr(front) front = (front + 1) % maxSize return temp } //查看隊列的頭元素,可是不取出 def peek(): Any = { if(isEmpty()) { return new Exception("隊列空,沒有數據返回"); } return arr(front) } //給隊列添加數據 def addQueue(num: Int): Unit = { if (isFull()) { println("隊列滿,不能加入!!") return } //先把數據放入到arr(rear) 位置,而後後移rear arr(rear) = num rear = (rear + 1) % maxSize } //遍歷隊列 def showQueue(): Unit = { if (isEmpty()) { println("隊列空!!") return } //動腦筋 //1. 從front 開始打印 , 打印幾個元素 for (i <- front until (front + size()) ) { printf("arr(%d)=%d\t", (i % maxSize) , arr(i % maxSize)) } println() } //求出當前隊列共有多少個數據 def size(): Int = { (rear + maxSize - front) % maxSize } }
鏈表是有序的列表,可是它在內存中是存儲以下: 鏈表在內存中不必定是連續分佈.
單鏈表的介紹
單鏈表(帶頭結點) 邏輯結構示意圖以下
單鏈表的應用實例
使用帶head頭的單向鏈表實現 –水滸英雄排行榜管理
1) 完成對英雄人物的增刪改查操做, 注: 刪除和修改,查找能夠考慮學員獨立完成
2) 第一種方法在添加英雄時,直接添加到鏈表的尾部
3) 第二種方式在添加英雄時,根據排名將英雄插入到指定位置(若是有這個排名,則添加失敗,並給出提示)
4) 對單鏈表的實現分析示意圖
代碼實現:
object SingleLinkedListDemo { def main(args: Array[String]): Unit = { val node1 = new HeroNode(1, "宋江", "及時雨") val node2 = new HeroNode(2, "盧俊義", "玉麒麟") val node3 = new HeroNode(3, "吳用", "智多星") val node4 = new HeroNode(3, "林沖", "豹子頭") //建立後一個單向鏈表 val singleLinkedList = new SingleLinkedList() //加入 // singleLinkedList.add(node1) // singleLinkedList.add(node3) // singleLinkedList.add(node2) singleLinkedList.addByOrder(node2) singleLinkedList.addByOrder(node1) singleLinkedList.addByOrder(node3) singleLinkedList.addByOrder(node4) println("鏈表的狀況是") singleLinkedList.list() /* //測試修改 val node4 = new HeroNode(3, "武松", "行者") singleLinkedList.update(node4) println("鏈表修改後的狀況") singleLinkedList.list() //測試刪除節點 singleLinkedList.del(3) singleLinkedList.del(1) singleLinkedList.del(2) println("刪除後的鏈表") singleLinkedList.list() */ } } //建立一個SingleLinkedLis 表示單向鏈表 class SingleLinkedList { //定義一個頭結點 //1 . 頭結點不能動 val head = new HeroNode(0, "", "") //刪除 //需求:由於咱們是單向鏈表,所以咱們要找到要刪除節點的前一個節點,才能刪除該節點 def del(no:Int): Unit = { //先去檢測是否爲空鏈表,是退出 if(head.next == null) { println("鏈表爲空,不能遍歷~") return } //由於head不能動,須要輔助變量 var temp = head var flag = false breakable { //遍歷,找到要刪除的節點的前一個節點 while (true) { if (temp.next.no == no) { //找到了 flag = true break() } //退出條件 if (temp.next.next == null) { break() } temp = temp.next //遍歷 } } if(flag) { //找到 temp.next = temp.next.next } else { printf("你要刪除的節點no=%d 不存在~", no) } } //修改 //1. 給我一個新的結點,根據結點的no,去該對應的鏈表中 的結點的信息 def update(heroNode: HeroNode): Unit = { //先去檢測是否爲空鏈表,是退出 if(head.next == null) { println("鏈表爲空,不能遍歷~") return } //先找到要修改的結點 var temp = head.next //設置一個標識變量,表示是否找到要修改的結點[小技巧] var flag = false breakable { while (true) { if (temp.no == heroNode.no) { //說明找到 flag = true break() } if (temp.next == null) { break() } temp = temp.next //後移 } } if(flag) { //修改 temp.name = heroNode.name temp.nickName = heroNode.nickName }else { printf("你要修改的節點no=%d 不存在~", heroNode.no) } } //遍歷 def list(): Unit = { //先去檢測是否爲空鏈表,是退出 if(head.next == null) { println("鏈表爲空,不能遍歷~") return } //1.定義一個輔助變量,幫助遍歷整個鏈表 //由於有效的節點是從head後一個 var temp = head.next breakable { while (true) { //先輸出temp指向的節點信息 printf("結點信息爲 no = %d name=%s nickname=%s\n", temp.no, temp.name, temp.nickName) //判斷是否temp是否是最後一個結點 if (temp.next == null) { break() } temp = temp.next //讓temp後移,實現遍歷 } } } //添加結點到鏈表 //第二種方式在添加英雄時,根據排名將英雄插入到指定位置(若是有這個排名,則添加失敗,並給出提示) def addByOrder(heroNode: HeroNode): Unit = { var temp = head var flag = false breakable { while (true) { if (temp.next == null) { //加入到鏈表的最後 flag = true break() } if (temp.next.no < heroNode.no) { //說明 heroNode 添加到temp的後面 flag = true break() } else if (temp.next.no == heroNode.no) { //在鏈表中已經存在這個編號 break() } temp = temp.next //後移 } } if(flag) { //加入到temp的後面 heroNode.next = temp.next // temp.next = heroNode }else { printf("你要加入的編號%d 已經存在不能添加\n", heroNode.no) } } //添加結點到單向鏈表 def add(heroNode: HeroNode): Unit = { //1. 先找到鏈表的最後結點 //2. 先定義一個輔助變量(指針),指向 head var temp = head //3.遍歷鏈表,直到到temp.next == null breakable { while (true) { if (temp.next == null) { //temp已是最後結點 break() } temp = temp.next //temp後移 } } //4. 當退出while時,temp 指向最後結點 temp.next = heroNode } } //建立一個HeroNode 表示節點 class HeroNode(hNo: Int, hName: String, hNickname: String) { val no = hNo var name = hName var nickName = hNickname var next: HeroNode = null //next默認爲null }
使用帶head頭的雙向鏈表實現 –水滸英雄排行榜管理單向鏈表的缺點分析:
1) 單向鏈表,查找的方向只能是一個方向,而雙向鏈表能夠向前或者向後查找。
2) 單向鏈表不能自我刪除,須要靠輔助節點 ,而雙向鏈表,則能夠自我刪除,因此前面咱們單鏈表刪除
時節點,老是找到temp的下一個節點來刪除的(認真體會).
3) 示意圖幫助理解刪除
代碼實現:
import scala.util.control.Breaks.{break, breakable} object DoubleLinkedListDemo { def main(args: Array[String]): Unit = { //測試雙向鏈表的添加和遍歷 val node1 = new HeroNode2(1, "宋江", "及時雨") val node2 = new HeroNode2(2, "盧俊義", "玉麒麟") val node3 = new HeroNode2(3, "吳用", "智多星") val node4 = new HeroNode2(3, "林沖", "豹子頭") val doubleLinkedList = new DoubleLinkedList() doubleLinkedList.add(node3) doubleLinkedList.add(node1) doubleLinkedList.add(node2) println("雙向鏈表的狀況") doubleLinkedList.list() doubleLinkedList.update(node4) println("雙向鏈表修改後") doubleLinkedList.list() //作刪除 doubleLinkedList.del(3) println("雙向鏈表刪除結點後") doubleLinkedList.list() } } //定義一個帶頭結點的雙向鏈表 //添加,遍歷, 修改, 刪除 class DoubleLinkedList { //定義一個頭結點 //1 . 頭結點不能動 val head = new HeroNode2(0, "", "") // 增長一個 var last 在咱們的雙向鏈表的最後 // 1. 順序遍歷list // 2. 逆序遍歷last-> pre ... //雙向鏈表的第二種添加方式(順序添加) //add2 //刪除-直接找到要刪除的結點,自我刪除 def del(no:Int): Unit = { //先去檢測是否爲空鏈表,是退出 if(head.next == null) { println("鏈表爲空,不能遍歷~") return } //直接讓temp指向head.next,而後定位 var temp = head.next var flag = false breakable { while (true) { if (temp.no == no) { flag = true break() //找到 } if (temp.next == null) { break(); //沒有這個刪除的結點 } temp = temp.next } } //判斷是否須要刪除 if(flag) { //刪除 temp.pre.next = temp.next if(temp.next != null) {//!!! temp.next.pre = temp.pre } }else { //沒有找到 printf("你要刪除的no=%d 結點不存在", no) } } //修改和前面同樣的 //修改 //1. 給我一個新的結點,根據結點的no,去該對應的鏈表中 的結點的信息 def update(heroNode: HeroNode2): Unit = { //先去檢測是否爲空鏈表,是退出 if(head.next == null) { println("鏈表爲空,不能遍歷~") return } //先找到要修改的結點 var temp = head.next //設置一個標識變量,表示是否找到要修改的結點[小技巧] var flag = false breakable { while (true) { if (temp.no == heroNode.no) { //說明找到 flag = true break() } if (temp.next == null) { break() } temp = temp.next //後移 } } if(flag) { //修改 temp.name = heroNode.name temp.nickName = heroNode.nickName }else { printf("你要修改的節點no=%d 不存在~", heroNode.no) } } //雙向鏈表的遍歷和單向鏈表同樣(指的是從head開始) //遍歷 def list(): Unit = { //先去檢測是否爲空鏈表,是退出 if(head.next == null) { println("鏈表爲空,不能遍歷~") return } //1.定義一個輔助變量,幫助遍歷整個鏈表 //由於有效的節點是從head後一個 var temp = head.next breakable { while (true) { //先輸出temp指向的節點信息 printf("結點信息爲 no = %d name=%s nickname=%s\n", temp.no, temp.name, temp.nickName) //判斷是否temp是否是最後一個結點 if (temp.next == null) { break() } temp = temp.next //讓temp後移,實現遍歷 } } } //添加結點到雙向鏈表 def add(heroNode: HeroNode2): Unit = { //1. 先找到鏈表的最後結點 //2. 先定義一個輔助變量(指針),指向 head var temp = head //3.遍歷鏈表,直到到temp.next == null breakable { while (true) { if (temp.next == null) { //temp已是最後結點 break() } temp = temp.next //temp後移 } } //4. 當退出while時,temp 指向最後結點 temp.next = heroNode heroNode.pre = temp } } //建立一個HeroNode 表示節點 class HeroNode2(hNo: Int, hName: String, hNickname: String) { val no = hNo var name = hName var nickName = hNickname var next: HeroNode2 = null //next默認爲null var pre: HeroNode2 = null }
1) 棧的英文爲(stack)
2)棧是一個先入後出(FILO-First In Last Out)的有序列表。
3) 棧(stack)是限制線性表中元素的插入和刪除只能在線性表的同一端進行的一種特殊線性表。容許插入和刪除的一端,爲變化的一端,稱爲棧頂(Top),另外一端爲固定的一端,稱爲棧底(Bottom)。
4) 根據堆棧的定義可知,最早放入棧中元素在棧底,最後放入的元素在棧頂,而刪除元素恰好相反,最後放入的元素最早刪除,最早放入的元素最後刪除
棧的應用場景
1) 子程序的調用:在跳往子程序前,會先將下個指令的地址存到堆棧中,直到子程序執行完後再將地址取出,以回到原來的程序中。
2) 處理遞歸調用:和子程序的調用相似,只是除了儲存下一個指令的地址外,也將參數、區域變量等數據存入堆棧中。
3) 表達式的轉換與求值(實際解決)。
4) 二叉樹的遍歷。
5) 圖形的深度優先(depth一first)搜索法。
1) 用數組模擬棧的使用,因爲棧是一種有序列表,固然可使用數組的結構來儲存棧的數據內容,
下面咱們就用數組模擬棧的出棧,入棧等操做。
2) 實現思路分析,並畫出示意圖
代碼實現:
import scala.io.StdIn object ArrayStackDemo { def main(args: Array[String]): Unit = { //測試棧 val stack = new ArrayStack(4) var key = "" while (true) { println("list: 顯示棧") println("push: 入棧") println("pop: 出棧") println("peek: 查看棧頂") key = StdIn.readLine() key match { case "list" => stack.list() case "push" => println("請輸入一個數") val value = StdIn.readInt() stack.push(value) case "pop" => val res = stack.pop() if(res.isInstanceOf[Exception]) { println(res.asInstanceOf[Exception].getMessage) }else{ printf("取出棧頂的元素是%d", res) } case "peek" => val res = stack.peek() if(res.isInstanceOf[Exception]) { println(res.asInstanceOf[Exception].getMessage) }else{ printf("棧頂的元素是%d", res) } //默認處理 } } } } //ArrayStack 表示棧 class ArrayStack(arrMaxSize:Int) { var top = -1 val maxSize = arrMaxSize val arr = new Array[Int](maxSize) //判斷棧空 def isEmpty(): Boolean = { top == -1 } //棧滿 def isFull(): Boolean = { top == maxSize - 1 } //入棧操做 def push(num:Int): Unit = { if(isFull()) { println("棧滿,不能加入") return } top += 1 arr(top) = num } //出棧操做 def pop(): Any = { if(isEmpty()) { return new Exception("棧空,沒有數據") } val res = arr(top) top -= 1 return res } //遍歷棧 def list(): Unit = { if(isEmpty()) { println("棧空") return } //使用for for(i <- 0 to top reverse) { //逆序打印 printf("arr(%d)=%d\n", i, arr(i)) } println() } //查看棧的元素是什麼,可是不會真的把棧頂的數據彈出 def peek(): Any = { if(isEmpty()) { return new Exception("棧空") } return arr(top) } }