本文脑图:
1 数组、链表
方式一 静态初始化
int[] arr0 = new int[]{2,4,6};
方式二 动态初始化
int[] arr1 = new int[3];//[3]代表这个数组可以存放3个元素
arr1[0] = 2;//[0]代表数组的第1个数据 将2存进去到数组第1个位置
arr1[1] = 4;//[1]代表数组的第2个数据 将4存进去到数组第2个位置
arr1[2] = 6;//[2]代表数组的第3个数据 将6存进去到数组第3个位置
方式三 隐式初始化
int[] arr1 = {2,4,6};
链表:创建链表的过程和创建数组的过程不同,不会先划出一块连续的内存。因为链表中的数据是不连续的,链表在存储数据的内存中有两块区域,一块区域用来存储数据,一块区域用来记录下一个数据保存在哪里(指向下一个数据的指针)。当有数据进入链表时候,会根据指针找到下一个存储数据的位置,然后把数据保存起来,然后再指向下一个存储数据的位置。这样链表就把一些碎片空间利用起来了,虽然链表是线性表,但是并不会按线性的顺序存储数据。
拓展必知:ArrayList和linklist。
目前,我比较多使用的是arraylist,毕竟很多开源小项目只确保程序能运行,不管他性能好不好,毕竟小项目,肉眼上看也看不出来。
- ArrayList是基于数组实现的,LinkedList是基于双链表实现的。LinkedList可以作为双向队列 ,栈和List集合使用,功能强大。
- 因为Array是基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的,可以直接返回数组中index位置的元素,因此在随机访问集合元素上有较好的性能。Array获取数据的时间复杂度是O(1),但是要插入、删除数据却是开销很大的。本人也大致了解到这,剩下的要接触在市场上已经在用的项目才更好地去理解运用。
- LinkedList需要更多的内存,因为ArrayList的每个索引的位置是实际的数据,而LinkedList中的每个节点中存储的是实际的数据和前后节点的位置。
2 队列、栈
队列:队列是一种先进先出的数据结构,数组和链表也都可以生成队列。当数据进入到队列中时也是先进入的在下面后进入的再上面,但是出队列的时候是先从下面出,然后才是上面的数据出,最晚进入的队列的,最后出。
栈:栈是一种先进后出的数据结构,数组和链表都可以生成栈。当数据进入到栈时会按照规则压入到栈的底部,再次进入的数据会压在第一次的数据上面,以此类推,在取出栈中的数据的时候会先取出最上面的数据,所以是先进后出。
由于数组和链表都可以组成栈,所以操作特点就需要看栈是由数组还是链表生成的了,然后就会继承相应的操作特点。
3 树、堆、图
树: 此处树特指二叉树(BinaryTree)。二叉树是n(n>=0)个结点的有限集合,该集合或者为空集(空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。二叉树的特点:
- 每个结点最多有两棵子树。(注意:不是都需要两棵子树,而是最多可以是两棵,没有子树或者有一棵子树也都是可以的。)
- 左子树和右子树是有顺序的,次序不能颠倒。
- 即使树中某结点只有一棵子树,也要区分它是左子树还是右子树,下面是完全不同的二叉树:
堆:堆是一颗完全二叉树。在这棵树中,所有父节点都满足大于等于其子节点的堆叫大根堆。所有父节点都满足小于等于其子节点的堆叫小根堆。堆虽然是一颗树,但是通常存放在一个数组中,父节点和孩子节点的父子关系通过数组下标来确定。
图:(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。图有各种形状和大小。边可以有权重(weight),即每一条边会被分配一个正数或者负数值。
4 哈希表
哈希表在应用中也是比较常见的,就如Java中有些集合类就是借鉴了哈希原理构造的,例如HashMap,HashTable等,利用hash表的优势,对于集合的查找元素时非常方便的,然而,因为哈希表是基于数组衍生的数据结构,在添加删除元素方面是比较慢的,所以很多时候需要用到一种数组链表来做,也就是拉链法。拉链法是数组结合链表的一种结构,较早前的hashMap底层的存储就是采用这种结构,直到jdk1.8之后才换成了数组加红黑树的结构,其示例图如下:
拓展必知:hashmap和hashtable(略)
hashmap(基础+源码+面试题)参考链接
5 7种基本排序
七种排序算法(最常见)的各种指标比较,稳定是只能是两两之间进行操作
(不记表,记排序规则,这个顺序记忆方便,记住动态图,看完后在看表,我这个表好有规律的哦)
排序方法 | 平均情况 | 最好情况 | 最坏情况 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | O(n²) | O(n) | O(n²) | O(1) | 稳定 |
插入排序 | O(n²) | O(n) | O(n²) | O(1) | 稳定 |
选择排序 | O(n²) | O(n²) | O(n²) | O(1) | 稳定 |
归并排序 | O(n logn) | O(n logn) | O(n logn) | O(n) | 稳定 |
堆排序 | O(n logn) | O(n logn) | O(n logn) | O(1) | 不稳定 |
快速排序 | O(n logn) | O(n logn) | O(n²) | O(logn)~O(n) | 不稳定 |
希尔排序 | O(n logn)~O(n²) | O(n^1.3~1.5) | O(n²) | O(1) | 不稳定 |
5.1 冒泡排序
两两比较,大的后排,所以是o(n²),如果一开始就是排序好的,只进行一次两两比较,没交换那就是o(n)
5.2 直接插入排序
取下一个数和前面比较,比它打就排在它后面,如果一开始就是排序好的,只进行取出插入,没进行和前面比较那就是o(n)。
5.3 折半插入排序:
5.4 选择排序
找最小,放前头,都要全找一遍,两次for叠加,所以是o(n²)
5.5 归并排序
从小到大排序,首先让数组中的每一个数单独成为长度为1的区间,然后两两一组有序合并,得到长度为2的有序区间,依次进行,直到合成整个区间。
5.6 快速排序
从小到大排序:一个基准数,小于基准数排放左边,大的放右边
在数组中随机选一个数(默认数组首个元素),数组中小于等于此数的放在左边,大于此数的放在右边,再对数组两边递归调用快速排序,重复这个过程。
5.7 希尔排序
先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序font>,具体算法描述:
6 简单算法篇
6.1 冒泡排序
public static int[] bubbleSort(int[] array){
if(array.length > 0){
for(int i = 0;i<array.length;i++){
for(int j = 0;j<array.length - 1 - i;j++){
if(array[j] > array[j+1]){
int temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
}
}
return array;
}
6.2 直接插入排序
public static int[] insertionSort(int[] array) {
if (array.length == 0)
return array;
int current;
for (int i = 0; i < array.length - 1; i++) {
current = array[i + 1];
int preIndex = i;
while (preIndex >= 0 && current < array[preIndex]) {
array[preIndex + 1] = array[preIndex];
preIndex--;
}
array[preIndex + 1] = current;
}
return array;
}
6.3 选择排序
public static int[] selectionSort(int[] array) {
if (array.length == 0)
return array;
for (int i = 0; i < array.length; i++) {
int minIndex = i;
for (int j = i; j < array.length; j++) {
if (array[j] < array[minIndex]) //找到最小的数
minIndex = j; //将最小数的索引保存
}
int temp = array[minIndex];
array[minIndex] = array[i];
array[i] = temp;
}
return array;
}
6.4 归并排序
public static int[] MergeSort(int[] array) {
if (array.length < 2) return array;
int mid = array.length / 2;
int[] left = Arrays.copyOfRange(array, 0, mid);
int[] right = Arrays.copyOfRange(array, mid, array.length);
return merge(MergeSort(left), MergeSort(right));
}
public static int[] merge(int[] left, int[] right) {
int[] result = new int[left.length + right.length];
for (int index = 0, i = 0, j = 0; index < result.length; index++) {
if (i >= left.length)
result[index] = right[j++];
else if (j >= right.length)
result[index] = left[i++];
else if (left[i] > right[j])
result[index] = right[j++];
else
result[index] = left[i++];
}
return result;
}
6.5 快速排序
public static int[] QuickSort(int[] array, int start, int end) {
if (array.length < 1 || start < 0 || end >= array.length || start > end) return null;
int smallIndex = partition(array, start, end);
if (smallIndex > start)
QuickSort(array, start, smallIndex - 1);
if (smallIndex < end)
QuickSort(array, smallIndex + 1, end);
return array;
}
//快速排序算法
public static int partition(int[] array, int start, int end) {
int pivot = (int) (start + Math.random() * (end - start + 1));
int smallIndex = start - 1;
swap(array, pivot, end);
for (int i = start; i <= end; i++)
if (array[i] <= array[end]) {
smallIndex++;
if (i > smallIndex)
swap(array, i, smallIndex);
}
return smallIndex;
}
//交换数组内两个元素
public static void swap(int[] array, int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
6.6 希尔排序
public static int[] ShellSort(int[] array) {
int len = array.length;
int temp, gap = len / 2;
while (gap > 0) {
for (int i = gap; i < len; i++) {
temp = array[i];
int preIndex = i - gap;
while (preIndex >= 0 && array[preIndex] > temp) {
array[preIndex + gap] = array[preIndex];
preIndex -= gap;
}
array[preIndex + gap] = temp;
}
gap /= 2;
}
return array;
}
6.7 其他算法,优先度低
算法重要,但优先度低,因短时间补不回来,战略性延后。本人身边同学面试的算法有:链表反转(超多人面试到)、合并有序数组、快速排序算法(超多人面试到)。
其余算法可参考:剑指 Offer 题解和Leetcode 题解
7 参考链接
数组的定义、声明、初始化、遍历、冒泡排序:https://blog.csdn.net/qq_43519384/article/details/106203666
“高频面经”之数据结构与算法篇:https://blog.csdn.net/qq_36936730/article/details/104342464
十大经典排序算法最强总结(含JAVA代码实现):https://blog.csdn.net/sinat_38325967/article/details/88851025
如有遗漏,请在评论区留言。
8 备注
大多公司都有各自的题库,大厂需要刷对应面经。