什么是时间复杂度:
一个算法流程中,常数操作数量的指标
什么常数时间操作:
大O描述的是算法的运行时间和输入数据的关系
一个操作 如果和数据量没有关系,每次都是固定时间内操作,叫做常数操作。如做加减操作,数组寻址
O(1)一次常数操作的平均时间
例如:for循环取出一个数及数组遍历数组寻址都为常数操作
O(logn)与忽略对数的底(log10n)或者是( log2n)
for(int i=1;i<n;i+=i){}
优化可以考虑通过推导的额数学公式将时间复杂度降一个维度(例如:等差数列求和)
什么是正确的算法:
对问题的独到见解;优化;代码规范;容错性
开始答题注意:
1.关注题目中的条件:
例如:
给定一个有序数组 --> 二分查找法
设计一个O(nlogn)的算法 --> logn 的算法基本与分治法(搜索树)相关 有可能与排序相关
无需考虑额外的空间 --> 开辟额外的空间 再空间换时间
数据规模大概是10000 --> 采用 O(n2)注:当没有思路的时候不要忽略暴力解法。暴力解法通常是思考的起点。
2.如何优化算法:
* 遍历常见的算法思路
* 遍历常见的数据结构
* 空间和时间的交换(哈希表)
* 预处理信息(排序)
* 在瓶颈处寻找答案:O(nlogn)+O(n^2);O(n^3)
3.实际编写问题:
* 极端条件的判断 --> 数组是否为空?字符串为空?数量为0?指针为NULL?
* 变量名
* 模块化,复用性
归并排序算法时间复杂度默认为O(nlog)
4.空间复杂度
* 多开一个为n的辅助数组:O(n)
* 多开一个辅助的二维数组:O(n^2)
* 多开常数空间(如临时变量):O(1)
注:递归调用是有空间代价的递归深度有多少 空间复杂度就有多少
5.小技巧及学习思路
* 异或运算符特点:一个数据A对一个数据B异或两次的结果是数据A常用来数据交换
* 计算时间复杂度注意循环判断条件以及每次遍历条件
* 学习优秀的程序思想 并尝试分享一些自己的学习心得与完成和在做的项目
排序 :
注意:mid=(l+r)/2 当l和r为很大的数字时会发生溢出的错误
交换排序 ( 冒泡排序 O ( n^2 ) )
快速排序O(nlogn)) 快速排序可求第n大数判断在第一个比较数左边还是右边可进行舍弃另一边
插入排序 ( 直接插入排序 O( n^2),希尔排序n^(2/3) )
选择排序 ( 简单选择排序 O ( n^2 ),堆排序)
归并排序 ( 自顶向下归并排序,自底向上归并排序 (没有通过索引直接获取元素适合于链表排序)) O(nlogn)
Todo --> 链表采用自底向上归并排序
最后一次归并可求逆数对当后一半在为排序完前一组数组进行赋值堆排序思路:
最经典可通过数组存储从下标1开始
二叉树从上到下从左到右逐次+1
数组的索引对应二叉树的索引最大堆:(最小堆类似)
堆中的某个节点的值总是不大于其父节点的值(可能大于其兄弟节点);
堆总是一个完全二叉树。(所有子节点优先集中在二叉树的左边)
每一个节点左节点是其两倍 右节点是其两倍加1
寻找父节点可以通过计算机的方式i/2 向下取整
原地堆化: heapify 可以优化从一个空堆逐渐插入n个元素 O( nlogn ) -- > O(n)
基数排序
动态规划和贪心算法:
分治递归:通常将问题一分为二再进行操作,而递归往往会出现重复判断 分治则进行更优化
贪心算法(Greedy):在对问题求解时,总是做出当前看来是最好的选择(具有一定的局限性)-->互相为倍数的情况比较适用
适用场景:
问题可以分解为子问题来解决。子问题的最优解能递推到最终问题的
最优解。子问题的最优解为最有子结构
贪心算法与动态规划不同:在于它堆子问题的解决方案都能做出选择,不能回退
动态规划(DP)会保存以前的运算结果,并根据以前的结果对当前进行选择,有回退功能