队列
环型队列判断满和空:
(rear + 1) % maxSize == front // 满
rear == front // 空
(rear + maxSize - front) % maxSize // 有效数据个数
栈
- 中缀表达式:(3+4)*5-6
- 前缀表达式:-*+ 3 4 5 6
- 后缀表达式:3 4 + 5 x 6 -
排序算法
算法复杂度比较
O(1) < o(log n) < o(n) < o(n log n) < o(n^2) < o(n^3) < o(n^k) <o(2^n) < o(n!)
各自复杂度
平均 | 最差 | 稳定度 | 空间 | 适用场景 | |
冒泡 | O(N^2) | O(N^2) | 稳定 | O(1) | N小 |
交换 | O(N^2) | O(N^2) | 不稳定 | O(1) | N小 |
选择 | O(N^2) | O(N^2) | 不稳定 | O(1) | N小 |
插入 | O(N^2) | O(N^2) | 稳定 | O(1) | 大部分已排好序 |
基数 | O(NxK) | O(NxK) | 稳定 | O(N) | - |
shell | O(NlogN) | O(N^S) | 不稳定 | O(1) | - |
快速 | O(NlogN) | O(N^2) | 不稳定 | O(NlogN) | N大 |
归并 | O(NlogN) | O(NlogN) | 稳定 | O(1) | N大 |
堆 | O(NlogN) | O(NlogN) | 不稳定 | O(1) | N大 |
冒泡排序
从前向后,从下标较小的元素开始,依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后面(如果一趟下来没有任何的交换,则说明序列有序,从而减少不必要的比较)
选择排序
- 从数组中选出最小值,与arr[0]交换
- 再从arr[1]~arr[n-1]中选出最小值,与arr[1]交换,以此类推
插入排序
- n个待排序的元素看成一个有序表和无序表,开始有序表只包含一个元素,无序表中包含n-1个元素
- 每次从无序表中取出第一个元素,把它的排序码一次与有序表元素的排序码进行比较,插入适当位置,成为新的有序表
希尔排序
先按一定增量分组,对每组使用直接插入排序算法排序,随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件被分为一组,算法终止
快速排序
- 先把数组中的一个数当做基准数,一般会把数组中最左边的数当做基准数,然后从两边开始检索,先从右边检索比基准数小的(如果基准数是最右边的数,则先从左边开始检索),再从左边检索比基准数大的,如果检索到了,就停下,然后交换这两个元素,然后再继续检索
- 左右指针一旦相遇就停止检索
- 把基准数和相遇位置的数交换,表示第一轮排序已经结束,此时,基准数左边的全比基准数小,右边全比基准数大
- 之后先排基准数左边,排完之后再排基准数右边,方式和第一轮一样
归并排序
先分成两块再分成两块,直到最后的两块每块只有一个元素,然后每小块排序
基数排序
将所有待比较数值统一为同样的数位长度,数位较短的前面补0,然后从最低位开始,依次进行一次排序,从最低位排序一直到最高位排序完成后,数列就变成一个有序序列
树
赫夫曼树
给定n个权值作为n个叶子节点,构造一棵二叉树,若该树的带权路径长度(wpl)达到最小,就称为最优二叉树,也称为哈夫曼树
- 从小到大排序,每一个数据就是一个节点,每个节点可以看成是一棵最简单的二叉树
- 取出根节点权值最小的两棵二叉树
- 组成新的二叉树,该新二叉树的根节点的权值是前面两棵二叉树根节点权值的和
- 对新的二叉树,以根节点的权值大小再次排序,不断重复1-2-3的步骤,直到数列中,所有的数据都被处理,就得到一颗赫夫曼树
二叉搜索树
【删除一个节点】
- 首先判断该节点是 叶子节点 or 只有一个叶子节点的节点 or 有两个叶子节点的节点
- 只有一个叶子节点的节点
- 判断该节点是其父节点的左子节点还是右子节点
- 判断该节点的叶子节点是其左子节点还是右子节点
- 有两个叶子节点的节点
- 找左子树的最右(最大)节点替上去 或者 找右子树的最左(最小)节点替上去
分治算法
复杂问题分解成小问题
【汉诺塔】
- 如果是有一个盘:A->C
- 如果有n>=2个盘,可以看做是两个盘1:最下面的盘;2:上面的所有盘
- 先把上面的盘A->B
- 最下面的盘A->C
- 上面的盘B->C
动态规划
将大问题划分为小问题进行解决,与分治算法不同的是:适用于动态规划算法求解的问题,经分解得到子问题往往不是相互独立的
KMP算法
解决问题
String a 与 String b,判断 b 中是否包含 a,若包含,返回 a 在 b 中的位置下标
思路
- 当匹配到某个位置,主串与模式串的字符不同时,此时不直接 [从主串下一个位置 模式串从头比较]
- 模式串的前面部分的字符串内容是与主串的部分字符是相同的
- 在该模式串"ABACABAD"中,下标0~2的字符是与下标4~6的字符是相同的
- 从主串不匹配的这个位置,模式串不重头开始,而是比较模式串下标3的字符与主串中的字符是否相同
- 部分匹配表
- 以该字符结尾的前缀与后缀最长相同子串的长度
代码
public class Kmp {
public static void main(String[] args){
String a = "ABACABAD";
String b = "BBC ABACABACABAD ABCDABDE";
int result = kmp(b, a);
//打印结果:和字符串获得匹配的位置
System.out.println("resultPosion:"+result);
}
/**
* KMP 匹配
*/
public static int kmp(String str, String dest){
//1.首先计算出 部分匹配表
int[] next = kmpnext(dest);
System.out.println("next ="+Arrays.toString(next));
//2.查找匹配位置
for(int i = 0, j = 0; i < str.length(); i++){
while(j > 0 && str.charAt(i) != dest.charAt(j)){
j = next[j-1];
}
if(str.charAt(i) == dest.charAt(j)){
j++;
}
if(j == dest.length()){
return i-j+1;
}
}
return -1;
}
/**
* 计算部分匹配表
*/
public static int[] kmpnext(String dest){
int[] next = new int[dest.length()];
next[0] = 0;
for(int i = 1,j = 0; i < dest.length(); i++){
while(j > 0 && dest.charAt(j) != dest.charAt(i)){
j = next[j - 1];
}
if(dest.charAt(i) == dest.charAt(j)){
j++;
}
next[i] = j;
}
return next;
}
}
贪心算法
对问题进行求解时,每一步选择中都采取最好或者最优(即最有利)的选择
与动态规划的区别:
- 动态规划:模拟所有的可能得到最优解
- 贪心算法:未模拟所有的可能
迪杰斯特拉算法
最短路径算法,用于计算一个节点到其他节点的最短路径
/* 参数说明:
* vs -- 起始顶点(start vertex)。即计算"顶点vs"到其它顶点的最短路径。
* prev -- 前驱顶点数组。即,prev[i]的值是"顶点vs"到"顶点i"的最短路径所经历的全部顶点中,位于"顶点i"之前的那个顶点。
* dist -- 长度数组。即,dist[i]是"顶点vs"到"顶点i"的最短路径的长度。
*/
public void dijkstra(int vs, int[] prev, int[] dist) {
// flag[i]=true表示"顶点vs"到"顶点i"的最短路径已成功获取
boolean[] flag = new boolean[mVexs.length];
// 初始化
for (int i = 0; i < mVexs.length; i++) {
flag[i] = false; // 顶点i的最短路径还没获取到。
prev[i] = 0; // 顶点i的前驱顶点为0。
dist[i] = mMatrix[vs][i]; // 顶点i的最短路径为"顶点vs"到"顶点i"的权。
}
// 对"顶点vs"自身进行初始化
flag[vs] = true;
dist[vs] = 0;
// 遍历mVexs.length-1次;每次找出一个顶点的最短路径。
int k=0;
for (int i = 1; i < mVexs.length; i++) {
// 寻找当前最小的路径;
// 即,在未获取最短路径的顶点中,找到离vs最近的顶点(k)。
int min = INF;
for (int j = 0; j < mVexs.length; j++) {
if (flag[j]==false && dist[j]<min) {
min = dist[j];
k = j;
}
}
// 标记"顶点k"为已经获取到最短路径
flag[k] = true;
// 修正当前最短路径和前驱顶点
// 即,当已经"顶点k的最短路径"之后,更新"未获取最短路径的顶点的最短路径和前驱顶点"。
for (int j = 0; j < mVexs.length; j++) {
int tmp = (mMatrix[k][j]==INF ? INF : (min + mMatrix[k][j]));
if (flag[j]==false && (tmp<dist[j]) ) {
dist[j] = tmp;
prev[j] = k;
}
}
}
// 打印dijkstra最短路径的结果
System.out.printf("dijkstra(%c): \n", mVexs[vs]);
for (int i=0; i < mVexs.length; i++)
System.out.printf(" shortest(%c, %c)=%d\n", mVexs[vs], mVexs[i], dist[i]);
}
弗洛伊德算法
计算图中各个顶点之间的最短路径,每个顶点都是出发访问点
- 设置顶点vi到顶点vk的最短路径已知为Lik,顶点vk到vj的最短路径为Lkj,顶点vi到vj的路径为Lij,则vi到vj的最短路径为min{(Lik+Lkj), Lij},vk的取值为图中所有顶点,则可获得vi到vj的最短路径
扩展
防止某个数溢出
if (rs * 10 / 10 != rs) { //表明溢出
return 0;
}
异或
- 任何数和本身异或则为0
- 任何数和0异或是本身
- 异或满足交换律
取模运算为什么是耗时的
n%10 = n - (n/10) * 10
为什么出现拆箱、装箱
- 泛型:如果除去对象的基本类型外,实现方法是相同的
- 只有引用类型才能和object类兼容,于是出现了包装类,例如int的包装类是Integer