数据结构知识点:
1.队列和栈说经常使用的数据结构,特点为:队列先进先出,栈是后进先出。
2.表包括很多种,有占有连续空间的数组;用指针链接的单向链表和双向链表;首尾相连的循环链表;及散列表,哈希表。
3.图,在特定的领域使用的比较多,例如路由算法中使用的比较多,分为有向图,无向图,带权图。
4.树,树一般用作查找和排序的辅助结构;(二叉树;多叉树)
a.多叉树包括B树族,有B树,B+树,B*树,比较是和做文件索引,另一个是字典树,适合进行字符串你的多模匹配。
b.二叉树包括平衡二叉树,红黑树,哈夫曼树,以及堆,适合用于数据的查询和排序。需要连接二叉树的构建,插入,删除操的实现,需要掌握二叉树的前序,中序,后续遍历。
二叉树:
二叉树满足以下条件:每个节点包含一个值,每个节点至多有两个子树,每个节点的左子树节点的值都小于自身,右子树节点的值都大于自身的值。
二叉树的查询时间复杂度是log(N) ,但随着不断地插入,删除节点,二叉树的高度可能会不断变大,当一个二叉树所有的节点都只有左子树或者右子树时,其查询性能就退化成线性了。
二叉树遍历
遍历:就是按照某种次序访问树中所有节点,且每个节点恰好只能访问一次
将整个二叉树分为三个部分,根、左子树、右子树;那么根据根的遍历顺序就有三种遍历方式:
先序遍历DLR:根、左子树、右子树
中序遍历LDR:左子树、根、右子树
后序遍历LRD:左子树、右子树、根 ;
如下图: (方法:先找根,先将根放在对应位置)
定义二叉树节点类
public class Node {
int value; //节点值
Node leftChild; //左子树引用
Node rightChild;//右子树引用
public Node(int value) {
this.value = value;
}
public Node(int value, Node leftChild, Node rightChild) {
super();
this.value = value;
this.leftChild = leftChild;
this.rightChild = rightChild;
}
}
平衡二叉树
平衡二叉树可以很好解决上面所说的这个问题,平衡二叉树保证每个节点左右子树的高度差的绝对值不超过1,叉树和删除可能经常需要旋转来保持平衡,比较适合插入,删除比较少的场景。
红黑树
红黑树是一种更加实用的非严格的平衡二叉树,红黑树更关注局部平衡而非整体平衡;确保没有一条路径会比其他路径长出2倍,所以是接近平衡的,但减少了许多不必要的旋转操作,更加实用。java8的HashMap中就应用了红黑树来解决散列冲突时的查找问题。TreeMap也是通过红黑树来保证有序性的
红黑树除了具有二叉树的特点外还具有一下规则:
1.根节点是黑色
2.每个叶子节点都是黑色的空节点
3.红色节点的两个子节点都是黑色的
4.任意节点到其叶子节点的每条路径上,包含相同数量的黑色节点
B 树
B树是一种多叉树,也叫多路搜索树。B树中每个节点可以存储多个元素,非常适合用在文件索引上,可以有效减少磁盘IO次数,B树种所有节点的最大子节点树称为B树的阶(如上图:3阶B树)
B+树
B+树的定义与B树基本相同,但也具有以前特点:
1.节点中的关键字与子树数目相同,比如节点中有三个关键字,那么就有三颗子树。
2.关键字对应的子树节点都大于或等于关键字,子树中包括关键字自身。
3.所有关键字都有出现在叶子节点上
4.所有叶子节点都有指向下一个叶子节点的指针。
与B树不同,B+树在搜索时不会在非叶子节点命中,一定会查询到叶子节点;另外一个,叶子节点相当于数据存储层,保存关键字对应数据,而非叶子节点只保存关键字和指向节点的指针,不保存关键字对应的数据,所以同样数量关键字的非叶子节点B+树比B树要小很多。
B+树更适合索引系统,MySQL数据库的索引就提供B+树实现:
1.由于叶子节点之间有指针相连,B+树更适合范围检索;
2.由于非叶子节点只保存关键字和指针,同样大小非叶子节点,B+树可以容纳更多的关键字,可以降低树高,查询时读写代价更低。
3.B+树的查询效率比较稳定。任何关键字的查找必须走一条从根节点到叶子节点的路,所有关键字查询路径长度相同,相率相当。
栈
栈(stack)又称堆栈,他是运算受限的线性表,其限制是仅允许在表的一端进行插入和删除操作,不允许在其他位置进行插入,查找,删除等操作。
表中进行插入,删除操作的一段称为栈顶(top),栈顶保存的元素称为栈顶元素。相对的表的另一端称为栈底(bottom)
对栈操作的专业词汇:入栈 (push)出栈(pop)获取栈顶元素(peek)
栈的存储结构:和线性表类似,堆栈也有两种存储结构,顺序存储结构和链式存储结构。
先进后出
队列
队列(queue)和栈一样也是一种运算受限的线性表,其限制条件是仅允许在表的一端进行插入,在表的另一端进行删除。
在队列中把插入元素的一段称队尾(rear),删除元素的一段称为队首(front)
先进先出
时间复杂度:
时间复杂度链接描述
若存在函数 f(n),使得当n趋近于无穷大时,T(n)/ f(n)的极限值为不等于零的常数,则称 f(n)是T(n)的同数量级函数。
记作 T(n)= O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。
渐进时间复杂度用大写O来表示,所以也被称为大O表示法。
如何推导出时间复杂度呢?有如下几个原则:
- 如果运行时间是常数量级,用常数1表示;
- 只保留时间函数中的最高阶项;
- 如果最高阶项存在,则省去最高阶项前面的系数。
排序算法
1.冒泡排序
/**
int[] arr = new int[] {3, 6, 10, 9, 5, 7, 4};
* 3 2 6 5 7 4 9 第一轮
* 2 3 5 6 4 7 第二轮
* 2 3 5 4 6
* 2 3 4 5
* 2 3 4
* 2 3
*/
public static void sort(int[] arr) {
int min;
//比较多少轮
for (int i = 0; i < arr.length - 1; i++) {
//每轮比较几个数
for (int j =0;j<arr.length-1-i;j++)
if (arr[j] > arr[j+1]) {
min = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = min;
}
}
System.out.println(Arrays.toString(arr));
}
2.快速排序
public static void quickSort(int[] arr, int left, int right) {
int l = left;//左下标
int r = right;//右下标
//中轴值(取数组中间的值作为比较对象)
int pivot = arr[(left + right)/2] ;
int temp ;
//while循环目的让比pivot下的值放在左边,比pivot大的值放在右边
while (l < r) {
//在pivot左边找,找到大于等于pivot的值才退出
while (arr[l] < pivot) {
l += 1;
}
//在pivot右边找,找到小于等于pivot的值才退出
while (arr[r] > pivot) {
r -= 1;
}
//如果l大于等于r说明小于pivot的值全部在左边,大于pivot的值全部在右边
if (l >= r) {
break;
}
//交换值
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
//如果交换完,发现arr[l]==pivot的值,则r--,前移
if (arr[l] == pivot) {
r -= 1;
}
//如果交换完,发现arr[r]==pivot的值,则l++,后移
if (arr[r] == pivot) {
l += 1;
}
//如果出现l==r,必须l++,r--
if (l == r) {
l += 1;
r -= 1;
}
System.out.println("===============l:"+l);
System.out.println("===============r:"+r);
//向左递归
if (left < r) {
quickSort(arr, left, r);
}
//向右递归
if (right > l) {
quickSort(arr, l, right);
}
}
}
3.选择排序
算法思想:首先,找到数组中最小的元素,再将他和数组的第一个元素交换(如果第一个元素就是最小的那就和自己交换),再在剩下的元素中找到最小的那个,与第二个元素交换,如此反复,直至最后一个元素找不到最小的元素(即自己就是最小的元素)。
选择排序的内循环只是比较当前与目前已知最小元素,外循环比较交换元素
public static void selectSort(int[] arr) {
for (int i=0 ;i<arr.length-1;i++) {
int minIndex = i;
int min = arr[i];
for (int j = i + 1; j < arr.length; j++) {
if (min > arr[j]) {//说明假定的最小值不是最小
min = arr[j]; //找到数组最小值,
minIndex = j; //找到数组最小值所对应的索引
}
}
// 将最小值放到arr[i]的位置
if (minIndex != i) {
arr[minIndex] = arr[i];//将最小值与数组第一位进行交换
arr[i] = min;
}
}
}
4.插入排序
将数组看作有序部分和无序部分,默认第一个元素是有序的。
从第二个元素开始往前插入,如果有序部分的元素大于要插入的元素,就继续向前遍历,直到碰见一个元素小于要插入的元素,这时,将要插入的元素插入该元素后面。
注意,在遍历的时候,大于要插入元素的元素一直在向后移动。
public static void insertSort(int[] arr) {
//定义待插入的数
for (int i = 1; i < arr.length; i++) {
int insertVule = arr[i];
int insertIndex = i-1;//即arr[i]前面的数
第一次先比较arr[1]与arr[0],然后退出while;
// 第二次比较arr[2]与arr[1],接着比较arr[1]与arr[0]再退出while
while (insertIndex >= 0 && insertVule < arr[insertIndex]) {
arr[insertIndex + 1] = arr[insertIndex];
insertIndex --;
}
//当退出while循环时,说明插入的位置找到插入位置
arr[insertIndex + 1] = insertVule;
}
}