尚硅谷—java数据结构与算法—韩顺平
文章目录
内容介绍
几个经典的面试题
-
字符串匹配问题
- 要求
- 两个字符串: str1=“abcabdabe”,str2=“abd”
- 判断str1中是否包含str2,如果存在,就返回第一次出现的位置,如果没有返回-1.
- 用最快的速度来完成匹配
- 暴力匹配:简单、效率低
- KMP算法(部分匹配表)
-
汉诺塔
- 要求:
- 将A塔的所有圆盘移动到C塔上
- 小圆盘不能放在大圆盘的下面
- 每次只能移动一个圆盘
- 分治问题
- 要求:
-
八皇后问题
- 任意两个皇后不能处于同一行同一列或同一斜线上,有多少种摆法
- 回溯算法
-
马踏棋盘
- 8x8棋盘。马走日,要求每个放个之进入一次,走遍棋盘格上全部64个方格
- 图的DFS+贪心算法优化
-
修路问题
- 最小生成树(普利姆算法)
-
最短路径问题
- 弗洛伊德算法
重要性
- 算法是程序的灵魂
- 使用内存计算框架(Spark)和缓存技术(Redis)来优化程序,这些计算框架和缓存技术,它们的核心?
- 实际开发,算法影响性能
- 面试需求
数据结构和算法概述
数据结构和算法的关系
- 数据结构(data structure)是一门研究组织数据方式的学科,有了编程语言也就有了数据结构,学好数据结构就可以编写出更加漂亮、有效率的代码
- 要学好结构要多多考虑如何将生活中的问题,用程序去实现解决
- 程序=数据结构+算法
- 数据结构是算法的基础
线性结构和非线性结构
线性结构:
- 数据元素存在一对一的关系
- 顺序存储结构和链式存储结构
- 常见的有数组、队列、链表和栈
非线性结构:
常见的有:二维数组,多维数组,广义表,树结构,图结构
稀疏数组
需求背景:五子棋程序,有存盘退出和继续上盘的功能。(或地图)
可以想到用二维数组记录棋盘,但其中很多值是默认值0,因此记录了很多没有意义的数据。 —》于是用稀疏数组来优化。
当一个数据中大部分元素是0,或者为同一个值时,可以用稀疏数组来保存该数组。
稀疏数组的处理方法:
- 记录数组一共有几行几列,有多少个不同的值
- 把具有不同值的元素的行列及值记录在一个小规模的数组中,从而缩小程序的规模
例如:
index | 行 | 列 | 值 |
---|---|---|---|
0 | 6 | 7 | 8 |
1 | 0 | 3 | 22 |
2 | 0 | 6 | 15 |
3 | 3 | 5 | 06 |
代码实现:
原始数组==》稀疏数组:
- 遍历原始的二维数组,得到有效数组的个数sum
- 根据sum创建稀疏数组int[sum+1][3]
- 将二维数组的有效数据存入到稀疏数组
稀疏数组==》原始数组:
- 读取第一行,创建原始二维数组
- 读取后几行的数据,赋给原始数组
队列(略)
数组模拟队列(略)
单链表
单链表面试题
- 求单链表中节点个数
- 查找单链表中倒数第k个结点
- 单链表的反转——遍历之后插入新链表(头插/尾插)
- 从尾到头打印单链表
- 反向遍历
- Stack
- 合并两个有序的单链表
- 合并K个有序的单链表
双向链表(略)
环形链表和约瑟夫Josephu问题
栈
综合计算器
- 两个栈:
- 数栈(存放数字,类似操作数栈)
- 符号栈(存放运算符)
- 当前符号和符号栈的比较
- 当前符号栈为空:直接入栈
- 当前符号优先级栈 < = <= <=栈顶符号:计算栈顶符号和数栈的结果,再分别把结果和当前符号入栈
- 当前符号优先级栈 > > >栈顶符号:直接入符号栈
前缀表达式(波兰表达式)
- 前缀表达式的运算符位于操作数之前。
- 举例: ( 3 + 4 ) ∗ 5 − 6 (3+4)*5-6 (3+4)∗5−6对应的前缀表达式是 − ∗ + 3456 -*+3456 −∗+3456
- 计算机求值流程:
- 从右至左扫描,将 6、5、4、3压入堆栈。
- 遇到 + + +运算符,弹出3和4,计算 3 + 4 3+4 3+4的值,为7,压入栈。
- ∗ * ∗运算符,得到35,入栈。
- − - −运算符,得到29,即为最终结果。
- 将堆栈元素依次取出,即为前缀表达式
中缀表达式
- 中缀表达式就是常见的运算表达式。如 ( 3 + 4 ) ∗ 5 − 6 (3+4)*5-6 (3+4)∗5−6
- 中缀表达式是人熟悉的,但确实计算机不好操作的。通常改写为后缀表达式
后缀表达式(逆波兰表达式)
- 与前缀表达式相似,只是运算符位于操作数之后。
- 举例: ( 3 + 4 ) ∗ 5 − 6 (3+4)*5-6 (3+4)∗5−6对应的后缀表达式是 34 + 5 ∗ 6 − 34+5*6- 34+5∗6−
- 针对后缀表达式求值步骤:
- 从左至右扫描,把3和4压入堆栈
- 遇到+运算符,因此弹出4和3,计算值为7,入栈。
- 将5入栈
- 遇到*运算符,弹出5和7,计算出值35,入栈
- 将6入栈
- 遇到-运算符,计算出值29,即为最后结果。
- 后缀表达式符合计算机运算的逻辑顺序
正常表达式 | 后缀表达式 |
---|---|
a + b a+b a+b | a b + ab+ ab+ |
a + ( b − c ) a+(b-c) a+(b−c) | a b c − + abc-+ abc−+ |
a + ( b − c ) ∗ d a+(b-c)*d a+(b−c)∗d | a b c − d ∗ + abc-d*+ abc−d∗+ |
a + d ∗ ( b − c ) a+d*(b-c) a+d∗(b−c) | a d b c − ∗ + adbc-*+ adbc−∗+ |
a = 1 + 3 a=1+3 a=1+3 | a 13 + = a13+= a13+= |
逆波兰计算器
- 将中缀表达式转换为逆波兰表达式
- 输入逆波兰表达式,使用栈,计算结果
- 支持小括号和多位数整数。
中缀表达式转后缀表达式
- 初始化两个栈:运算符栈1和中间结果栈2
- 从左至右扫描中缀表达式
- 遇到操作数时,压入s2 。
- 遇到运算符时,比较其与s1栈顶运算符的优先级:
- 如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈。
- 否则,若优先级比 栈顶运算符高,也将运算符压入s1。
- 否则,将s1栈顶的运算符弹出并压入s2中,再次转到(4-1)与s1中新的栈顶运算符相比较。
- 遇到括号时
- 若为左括号“( ”,则直接压入s1
- 若为右括号“ )”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃。
- 重复步骤(2)-(5) ,直到表达式的最右边
- 将s1中剩余的运算符依次弹出并压入s2
- 依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
递归
迷宫问题
- map[m][n] 二维数组表示地图:
- 0:没有走过
- 1:表示墙
- 2:通路可以走
- 3:已经走过,但是走不通
- 确定一个策略:下 => 右 => 上 => 左;走不通,再回溯
- 最短路径
- 路径和策略有关——策略遍历,比较
八皇后问题(回溯算法)
- 暴力解法
- 用一维数组表示棋盘,八皇后的位置arr[8] = {0,4,7,5,2,6,1,3} :数组下标表示row,数组值表示col。
- 判断是否是同一斜线: Math.abs(i-j)==Math.abs(arr[i]-arr[j])