一、数据结构
1、基本术语
在计算机科学中,数据结构(英语:data structure)是计算机中存储、组织数据的方式。
数据:是描述客观事物的符号,是计算机中可以操作的对象,是能被计算机识别,并输入给计算机处理的符号集合。数据不仅仅包括整型、实型等数值类型,还包括字符及声音、图像、视频等非数值类型。
数据元素:是组成数据的,且有一定意义的基本单位,在计算机中通常作为整体处理。比如组成社会的人、比如动物园里的老虎。
数据项: 一个数据元素可以由若干数据项组成.比如组成人这个数据元素的耳鼻喉和四肢。
数据对象:是性质相同的数据元素的集合,是数据的子集.性质相同,是指数据元素具有相同数量和类型的数项. 类似数组中的元素保持性质一致.

2、逻辑结构与物理结构
根据视角不同,我们将数据结构分为2种: 逻辑结构与物理结构;
2.1.逻辑结构
逻辑结构: 指的是数据对象中的数据元素之间的相互关系. 逻辑结构分为四种: 集合结构,线性结构,树形结构,图形结构.
2.1.1集合结构
集合结构: 集合结构中的数据元素除了同属于一个集合外,它们之间没有其他关系. 各个数据元素是"平等"的. 它们的共同属性是:"同属于一个集合".比如动物园中的动物们.

2.1.2线性结构
线性结构中的数据元素之间是一对一的关系.常用的线性结构有:线性表,栈,队列,双队列,数组,串。

2.1.3树型结构
树形数据结构可以表示数据表素之间一对多的关系.树型结构中的数据元素之间存在一种一对多的层次关系. 常见的树形结构: 二叉树,B树,哈夫曼树,红黑树等.

2.1.4图形结构
图形结构的数据元素是多对多的关系. 常见的图形结构: 邻近矩阵,邻接表.

2.2.物理结构
物理结构,别称"存储结构". 顾名思义,指的是数据的逻辑结构在计算机的存储形式.数据元素的存储结构形式有2种: 顺序存储和链式存储。
2.2.1顺序存储
顺序存储结构,是指把数据元素存放在地址连续的存储单元里,其数据间的逻辑关系和物理关系是一致的.比如当你创建一个数组时,计算机会在内存是开辟一片连续的内存空间,然后将数据以此存储进去.顺序存储在存储时较为方便.

2.2.2链式存储
链式存储结构,是把数据元素放在任意的存储单元里,这组存储单元可以是连续的,也可以是不连续的. 数据元素的存储关系并不能反映逻辑关系,因此需要用一个指针存放数据元素的地址,这样通过地址就可以找到相关关联数据元素的位置.显然链式存储更为灵活。

二、算法
算法就是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作.
1.算法的特性
算法必须具备几个基本特性: 输入,输出,有穷性,确定性和可行性;
1.1输入输出
输入输出,很好理解. 在解决问题时必须有已知条件,当然有些算法可能没有输入. 但是算法至少有一个或多个输出.否则没有输出,没有结果.你用这个算法干吗?
1.2有穷性
有穷性: 指的是算法在执行有限的步骤之后,自动结束而不会出现无限循环,且每一个步骤在可接受的时间内完成.
1.3确定性
确定性: 算法的每一个步骤都具有确定的含义,不能出现二义性; 算法在一定条件下,只有一条执行路径,相同的输入只能有唯一的输出结果.
1.4可行性
可行性: 算法的每一步都必须是可行的,换句话说,每一步都能通过执行有限次数完成.
2.算法效率的度量方法
习惯上将算法语句重复执行的次数作为算法的时间量度
3.算法时间复杂度
3.1 时间复杂度的大O表示法
- 用常数1取代运行时间中所有加法常数;
- 在修改后的运行次数函数中,只保留最高阶项;
- 如果在最高阶项存在且不是1,则去除与这个项相乘的常数;
3.2 常见的时间复杂度
3.2.1 常数阶
常数阶O(1) 无论代码执行了多少行,只要是没有循环等复杂结构,那这个代码的时间复杂度就都是O(1),如:
//1+1+1 = 3
void testSum1(int n){
int sum = 0; //执行1次
sum = (1+n)*n/2; //执行1次
printf("testSum2:%d\n",sum);//执行1次
}
3.2.2 线性阶
void add(int x,int n){
for (int i = 0; i < n; i++) {
x = x+1;
}
}
这段代码,for循环里面的代码会执行n遍,因此它消耗的时间是随着n的变化而变化的,因此这类代码都可以用O(n)来表示它的时间复杂度。
3.2.3 对数阶
int count = 1;
while(count < n){
count = count * 2;
}
count = count * 2 ; 每次执行这句代码,就会距离n更近一步; 那么根据 x = log2^n 也就是说当循环 log2^n 次以后,这个代码就结束了。因此这个代码的时间复杂度为:O(logn)
3.2.4 平方阶
//x=x+1; 执行n*n次
void add3(int x,int n){
for (int i = 0; i< n; i++) {
for (int j = 0; j < n ; j++) {
x=x+1;
}
}
}
这段代码为双重for循环,循环此时为n*n, 循环次数为O(n^2).
3.2.5 立方阶
即三重循环,O(n^3).
3.2.6 nlogn阶
O(nlog n).
3.2.7 指数阶
O(2^n).
常见时间复杂度比较
O(1) < O(log n) < O(n) < O(nlog n) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)
3.3 最坏情况与最好情况

3.4 算法空间复杂度
算法的空间复杂度通过计算算法所需的存储空间实现,算法空间复杂度的计算公式 记做: S(n) = n(f(n)),其中,n为问题的规模,f(n)为语句句关于n所占存储空间的函数
一般情况下, 一个程序在机器上执行时,除了需要寄存本身所用的指令,常数,变量和输入数据外,还需要一些对数据进行操作的辅助存储空间. 其中,对于输入数据所占的具体存储量取决于问题本身,与算法无关. 这样只需要分析该算法在实现时所需要的辅助空间就可以了.
如果算法执行时所需要的辅助空间相对于输入数据量是一个常数,则成这个算法原地工作,辅助空间为O(1).
问题: 数组逆序,将一维数组a中的n个数逆序存放在原数组中.
int n = 5;
int a[10] = {1,2,3,4,5,6,7,8,9,10};
//算法实现(1)
/*
算法(1),仅仅通过借助一个临时变量temp,与问题规模n大小无关,所以其空间复杂度为O(1);
*/
int temp;
for(int i = 0; i < n/2 ; i++){
temp = a[i];
a[i] = a[n-i-1];
a[n-i-1] = temp;
}
for(int i = 0;i < 10;i++)
{
printf("%d\n",a[i]);
}
//算法实现(2)
/*
算法(2),借助一个大小为n的辅助数组b,所以其空间复杂度为O(n).
*/
int b[10] = {0};
for(int i = 0; i < n;i++){
b[i] = a[n-i-1];
}
for(int i = 0; i < n; i++){
a[i] = b[i];
}
for(int i = 0;i < 10;i++)
{
printf("%d\n",a[i]);
}
算法(1),仅仅通过借助一个临时变量temp,与问题规模n大小无关,所以其空间复杂度为O(1);
算法(2),借助一个大小为n的辅助数组b,所以其空间复杂度为O(n).