interview
内容管理
数据结构— 顺序表、链表、数组、矩阵; 数组算法续
数据结构[simplify]
数据结构作为CS的核心课程,这里就简单介绍简单的数据结构:线性表、链表、栈、队列等,可以分享一下B树和B+树【B树作为Mysql的底层】
数据结构是指数据元素的集合及元素间的相互关系和构造方法,结构就是元素之间的关系;元素之间的关系是数据的逻辑结构 ,物理结构要看分配的空间来定
线性表
线性表就是一个序列,两种方式【链式存储和顺序存储】; 也就是唯一的第一个和最后一个;除第一个之外,只有一个直接前驱,除最后一个,每一个元素只有一个直接后继
顺序表
顺序表可以看作可以设置长度的数组,在C中的实现方式就是结构体,两个元素就是len和数组arr【还可以其他的参数】; 顺序表因为就是一组连续的空间,所以逻辑上相邻,物理上也相邻
- 可以随机存取表中的元素,L.arr[i];按照序号查找的速度很快
- 插入和删除元素需要移动元素,时间复杂度高【最坏复杂度就是O(N)],平均插入一个元素需要移动n/2次;删除元素平均移动的次数为(n - 1)/2
链表LinkedList
线性表的链式存储就是使用结点来存储数据,元素的结果i的那的地址可以连续,也可以不连续,结点空间只有在需要的时候才申请,无需事先分配 【逻辑上相邻,物理上不一定相邻】
C/C+=中的指针、其实就是地址【地址就是下一个结点的位置】,java中的对象的引用也是地址
- 插入和删除元素不需要移动元素 【 直接变换指针即可】
- 只能按顺序访问元素,遍历,不能随机存取
将最后一个结点的指针指向头结点 – 循环链表; 有两个指针域的结点,一个指向前驱,一个指向后继 ------ 双链表
栈stack
栈就是一种特殊的线性表,LIFO表 — 后进先出;
- 顺序栈: 用一组连续的地址的存储单元依次存储自栈顶到栈底的数据元素; 存储空间是预先定义或者申请,可能栈满; 每一个元素入栈push的时候判断栈满,需要设置一个头指针指向栈顶,需要附设top指示栈顶元素的位置 top指向栈顶,bottom指向栈底
//计算后缀表达式
46 5 37 - * +
arithmetic: 遇到操作数直接压栈,遇到运算符,弹栈两个数,然后运算结果压栈,之后继续操作,知道得到最终的结果
//括号匹配
遇到括号压栈,遇到操作数忽略,直到最后,判断即可
- 链栈: 链栈就是一个链表,在头部进行插入,top位置进行插入和删除,不需要设置头结点,头指针就是栈顶指针
队列queue
FIFO表,先进先出,队尾入队头出;
* 顺序队列 :两个指针,rear、front指向数组的位置即可,但是这样子会出现空间的浪费
- 循环队列,相当于数组的首尾相连,主要是判断两指针的位置即可
- 链式队列: 可以给链队列加上一个头结点,头指针指向头结点; 队列为空就是头尾指针都指向头结点
//广度优先搜索
遇到结点,首先弹出u,然后扩展u
串
字符串也可以看作一种线性表,是一串文字和符号的简称
也可以采用顺序存储或者链式存储【顺序就是物理上相邻,链式不相邻】
字符串的基本元素就是字符,常常将一个串当作一个整体来进行处理;串也是有限的序列,
- 串长: 字符串的长度
- 空串: 长度为0的字符串 “”
- 空格串,一个或者多个空格组成的串 " "
- …
数组
一维数组就是长度固定的线性表,数组中每一个元素的类型相同,n维数组就是数组的数组,在java中可以长度不相等;数组作为固定长度的线性表,一般不做插入和删除,就算删除也是使用的双指针法;
数组的单元的计算------ easy,只是明确起始位置从0开始就可
过于easy的入门知识不提了
矩阵
当时数据结构中觉得比较有趣的地方就是矩阵 ----- 特别是特殊矩阵的存储
对多个值相同的元素可以只分配一个存储单元,0不分配; 【对称矩阵、三对角矩阵、稀疏矩阵】
对称矩阵
就是将数据给压缩存储到一个一维数组中,这里就简单给一下公式吧,我觉得也没啥review的,起始最复杂的还是十字链表,要遍历,并且for循环; 就可以停在目标位置
k = i(i-1)/2 + j - 1 --- 就前面有几个元素即可 如果是0开始就是这个结果,但是如果要从1开始,那么就+1就得到了下标了
三对角矩阵
三对角矩阵就是对角线和其两边的相邻平行路的元素之外的其他的元素都为0
这里放在一维数组中就是从上到下,从左到右依次存储;要计算压缩后的位置,还是计算其之前有多少个元素,
(i - 1)*3 + j - i + 1 = 2i + j -3
稀疏矩阵
稀疏矩阵就使用一个结构体表示一个矩阵,用三元组表示每一个非0元素,col、row、value;顺序存储就是三元组顺序表,链式存储就是十字链表
Part 2 arithmetic
其实串也可以当数组来看,所以我将上面的串模式匹配拿下来吧
布鲁特-福斯算法和KMP算法
这个算法其实和我们之前分析的双指针类似,这里就是专门的串模式匹配算法
字串(模式串)在主串中的定位
如果是一道普通的算法题,可能会直接想到暴力解法,直接开始遍历,然后当碰到相等的时候,j从i开始比较,比较失败i就继续循环;但是这样子的复杂度达到了O(N*M)
28. 实现 strStr() - 力扣(LeetCode) (leetcode-cn.com)
class Solution {
public int strStr(String haystack, String needle) {
//开局使用克鲁德算法,双指针
int i = 0 , j = 0;
while(i < haystack.length() && j < needle.length()) {
if(haystack.charAt(i) == needle.charAt(j)) {
i ++;
j ++;
}else{
//匹配失败的时候,j需要回溯
i = i + 1 - j; //增加一个同时要注意减去模式串的清零的位置
j = 0;
}
}
if(j == needle.length()) {
return i - needle.length(); //最后i停在匹配的最后
}
return -1;
}
}
这里最主要的就是双指针,当相等的时候就递增,当不相等的时候,就需要回溯,j直接变成0,i本来直接加位的,但是i现在是停在最终匹配的位置,所以需要 - 当前模式串已经匹配的长度j i = i + 1 - j;
螺旋矩阵,边界的移动,循环不变量
所谓的循环不变量就是指每次循环的区间的开闭状态要相同,这里就统一成做闭右闭就可,这道题目就是没有考算法,但是就是考模拟的过程,但是需要想到边界的移动,直观移动即可
59. 螺旋矩阵 II - 力扣(LeetCode) (leetcode-cn.com)
class Solution {
public int[][] generateMatrix(int n) {
int[][] result = new int[n][n];
int up = 0,down = n -1, left = 0, right = n -1;
int index = 1; //位置,一直填到结束
while(index <= n * n) {
//上
for(int i = left ; i <= right; i ++) {
result[up][i] = index++;
}
up ++; //完成之后上界就会下移
//右
for(int i = up; i <= down; i ++) { //都是左闭右闭
result[i][right] = index ++;
}
right --;//完成之后由边界左移
//下
for(int i = right; i >= left; i --) {
result[down][i] = index ++;
}
down --;
//左
for(int i = down; i >= up; i--) {
result[i][left] = index ++;
}
left ++;
}
return result;
}
}
这里就是要想到层数的概念,就是每次移动之后四面就会靠拢,每次就会减少或者增加
这里还有一个一模一样的题目
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> list = new ArrayList();
//这个和哪个题一样的,都是螺旋,螺旋,四面墙靠拢
int index = 1;
int row = matrix.length;
int col = matrix[0].length;
int up = 0, down = row - 1, left = 0, right = col - 1;//四面墙,都是闭
LOOP:
while(true) {
//上
for(int i = left; i <= right; i ++) {
list.add(matrix[up][i]);
index ++;
if(index > row * col) break LOOP;
}
up ++;
//右
for(int i = up; i <= down; i ++) {
list.add(matrix[i][right]);
index ++;
if(index > row * col) break LOOP;
}
right --;
//下
for(int i = right; i >= left; i --) {
list.add(matrix[down][i]);
index ++;
if(index > row * col) break LOOP;
}
down --;
//左
for(int i = down; i >= up; i --) {
list.add(matrix[i][left]);
index ++;
if(index > row * col) break LOOP;
}
left ++;
}
return list;
}
}
这里可以将其中的break语句拿出来到外面的循环中,注意这里不能使用在while中直接使用条件,因为while的条件判断是在块语句执行完成之后,并且java中int类型是不能和boolean类型进行转换的
还是模拟即可🎄明天再总结一下数组,这几天都是在弄数组,接下来就是链表了