数据结构(算法)的介绍
数据结构的介绍
数据结构是一门研究算法的学科,只从有了编程语言也就有了数据结构.学好数据结构可以编写出更加漂亮,更加有效率的代码。
要学习好数据结构就要多多考虑如何将生活中遇到的问题,用程序去实现解决.
程序 = 数据结构 + 算法
数据结构(数组,队列,栈,链表,树,图)和算法的关系
算法是程序的灵魂,为什么有些网站能够在高并发,和海量吞吐情况下依然坚如磐石,大家可能会说: 网站使用了服务器群集技术、数据库读写分离和缓存技术(比如Redis等),那如果我再深入的问一句,这些优化技术又是怎样被那些天才的技术高手设计出来的呢?
大家请思考一个问题,是什么让不同的人写出的代码从功能看是一样的,但从效率上却有天壤之别, 拿在公司工作的实际经历来说, 我是做服务器的,环境是UNIX,功能是要支持上千万人同时在线,并保证数据传输的稳定, 在服务器上线前,做内测,一切OK,可上线后,服务器就支撑不住了, 公司的CTO对我的代码进行优化,再次上线,坚如磐石。那一瞬间,我认识到程序是有灵魂的,就是算法。如果你不想永远都是代码工人,那就花时间来研究下算法吧!
本章着重讲解算法 的基石-数据结构。
看几个实际编程中遇到的问题
Scala代码:
def main(args: Array[String]): Unit = {
val str = "scala,scala, hello,world!"
val newStr = str.replaceAll("scala", "尚硅谷")
println("newStr=" + newStr) //暴力匹配=>KMP算法
}
看几个实际编程中遇到的问题
一个五子棋程序:
?如何判断游戏的输赢,并可以完成存盘退出和继续上局的功能
约瑟夫问题(丢手帕问题)
Josephu 问题
Josephu 问题为:设编号为1,2,… n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
提示:用一个不带头结点的循环链表来处理Josephu 问题:先构成一个有n个结点的单循环链表,然后由k结点起从1开始计数,计到m时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从1开始计数,直到最后一个结点从链表中删除算法结束。
其它常见算法问题:
邮差问题
最短路径问题
汉诺塔
八皇后问题
稀疏sparsearray数组
先看一个实际的需求
编写的五子棋程序中,有存盘退出和续上盘的功能。
分析问题:
因为该二维数组的很多值是默认值0, 因此记录了很多没有意义的数据.->稀疏数组。
稀疏数组
基本介绍
当一个数组中大部分元素为0,或者为同一个值的数组时,可以使用稀疏数组来保存该数组。
稀疏数组的处理方法是:
记录数组一共有几行几列,有多少个不同的值(有效值的个数)
把具有不同值的元素的行列及值记录在一个小规模的数组中,从而缩小程序的规模
应用实例
使用稀疏数组,来保留类似前面的二维数组(棋盘、地图等等)
把稀疏数组存盘,并且可以重新恢复原来的二维数组数
整体思路分析
代码实现
package datastructure
import scala.collection.mutable.ArrayBuffer
/**
* @author cherry
* @create 2019-09-16-22:33
*/
object SparseArrayDemo {
def main(args: Array[String]): Unit = {
//先使用二维数据,映射棋盘
val rows = 11 //棋盘行数
val cols = 11 //列数
val chessMap: Array[Array[Int]] = Array.ofDim[Int](rows, cols)
//初始化
chessMap(1)(2) = 1 //表示黑子
chessMap(2)(3) = 2 //表示蓝子
println("原始的棋盘")
for (row <- chessMap) {
for (item <- row) {
printf("%d ", item)
}
//换行
println()
}
/**
* 对原始的二维数据进行压缩
* 思路:
* 1.创建一个ArrayBufffer,可以动态添加数据
* 2.使用Node对象,表示一行数据
*/
val sparseArray: ArrayBuffer[Node] = ArrayBuffer()
//先将第一行数据放入
sparseArray.append(new Node(rows, cols, 0))
//遍历chessMap,如果发现有非0的值,创建一个Node对象,并加入到spareArray
for (i <- 0 until chessMap.length) {
for (j <- 0 until chessMap(i).length) {
if (chessMap(i)(j) != 0) {
//有效数据需要保存
sparseArray.append(new Node(i, j, chessMap(i)(j)))
}
}
}
println("稀疏数组的情况是:")
for (i <- 0 until sparseArray.length) {
val node: Node = sparseArray(i)
printf("%d %d %d\n", node.row, node.col, node.value)
}
/**
* 将悉数数组恢复到原盘
* 思路:
* 1.读取稀疏数组的第一行,创建一个二维棋盘
* 2.遍历(从悉数数组的第二行),每读取到一个node,就将对应的值恢复到chessMap2
*/
val node: Node = sparseArray(0)
val chessMap2: Array[Array[Int]] = Array.ofDim[Int](node.row, node.col)
for (i <- 1 until sparseArray.length) {
val node2: Node = sparseArray(i)
chessMap2(node2.row)(node2.col) = node2.value
}
println("恢复的棋盘")
for (row <- chessMap2) {
for (item <- row) {
printf("%d ", item)
}
//换行
println()
}
}
}
class Node(val row: Int, val col: Int, val value: Int)
控制台打印结果