最常用的、最基础的数据结构和算法:
10个数据结构:数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Tire
10个算法:递归、排序、二分查找、搜索树、哈希算法、贪心算法、分治算法、回溯算法、动态规划、字符串匹配算法
事半功倍的学习技巧:
1、边学边练,适度刷题
学以致用
2、多问、多思考、多互动
3、打怪升级学习法
在枯燥的学习中,给自己指定合适的目标
4、知识需要时间的沉淀,不会一下掌握所有
相信时间的力量,时间是有复利效应的
一、复杂度分析
为什么要进行复杂度分析?
-
和性能测试相比,复杂度分析有不依赖执行环境、成本低、效率高、易操作、指导性强的特点。
-
掌握复杂度分析,将能编写出性能更优的代码,有利于降低系统开发和维护成本
-
复杂度分析是算法学习的精髓,掌握了它,就能知道算法的执行效率,在工作中得心应手。
时间复杂度:全程是渐进时间复杂度,表示算法的执行时间与数据规模之间的关系。
几种常见的时间复杂度量级:
O(1)<O(logn)<O(√n)<O(n)<O(nlogn)<O(n²)<O(2^n)<O(n!)
1、O(1)
O(1) 指常量级时间复杂度的一种表示方法不是只执行了一行代码。
int i = 8;
int j = 6;
int sum = i+j;
2、O(logn)、O(nlogn)
非常常见也是最难分析的一种时间复杂度。
int i = 1;
while(i <= n){
i *= 2;
}
分析:
可以看出第三行是执行循环次数最多的,所以可算出这行代码执行了多少次,就能知道这段代码的时间复杂度。
可得出这是一个等比数列:2^x = n ,得出x = log2n ,所以这段代码的时间复杂度是O(nlog2n)
比如:归并排序、快速排序时间复杂度都是O(nlogn)。
复杂度分类:
最好时间情况复杂度、最坏情况时间复杂度、平均情况时间复杂度、均摊时间复杂度
空间复杂度:全程是渐进空间复杂度,表示算法的存储空间与数据规模之间的关系。
二、数组
数组是一种线性数据结构。它用一种连续的内存空间来存储一组有相同类型的数据。
1、线性表
线性表就是数据排成想像一条线一样的结构。每个线性表上的数据最多只有前和后两个方向。
像数组、链表、队列、栈都是线性结构。
2、非线性表
数据之间没有简单的线性关系。比如:二叉树、堆、图等等
数组和链表的区别:数组支持随机访问,根据下标随机访问的时间为O(1);链表插入删除的时间复杂度为O(1)。
三、链表
1、分类:
单链表、双链表、循环链表。其中最常用的是单链表。
2、单链表
结点:链表通过指针将一组零散的内存块串联到一起,其中把内存块称为链表的 结点 。第一个结点称作为 头结点,最后一个结点称作为尾结点。而尾结点特殊的地方是:指针不是指向下一个结点,而是指向一个空地址 NULL,表示这是链表上最后一个结点。
结点的下个指针叫做 后继指针 next。
链表的插入和删除的时间复杂度为O(1)
3、循环链表
循环链表的尾结点指针指向链表的头节点。
4、双向链表
它支持两个方向,每个结点不止有一个后继指针 next 指向后面的结点,还有一个前驱指针 prev 指向前面的结点。
从图可以看出,双向链表需要额外的两个空间来存储后继结点和前驱结点的地址。所以,如果存储同样多的数据,双向链表要比单链表占用更多的内存空间。虽然两个指针比较浪费存储空间,但可以支持双向遍历,这样也带来了双向链表操作的灵活性。
5、空间换时间的思想:
当内存空间充足的时候,如果我们更加追求代码的执行速度,我们就可以选择空间复杂度相对较高、但时间复杂度相对很低的算法或者数据结构。相反,如果内存比较紧缺,比如代码跑在手机或者单片机上,这个时候,就要反过来用时间换空间的设计思路。
所以,对于执行较慢的程序,可以通过消耗更多的内存(空间换时间)来进行优化;而消耗过多内存的程序,可以通过消耗更多的时间(时间换空间)来降低内存的消耗。
6、链表和数组的区别
数组 | 链表 | |
---|---|---|
插入删除 | O(n) | O(1) |
随机访问 | O(1) | O(n) |