数据结构与算法
前提概述
本篇的目的
事实上,我不按照传统的讲法一个一个数据结构无聊的诉说下去,我尽可能让你感兴趣且能够快速入门,所以有些地方可能不那么规范和严谨,或者因为我的学识尚浅难免出现或多或少或大或小的问题,虚心向各位大手请教,欢迎指出和讨论。
什么是数据结构
简单的来说就是一段数据的存储方式
什么是算法
算法就是一个问题的解决方案,而好的算法对于解决这个问题则有更少的时间或空间成本
比如我们在小学的时候,做1+2+3+4+......+99+100这个算数时,可以一个一个相加,也可以根据这个规则知道头尾相加等于100,这样我们简单且更快就能算出4900+50=4950
两者的联系
算法往往是针对特定数据结构的,例如一串数据用两种结构,链表和数组来保存,那么对应的插入算法就完全不同了
对于链表,我插入一个B数据只需要将A数据的链尾指针指向B的头部,然后,把B数据的尾部指针指向原先A指针指向的C数据,这样就能将数据插入。
对于数组,上面的算法就完全行不通,因为数组的结构决定了两个数据之间是紧密的,所以无法将所谓A数据和C数据分开,在中间插入B数据。这个算法主要解决办法是,开辟一个新的内存空间,这个新的内存空间比之前的数组空间正好大一个B数据的大小,而把原数组A之前的先复制过来,然后把B数据顺序放入,再把原数组C之后的复制过去,正好填满这个新开辟的数据空间,这样就完成了数组插入。
数组和链表的各自优势劣势我就懒得说了,老生常谈了。
从算法成本开始
我们前文说过衡量一个算法好坏基本是依照两点,一个是时间成本,一个是空间成本
如何得知时间成本
第一点在Java当中我可以这么直观的得到这个计算速度,如下
public class TimeComplexity {
public static void main(String[] args) {
int a = 0;
long before = System.currentTimeMillis();
for(long i = 0;i<10000L;i++){
likeAAlgorithm(a);
}
long after = System.currentTimeMillis();
System.out.println(after-before);
}
public static void likeAAlgorithm(int a){
//假装里面是算法
}
}
不用解释吧,after-before得到时间差就是算法时间成本,而因为CPU计算实在太快了,为了能够直观的看到两个算法的差别(你可以试试看,只要不是太夸张的故意开销,基本上print都是1),for循环了10000次!
更学术的科学的定义—时间复杂度Big O
时间复杂度
不考虑这个算法的必须操作,例如循环、初值、初始化
不考虑常数项,例如2n、5n,记O(n)
不考虑低次项、例如n^2+n+5,记为O(n^2)
举个例子吧
访问数组的某个位置的值
因为我们知道在数组中由于他的数据是紧密排列的,所以当我想要找其中的一个数据,我知道他的位置,相当于一个偏移量,只需要一次就完成了,无论这个数组被扩大到多大,只要是顺序紧密排列的,我们都只需要经过一次就能找到这个位置的值。所以他的时间复杂度被记为O(1),这里的1并不是一次的意思,而是指一个常数量!不会因为数据量n变大,而时间复杂度跟着以某种维度变大,他一直维持着一个常数量级,就记为O(1)。
访问列表时
初学者,可能会回答,每个位置不同,emmm确实如此,链表的数据结构决定他只能一个一个遍历的找,离头节点越远越久,但是这里也引出了第二个特性:要考虑最坏情况,显然考虑最坏情况就是在最后一个节点,显然复杂度是O(n)
O(n)的含义是,随着数据的增大,而这个算法所花费的时间呈线性增大,如Y=KX+B,忽略常数项,Y=O(X)。
求数组的平均数
平均数就是arr[1]+arr[2]+.....+arr[n]/n,这个数学公式随着我数组的数据量(也就是n)增大,也是线性增大(分子的维数是n),所以显然时间复杂度是O(n)