1. 如何评估算法的时间开销?
如果让算法先运行事后统计运行时间,存在什么问题?
- 和机器性能有关,比如超级计算机vs单片机
- 和编程语言有关,越高级的语言执行效率越低
- 和编译程序产生的机器质量质量有关
- 有些算法是不能事后再统计的,如:导弹控制算法
能够排除与算法本身无关的外界因素,以及能否事先估计呢?
算法的时间复杂度应该是事前预估算法时间开销 T ( n ) T(n) T(n)与问题规模 n n n的关系。
2.常见数据结构基本操作的时间复杂度分析
数据结构 | 查找 | 插入 | 删除 | 遍历 |
---|---|---|---|---|
无序数组 | O(N) | O(N) | O(N) | O(N) |
有序数组 | O(N) | O(N) | O(N) | O(N) |
单向链表 | O(N) | O(N) | O(N) | O(N) |
双向链表 | O(N) | O(1) | O(1) | O(N) |
二叉查找树(一般情况) | O(logN) | O(logN) | O(logN) | O(N) |
二叉查找树(最坏情况) | O(N) | O(N) | O(N) | O(N) |
红黑树 | O(logN) | O(logN) | O(logN) | O(N) |
平衡树 | O(logN) | O(logN) | O(logN) | O(N) |
(1)数组
无序数组:
查:数组支持随机访问,根据下标随机访问的时间复杂度为O(1)。根据值来查找的时间复杂度为O(N)。
删除:使用顺序查找找到删除位置,再把删除位置后的所有元素前移一位。总时间复杂度:O(n)+O(n)=O(n)
插入:如果空间充足直接添加在后面,时间复杂度为O(1)。如果空间不足,整个数组移到另一个空间,再添加元素。总的时间复杂度:O(1)+O(n)=O(N)。
有序数组:
查:二分查找的时间复杂度为O(log2N)。
删除: 使用二分查找找到删除位置,再把删除位置后的所有元素前移一位。总时间复杂度:O(logn)+O(n)=O(N).
插入:查找插入位置用二分查找是O(log2n)。但是数组的插入操作为了保证有序性需要将插入位置后的元素全部后移一位,这需要O(n)。所以总的时间复杂度是O(n)。( O(log2n)+O(n)=O(n) )
(2) 链表
单向链表
查:只查链表头的元素时间复杂度为O(1),根据索引查找的时间复杂度为O(n),判断是否包含的时间复杂度为O(n).
删除:在链表头删除的时间复杂度为O(1),在链表尾删除的时间复杂度为O(n),根据索引删除的时间复杂度为O(n/2)=O(n).
插入:在链表头插入的时间复杂度为O(1),在链表尾插入的时间复杂度为O(n),根据索引插入的时间复杂度为O(n/2)=O(n).
双向链表
单链表查找后继节点时间复杂度为:0(1),而查找前驱节点为0(n),
为克服这个缺陷,有了双向链表。
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。
双链表(双向链表)知道要删除某一节点p时,获取其前驱节点q的方式为 q = p->prior,不必再进行遍历。故时间复杂度为O(1)。而若只知道待删除节点的序号,则依然要按序查找,时间复杂度仍为O(n)。
单向链表和双向链表在操作头节点、链表的前半部分、链表的中间节点时都是一样的。而双向链表在操作尾节点、链表后半部分是大大优于单向链表的。
(3)二叉树
在一个树中查找一个数字,第一次在根节点判断,第二次在第二层节点判断.以此类推,树的高度是多少就会判断多少次,树的高度和节点的关系就是以2为底,树的节点总数n的对数log2N
二叉查找树比普通树查找更快,查找、插入、删除的时间复杂度为O(logN)。但是二叉查找树有一种极端的情况,就是会变成一种线性链表似的结构。此时时间复杂度就变味了O(N).