算法与数据结构
文章目录
- 算法与数据结构
- 一、算法相关
- 1、十大排序算法总结
- 冒泡排序
- 插入排序
- 选择排序
- 快速排序
- 基数排序
- 桶排序
- 希尔排序
- 归并排序
- 堆排序
- 2、经典算法介绍
- 递归算法
- 回溯算法
- 贪心算法
- 深度优先遍历
- 广度优先遍历
- 动态规划算法
- 3、常见算法
- 1、迪杰斯特拉(Dijkstra)算法介绍
- 二、数据结构介绍
- 1、字符串
- 2、数组
- 3、链表
- 4、Hash
- 5、跳跃表
- 6、二叉树
- 7、大顶堆
- 8、小顶堆
- 9、红黑树
- 10、B树
- 11、B+树
- 12、Treap树
- 13、LSM树
- 14、Trie树
- 15、多叉树
- 16、无向图
- 17、有向图
- 18、带权图(网)
- 7、大顶堆
- 8、小顶堆
- 9、红黑树
- 10、B树
- 11、B+树
- 12、Treap树
- 13、LSM树
- 14、Trie树
- 15、多叉树
- 16、无向图
- 17、有向图
- 18、带权图(网)
一、算法相关
1、十大排序算法总结
排序算法 | 平均时间 | 最差时间 | 稳定度 | 额外空间 | 备注 |
---|---|---|---|---|---|
冒泡排序 | O(n²) | O(n²) | 稳定 | O(1) | n小较好 |
插入排序 | O(n²) | O(n²) | 稳定 | O(1) | n小较好 |
选择排序 | O(n²) | O(n²) | 不稳定 | O(1) | n小较好 |
交换排序 | O(n²) | O(n²) | 不稳定 | O(1) | 大部分数据已经排序好 |
快速排序 | O(nlogn) | O(n²) | 不稳定 | O(nlogn) | n大时较好 |
基数排序 | O(nlogrn) | O(nlogrn) | 稳定 | O(n) | n是真数,r是基数 |
桶排序 | O(nlogn) | O(nlogn) | 不稳定 | O(n) | 数据均匀分配到每一个桶中快; |
计数排序 | 稳定 | O(n) | |||
希尔排序 | O(nlogn) | O(n²)1<s<2 | 不稳定 | O(1) | s是所选分组 |
归并排序 | O(nlogn) | O(nlogn) | 稳定 | O(1) | n大时候较好 |
堆排序 | O(nlogn) | O(nlogn) | 不稳定 | O(1) | n大时候较好 |
冒泡排序
冒泡排序(Bubble Sorting)的基本思想是:通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就象水底下的气泡一样逐渐向上冒。
public class BucketSort {
public static void main(String[] args) {
int[] arrays = {6,3,7,1000,1,3};
for(int i=1;i<arrays.length;i++) {
for(int k=0;k<arrays.length-1;k++) {
if(arrays[k]>arrays[k+1]) {
int tempNum = arrays[k];
arrays[k] = arrays[k+1];
arrays[k+1] = tempNum;
}
}
}
}
public static void print(int[] arrays) {
for(int k=0;k<arrays.length;k++) {
System.out.print(arrays[k]+",");
}
}
}
插入排序
插入排序的代码实现虽然没有冒泡排序和选择排序那么简单粗暴,但它的原理应该是最容易理解的了,因为只要打过扑克牌的人都应该能够秒懂。插入排序是一种最简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序和冒泡排序一样,也有一种优化算法,叫做拆半插入。
/**
* 插入排序: 解题思路分布发
* 思想: 先选一个稳定值, 数组其他值和稳定值做比较
* 完成度:已完成
* */
public class InsertSort {
public static void main(String[] args) {
int[] arrays = {6,3,7,1000,1,3};
///1、第一次插入,期望结果:{3,6 ,7,1000,1,3};
for(int j = 1;j<arrays.length;j++) {
for(int i = 0;i<j;i++) {
if(arrays[j]<=arrays[i]) {
int tempNum = arrays[i];
arrays[i] = arrays[j];
arrays[j] = tempNum;
}
}
System.out.println("第"+j+"次找最小值:");
System.out.println(Arrays.toString(arrays));
}
}
}
选择排序
选择排序是一种简单直观的排序算法,无论什么数据进去都是 O(n²) 的时间复杂度。所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。
-
交换排序:
快速排序
快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要 Ο(nlogn) 次比较。在最坏状况下则需要 Ο(n²) 次比较,但这种状况并不常见。事实上,快速排序通常明显比其他 Ο(nlogn) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。快速排序又是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。
快速排序的名字起的是简单粗暴,因为一听到这个名字你就知道它存在的意义,就是快,而且效率高!它是处理大数据最快的排序算法之一了。虽然 Worst Case 的时间复杂度达到了 O(n²),但是人家就是优秀,在大多数情况下都比平均时间复杂度为 O(n logn) 的排序算法表现要更好,可是这是为什么呢,我也不知道。好在我的强迫症又犯了,查了 N 多资料终于在《算法艺术与信息学竞赛》
上找到了满意的答案
快速排序的最坏运行情况是 O(n²),比如说顺序数列的快排。 但它的平摊期望时间是 O(nlogn),且 O(nlogn) 记号中隐含的常数因子很小, 比复杂度稳定等于 O(nlogn) 的归并排序要小很多。 所以,对绝大多数顺序性较弱的随机数列而言,快速排序总是优于归并排序。
1、算法步骤
先从数列中取出一个数作为基准数。 分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。 再对左右区间重复第二步,直到各区间只有一个数。
/** * 描述:快速排序 * 完成度:已完成 * 时间复杂度: * 空间复杂度: * * 排序思想:快速排序(Quicksort)是对冒泡排序的一种改进。基本思想是:通过一趟排序将要排序的数据分割成独立的两部分, * 其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列 * */ public class QuickSort { public static void main(String[] args) { int[] nums = {5,1,1,2,0,0}; int[] arr = new int[8000000]; for (int i = 0; i < 8000000; i++) { arr[i] = (int) (Math.random() * 8000000); // 生成一个[0, 8000000) 数 } long s1 = System.currentTimeMillis(); new QuickSort().sortArray(arr); long s2 = System.currentTimeMillis(); System.out.println("快排耗时:"+(s2-s1)); } public int[] sortArray(int[] nums) { nums = quickSort(nums,0, nums.length-1); return nums; } public int[] quickSort(int[] nums,int left, int right) { //初始化l,r int l = left; int r = right; //左右端值交换中间变量 int tempVal =0; //中间节点 int pivot = nums[(left+right)/2]; //左右2端往中心遍历 while (l<r) { //左边往右遍历 while (nums[l]<pivot) { l++; } //右边往左遍历 while (nums[r]>pivot) { r--; } //当左右2端点相交,说明完成一次值交换 if(l>=r) { break; } tempVal = nums[l]; nums[l] = nums[r]; nums[r] = tempVal; //左边等于pivot,r后移 if(nums[l]==pivot) { r--; } //右边等于pivot,l后移 if(nums[r]==pivot) { l++; } } //防止栈溢出,当l=r条件恒成立 if(l==r) { l++; r--; } //左向右边递归 if(left<r) { quickSort(nums,left, r); } //右向左边递归 if(right>r) { quickSort(nums,l, right); } return nums; } }
基数排序
基数排序(桶排序)介绍:基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用基数排序法是属于稳定性的排序,基数排序法的是效率高的稳定性排序法基数排序(Radix Sort)是桶排序的扩展基数排序是1887年赫尔曼·何乐礼发明的。它是这样实现的:将整数按位数切割成不同的数字,然后按每个位数分别比较。
基数排序基本思想
将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。这样说明,比较难理解,下面我们看一个图文解释,理解基数排序的步骤,将数组 {53, 3, 542, 748, 14, 214} 使用基数排序, 进行升序排序。
![1684744709980](C:\Users\hp\Documents\WeChat Files\god8816\FileStorage\Temp\1684744709980.png)
![1684744731546](C:\Users\hp\Documents\WeChat Files\god8816\FileStorage\Temp\1684744731546.png)
![1684744753790](C:\Users\hp\Documents\WeChat Files\god8816\FileStorage\Temp\1684744753790.png)
/** * 基数排序 * 完成度:已完成 * 未理解 * */ public class RadixSort { public static void main(String[] args) { int arr[] = { 53, 3, 542, 748, 14, 214}; // 80000000 * 11 * 4 / 1024 / 1024 / 1024 =3.3G // int[] arr = new int[8000000]; // for (int i = 0; i < 8000000; i++) { // arr[i] = (int) (Math.random() * 8000000); // 生成一个[0, 8000000) 数 // } System.out.println("排序前"); Date data1 = new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String date1Str = simpleDateFormat.format(data1); System.out.println("排序前的时间是=" + date1Str); radixSort(arr); Date data2 = new Date(); String date2Str = simpleDateFormat.format(data2); System.out.println("排序前的时间是=" + date2Str); System.out.println("基数排序后 " + Arrays.toString(arr)); } //基数排序方法 public static void radixSort(int[] arr) { //根据前面的推导过程,我们可以得到最终的基数排序代码 //1. 得到数组中最大的数的位数 int max = arr[0]; //假设第一数就是最大数 for(int i = 1; i < arr.length; i++) { max = Math.max(max, arr[i]); } //得到最大数是几位数 int maxLength = (max + "").length(); //定义一个二维数组,表示10个桶, 每个桶就是一个一维数组 //说明 //1. 二维数组包含10个一维数组 //2. 为了防止在放入数的时候,数据溢出,则每个一维数组(桶),大小定为arr.length //3. 名明确,基数排序是使用空间换时间的经典算法 int buckeNum = 10; int[][] bucket = new int[buckeNum][arr.length]; //为了记录每个桶中,实际存放了多少个数据,我们定义一个一维数组来记录各个桶的每次放入的数据个数 //可以这里理解 //比如:bucketElementCounts[0] , 记录的就是 bucket[0] 桶的放入数据个数 int[] bucketElementCounts = new int[buckeNum]; //这里我们使用循环将代码处理 for(int i = 0 , n = 1; i < maxLength; i++, n *= buckeNum) { //(针对每个元素的对应位进行排序处理), 第一次是个位,第二次是十位,第三次是百位.. for(int j = 0; j < arr.length; j++) { //取出每个元素的对应位的值 int digitOfElement = arr[j] / n % buckeNum; //放入到对应的桶中 bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j]; bucketElementCounts[digitOfElement]++; } //按照这个桶的顺序(一维数组的下标依次取出数据,放入原来数组) int index = 0; //遍历每一桶,并将桶中是数据,放入到原数组 for(int k = 0; k < bucketElementCounts.length; k++) { //如果桶中,有数据,我们才放入到原数组 if(bucketElementCounts[k] != 0) { //循环该桶即第k个桶(即第k个一维数组), 放入 for(int l = 0; l < bucketElementCounts[k]; l++) { //取出元素放入到arr arr[index++] = bucket[k][l]; } } //第i+1轮处理后,需要将每个 bucketElementCounts[k] = 0 !!!! bucketElementCounts[k] = 0; } } } }
桶排序
桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。为了使桶排序更加高效,我们需要做到这两点:在额外空间充足的情况下,尽量增大桶的数量,使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中,数据范围在桶范围内。对于桶中元素的排序,选择何种比较排序算法对于性能的影响至关重要。
1、什么时候最快
当输入的数据可以均匀的分配到每一个桶中。
2、什么时候最慢
当输入的数据被分配到了同一个桶中。
/**
* 描述:桶排序
* 完成度:已完成
* */
public class BarrelSort {
public static void main(String[] args) {
int[] array = {1,1,1,1,1,1,1,1,0,1,2,3,5,4,9,7,8,6,10};
int[] arr = new int[8000000];
for (int i = 0; i < 8000000; i++) {
arr[i] = (int) (Math.random() * 8000000); // 生成一个[0, 8000000) 数
}
long s1 = System.currentTimeMillis();
new BarrelSort().barrelSort(arr);
long s2 = System.currentTimeMillis();
System.out.println("桶排序耗时:"+(s2-s1));
}
public int[] barrelSort(int[] array) {
//1、找最大值
int maxNum = Integer.MIN_VALUE;
for(int i=0;i<array.length;i++) {
if(array[i]>maxNum) {
maxNum = array[i];
}
}
//2、创建桶(范围是 max+1,应为数组从0开始)
int[] bucketArray = new int[maxNum+1];
//3、数据和桶编号做映射,如果映射关系成立,给桶标记
for(int i=0;i<array.length;i++) {
int num = array[i];
//4、如果有桶映射有数据给桶编号+1标志,相同的数累加1
bucketArray[num]=bucketArray[num]+1;
}
//5、遍历桶
int n = 0;
for (int i=0;i<bucketArray.length;i++) {
//每个桶有bucketNum个数
int bucketNum = bucketArray[i];
//标记一个桶有多少个元素
for(int k = 0 ;k<bucketNum;k++) {
//array的第n个元素=第N个桶,桶内元素第n个
array[n++] = i;
}
}
return array;
}
}
希尔排序
希尔排序法介绍希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。
希尔排序法基本思想是,希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4XS3JaJs-1685349042126)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20230522161756819.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cGaFceDn-1685349042127)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20230522161902505.png)]
public class ShellSort {
public static void main(String[] args) {
//int[] arr = { 8, 9, 1, 7, 2, 3, 5, 4, 6, 0 };
// 创建要给80000个的随机的数组
int[] arr = new int[5];
for (int i = 0; i < 5; i++) {
arr[i] = (int) (Math.random() * 8000000); // 生成一个[0, 8000000) 数
}
System.out.println("排序前");
Date data1 = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date1Str = simpleDateFormat.format(data1);
System.out.println("排序前的时间是=" + date1Str);
shellSort(arr); //交换式
//shellSort2(arr);//移位方式
Date data2 = new Date();
String date2Str = simpleDateFormat.format(data2);
System.out.println("排序前的时间是=" + date2Str);
System.out.println(Arrays.toString(arr));
}
// 使用逐步推导的方式来编写希尔排序
// 希尔排序时, 对有序序列在插入时采用交换法,
// 思路(算法) ===> 代码
public static void shellSort(int[] arr) {
int temp = 0;
int count = 0;
// 根据前面的逐步分析,使用循环处理
for (int gap = arr.length / 2; gap > 0; gap = gap/2) {
for (int i = gap; i < arr.length; i++) {
// 遍历各组中所有的元素(共gap组,每组有个元素), 步长gap
for (int j = i - gap; j >= 0; j = j-gap) {
// 如果当前元素大于加上步长后的那个元素,说明交换
if (arr[j] > arr[j + gap]) {
temp = arr[j];
arr[j] = arr[j + gap];
arr[j + gap] = temp;
}
}
}
}
}
//对交换式的希尔排序进行优化->移位法
public static void shellSort2(int[] arr) {
// 增量gap, 并逐步的缩小增量
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
// 从第gap个元素,逐个对其所在的组进行直接插入排序
for (int i = gap; i < arr.length; i++) {
int j = i;
int temp = arr[j];
if (arr[j] < arr[j - gap]) {
while (j - gap >= 0 && temp < arr[j - gap]) {
//移动
arr[j] = arr[j-gap];
j -= gap;
}
//当退出while后,就给temp找到插入的位置
arr[j] = temp;
}
}
}
}
}
归并排序
归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。 归并排序分为2步,拆分和合并。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RC0BYvB4-1685349042128)(C:\Users\hp\Documents\WeChat Files\god8816\FileStorage\Temp\1684744088632.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MXBE8UYi-1685349042128)(C:\Users\hp\Documents\WeChat Files\god8816\FileStorage\Temp\1684744136967.png)]
/**
* 归并排序
* 完成度:已完成
* */
public class MergetSort {
public static void main(String[] args) {
//int arr[] = { 8, 4, 5, 7, 1, 3, 6, 2 }; //
//测试快排的执行速度
// 创建要给80000个的随机的数组
int[] arr = new int[8000000];
for (int i = 0; i < 8000000; i++) {
arr[i] = (int) (Math.random() * 8000000); // 生成一个[0, 8000000) 数
}
System.out.println("排序前");
Date data1 = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date1Str = simpleDateFormat.format(data1);
System.out.println("排序前的时间是=" + date1Str);
int temp[] = new int[arr.length]; //归并排序需要一个额外空间
mergeSort(arr, 0, arr.length - 1, temp);
Date data2 = new Date();
String date2Str = simpleDateFormat.format(data2);
System.out.println("排序前的时间是=" + date2Str);
}
//分+合方法
public static void mergeSort(int[] arr, int left, int right, int[] temp) {
if(left < right) {
int mid = (left + right) / 2; //中间索引
//向左递归进行分解
mergeSort(arr, left, mid, temp);
//向右递归进行分解
mergeSort(arr, mid + 1, right, temp);
//合并
merge(arr, left, mid, right, temp);
}
}
//合并的方法
/**
*
* @param arr 排序的原始数组
* @param left 左边有序序列的初始索引
* @param mid 中间索引
* @param right 右边索引
* @param temp 做中转的数组
*/
public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
int i = left; // 初始化i, 左边有序序列的初始索引
int j = mid + 1; //初始化j, 右边有序序列的初始索引
int t = 0; // 指向temp数组的当前索引
//(一)
//先把左右两边(有序)的数据按照规则填充到temp数组
//直到左右两边的有序序列,有一边处理完毕为止
while (i <= mid && j <= right) {
//如果左边的有序序列的当前元素,小于等于右边有序序列的当前元素
//即将左边的当前元素,填充到 temp数组
//然后 t++, i++
if(arr[i] <= arr[j]) {
temp[t] = arr[i];
t += 1;
i += 1;
} else { //反之,将右边有序序列的当前元素,填充到temp数组
temp[t] = arr[j];
t += 1;
j += 1;
}
}
//(二)
//把有剩余数据的一边的数据依次全部填充到temp
while( i <= mid) { //左边的有序序列还有剩余的元素,就全部填充到temp
temp[t] = arr[i];
t += 1;
i += 1;
}
while( j <= right) { //右边的有序序列还有剩余的元素,就全部填充到temp
temp[t] = arr[j];
t += 1;
j += 1;
}
//(三)
//将temp数组的元素拷贝到arr
//注意,并不是每次都拷贝所有
t = 0;
int tempLeft = left; //
//第一次合并 tempLeft = 0 , right = 1 // tempLeft = 2 right = 3 // tL=0 ri=3
//最后一次 tempLeft = 0 right = 7
while(tempLeft <= right) {
arr[tempLeft] = temp[t];
t += 1;
tempLeft += 1;
}
}
}
堆排序
堆排序基本介绍堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆, 注意 : 没有要求结点的左孩子的值和右孩子的值的大小关系。每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆大顶堆举例说明。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-95ErdP3Q-1685349042129)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20230522164203118.png)]
2、经典算法介绍
递归算法
无处不在,就是自己循环调用自己。
回溯算法
【代码随想录】回溯能够解决的所有问题都能够抽象为树形结构(多叉树)。注意,回溯法解决的是在集合中递归地查找子集,集合大小就是树的宽度,递归的深度就是树的深度。着重注意:在集合中递归查找。 回溯算法剪枝是比较难的一部分。 常见leetcode题:组合问题、切割问题、子集问题、排列问题、棋盘问题、电话号码的字母组合
void backtracking(参数) {//1:函数参数及返回值
if (终止条件) { //2:终止条件
存放结果;
return;
}
//3:回溯搜索逻辑
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
贪心算法
贪婪算法(贪心算法)是指在对问题进行求解时,在每一步选择中都采取最好或者最优(即最有利)的选择,从而希望能够导致结果是最好或者最优的算法。
贪婪算法所得到的结果不一定是最优的结果(有时候会是最优解),但是都是相对近似(接近)最优解的结果。
深度优先遍历
从一个点出发,选择一个遍历方向一直遍历到结束。
广度优先遍历
从一个点出发,尝试遍历更多平行节点。直到平行节点遍历完毕。
动态规划算法
利用递归暴利解决一类问题。 动态规划大致分5步: dp数组及下际的含义,递推公式、dp数组如何初始化、遍历顺序、打印dp数组。 常见leetcode题:背包问题、打家劫舍问题、股票问题、子序列问题。 个人感觉是算法中最难的,地推公式这关都不好过。
3、常见算法
1、迪杰斯特拉(Dijkstra)算法介绍
二、数据结构介绍
常用的数据结构,字符串,数组,链表,跳跃表,hash,平衡树、红黑树、B树、tire树、LSM树、treap树,有向图、无向图。 数据结构可视化网站推荐
数据结构 | 平均时间 | 最差时间 | 稳定度 |
---|---|---|---|
字符串 | - | - | - |
数组 | - | - | - |
链表 | - | - | - |
Hash | O(1) | O(1) | 稳定 |
跳跃表 | O(nlogn) | O(n) | 不稳定 |
二叉树 | |||
大顶堆 | |||
小顶堆 | |||
红黑树 | 稳定 | ||
B树 | |||
B+树 | |||
Treap树 | |||
LSM树 | |||
Trie树 | 不稳定 | ||
无向图 | |||
有向图 | |||
带权图(也叫网) |
1、字符串
字符串是最常用的数据结构,不同遍历算法不同,时间复杂度也不同。
2、数组
数组也是常用的数据结构,分为一维数组、多维数组、稀疏数组;稀疏数组常用于数据压缩数组;比如游戏地图
3、链表
链表分为单链表、双链表;不同遍历算法不同; 场景:LinkedList
4、Hash
Hash是一个时间复杂度最高的数据结构,但是其不能查询相邻数据信息。 场景:hashMap、redis
5、跳跃表
跳跃表结构多表;性能最好时接近hash,平均性能接近平衡树;最差性能接近字符串,是一种不稳定的数据结构; 场景:需要查询相邻数据的结构 redis Zset结构、ConcurrentSkipListMap、ConcurrentSkipListSet
6、二叉树
二叉树有很多种类;满二叉树、完全二叉树、顺序存储二叉树、线索化二叉树、平衡二叉树(AVL);我们用最多的是平衡二叉树、顺序存储二叉树、替罪羊树、赫夫曼树。
满二叉树定义:如果该二叉树的所有叶子节点都在最后一层,并且结点总数= 2^n -1 , n 为层数,则我们称为满二叉树。
完全二叉树定义:如果该二叉树的所有叶子节点都在最后一层或者倒数第二层,而且最后一层的叶子节点在左边连续,倒数第二层的叶子节点在右边连续,我们称为完全二叉树。
平衡二叉树: 平衡二叉树也叫平衡二叉搜索树(Self-balancing binary search tree)又被称为AVL树, 可以保证查询效率较高。具有以下特点:它是一. 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等
其他的;请看韩顺平老师讲解,我们最常用的二叉树就是平衡二叉树、红黑树
7、大顶堆
二叉树的一种;每个结点的值都大于或等于其左右孩子结点的值。 场景:少见
8、小顶堆
二叉树的一种;每个结点的值都小于或等于其左右孩子结点的值。 场景:少见
9、红黑树
平衡二叉树的一种;使用频率最高的二叉树; 场景:hashmap;操作系统;nginx
10、B树
B树是IO友好型树;其使用场景是:mysql innodb存储引擎下非聚集索引,MyiSam索引引擎,这2者区别前者记录的是主键id,需要从主键查询数据。 后者记录的是数据的硬件地址,直接从目标寻址,时间复杂度O(1).
11、B+树
B+树是IO友好型树;其使用场景是:mysql innodb存储引擎下聚集索引,其特点子节点记录了大量的数据,子节点通过双向链表串联起来。
12、Treap树
二叉随机平衡收索堆树;使用场景不详细
13、LSM树
14、Trie树
15、多叉树
16、无向图
17、有向图
18、带权图(网)
左边连续,倒数第二层的叶子节点在右边连续,我们称为完全二叉树。
平衡二叉树: 平衡二叉树也叫平衡二叉搜索树(Self-balancing binary search tree)又被称为AVL树, 可以保证查询效率较高。具有以下特点:它是一. 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等
其他的;请看韩顺平老师讲解,我们最常用的二叉树就是平衡二叉树、红黑树
7、大顶堆
二叉树的一种;每个结点的值都大于或等于其左右孩子结点的值。 场景:少见
8、小顶堆
二叉树的一种;每个结点的值都小于或等于其左右孩子结点的值。 场景:少见
9、红黑树
平衡二叉树的一种;使用频率最高的二叉树; 场景:hashmap;操作系统;nginx
10、B树
B树是IO友好型树;其使用场景是:mysql innodb存储引擎下非聚集索引,MyiSam索引引擎,这2者区别前者记录的是主键id,需要从主键查询数据。 后者记录的是数据的硬件地址,直接从目标寻址,时间复杂度O(1).
11、B+树
B+树是IO友好型树;其使用场景是:mysql innodb存储引擎下聚集索引,其特点子节点记录了大量的数据,子节点通过双向链表串联起来。
12、Treap树
二叉随机平衡收索堆树;使用场景不详细