资本贫穷,精神贫穷,你是不是双穷?
做好软件开发工作,就必须了解如何组织各种数据在计算机中的存储、传递和转换。
你真的了解吗?
"数据结构"课程脱胎于“离散数学结构”,它涉及各种离散结构(如向量、集合、树、图、代数方程、多项式等)在计算机上如何存储和处理。
现在网络世界,是我们的现实世界通过计算机构建出来的,而我们的信息需要转换成为数据才能在计算机中进行处理;
数据(data)是信息的载体,是描述客观事物的数、字符,以及所有能输入到计算机中并被计算机程序识别和处理的符号的集合。
数据大致可以分成两类数值型数据和非数值性数据;
数值型数据:就是咱们从上学开始接触的数字(整数、浮点数、复数等),用它就是各种计算呗,比如说计算核弹轨道或者是买菜算账啥的,你能想到的数学上运算相关的,只要是他能干的都干(这是个废话);
非数值数据:包括字符和字符串以及文字、图像、语音等数据;比如说李诞的脱口秀,帅气的脸庞、高笑的段子和魔性的笑声。
数据结构:
数据结构由某一数据元素的集合和该集合中数据元素之间的关系组成;
Data_Structure = {D,R}
D是某一数据元素的集合;
R是该集合中所有数据元素之间的关系的有限集合;
根据数据元素之间的关系的不同,我们可以将数据结构分成两大类:
线性结构和非线性结构;
线性结构:
最常用的数据结构,其特点是数据元素之间存在一对一的线性关系;
两种不同的存储结构:顺序存储结构和链式存储结构;
顺序存储的线性表称为顺序表,顺序表存储元素是连续的;
链式存储的线性表称为链表,链表中的存储元素不一定是连续的,元素节点中存储数据元素以及相邻元素的地址信息;
顺序表的存储空间是连续的,就是各个表项的逻辑顺序和其存放的物理顺序是一致的;就和你考试一样,你坐的顺序和你考号的顺序他是一致的,不一致那绝对会出事的;
单链表的一个存储节点包括两个部分:数据域(data)他是存数据的,指针域(link)他是一个指针,指向下一个节点的开始存储地址;
线性结构常见的有一维数组、队列、栈、链表;
非线性结构:树、图、多维数组、广义表;
算法是指用来操作数据、解决程序问题的一组方法;
对于同一个问题,使用不同的算法,或许最后我们得到的最终结果是一样的,但是在这个过程中消耗的资源和时间会有很大的区别;
就好比我们从青岛去北京旅游,你可以坐飞机,这样会比较节省时间,但是相对的花费可能会比较高;有人可能会开车,这样虽然比较节省成本,但是花费的时间会比较长,对于算法也是一样的,我们如何去衡量不同算法之间的优劣呐?
我们衡量算发的好坏主要是从时间和空间两个维度去考量;
时间维度:执行当前算法消耗的时间,用时间复杂度衡量;
空间维度:执行当前算法需要占用多少内存,用空间复杂度衡量;
当然两个指标都好更好,但是有时候鱼和熊掌不可兼得,就像左手右手不能同时做兄弟一样。
可能你会想,比较时间还不容易,把程序都运行一边不就知道时间了,这个方法当然是可取的,但是很容易受你的硬件的影响还有你的数据规模。
这里我们来一个统一的衡量办法–大O符号表示法;
T(n) = O(f(n))
常见的时间复杂度的量级:
当然时间复杂度越大,执行的效率就越低;
public class changshu {
public static void main(String[] args) {
int i = 1;
i++;
int j = 3;
++j;
System.out.println(i+j);
}
}
这段代码执行的时间不会随着某个变量的增长而增长,无论这类代码有多长,它的时间复杂度都是O(1);
如果你每天只是写一些CRUD的代码,那么你的复杂度也不会很高;不断学习,不断提高;
public class duishu {
public static void main(String[] args) {
int i = 1;
int m = 0;
while (i<1024){
i = i * 2;
m++;
}
System.out.println("m = " + m);
}
}
在while循环里面,i每次都是在接近1024的,假设1024用n表示,循环m次,跳出循环,m是约等于 log2^n,转换一下就是当循环 log2^n 次以后,这个代码就结束了。因此这个代码的时间复杂度为:O(logN);
public class xianxing
{
public static void main(String[] args) {
for (int i = 0; i < n; i++) {
System.out.println("重要的话说n遍");
}
}
}
for循环中的n会影响代码 执行的次数,一个for循环,这类代码都可以用O(n)来表示它的时间复杂度。
for(m=1; m<n; m++)
{
i = 1;
while(i<n)
{
i = i * 2;
}
}
这个很容易理解吧,就是线性阶和对数阶的组合;
for(x=1; i<=m; x++)
{
for(i=1; i<=n; i++)
{
j = i;
j++;
}
}
for循环套for循环
O(n³)相当于三层n循环;
O(n³)相当于k层n循环;
空间复杂度是对一个算法在运行过程中临时占用存储空间大小的一个量度,同样反映的是一个趋势。
S(n)=O(f(n))
常见的有 O(1)、O(n)、O(n²)
0(1):
public class changshu {
public static void main(String[] args) {
int i = 1;
i++;
int j = 3;
++j;
System.out.println(i+j);
}
}
不论i j怎么变,算法需要的空间都是一个固定的常数,空间复杂度O(1);
对单一变量;
0(n) :
int sum(int x){
int m;
int arr[x];
......
}
int类型占用4个字节,这个程序需要的空间 4+4+4n,S(n)=O(n);
一维数组;
0(n^2):
int add(int n){
int a;
int arr1[n];
int arr2[n][n];
}
这段代码占用的空间4+4+4n+4n^2,
S(n)= O(n^2)
二维数组;
递归函数的空间复杂度
递归函数不断的调用自己,函数的参数和局部变量占用的内存空间在递去的过程中会持续增长,在归来的时候逐层释放。
当递归的深度达到一定的数量级,可能会造成栈内存空间不足的情况;