1. 数据结构
数据:程序的操作对象,用于描述客观事物。数据不仅仅包括整型、实型等数值类型,还包括字符、声音、图像和视频等非数值类型。
数据具备两个特性:
- 可以输入到计算机中。
- 可以被计算机处理。
数据元素:组成数据的有一定意义的基本单位。比如在人类中,人就是数据元素,在畜类中,牛、羊、马、鸡等就是数据元素。
数据项:一个数据元素可以由若干个数据项组成。比如人的名字、性别、年龄等都是数据项。
数据项是数据不可分割的最小数据单位。
数据对象:是性质相同的数据元素的集合,是数据的子集。类似于数组。
数据结构:是相互之间存在一种或多种特定关系的数据元素的集合,也可以理解为数据对象中数据元素之间的关系。
//声明一个结构体类型
struct Person{ //一种数据结构
char *name; //数据项--名字
char *title; //数据项--职称
int age; //数据项--年龄
};
int main(int argc, const char * argv[]) {
struct Person person; //数据元素;
struct Person pArray[10]; //数据对象;
person.age = 18; //数据项
person.name = "Daneil"; //数据项
person.title = "developer"; //数据项
printf("姓名:%s\n",person.name);
printf("年龄:%d\n",person.age);
printf("职称:%s\n",person.title);
return 0;
}
2. 逻辑结构和物理结构
2.1 逻辑结构
逻辑结构:是指数据对象中数据元素之间的相互关系。逻辑结构分为以下四种:
1. 集合结构
集合结构:集合中的数据元素除了同属于一个集合外,他们之间就没有其他关系了。如下图:
2. 线性结构
线性结构:线性结构中的数据元素之间是一对一的关系。如下图:
3. 树形结构
树形结构:树形结构中的数据元素之间存在一对多的层次关系。如下图:
4. 图形结构
图形结构:图形结构的数据元素是多对多的关系。如下图:
2.2 物理结构
物理结构是指数据的逻辑结构在计算机中的存储形式,数据元素的存储形式分为两种:顺序存储和链式存储。
1. 顺序存储结构
顺序存储结构是把数据元素存放在地址连续的存储单元里,其数据元素间的逻辑关系和物理关系是一致的。如下图:
2. 链式存储结构
链式存储结构是把数据元素存储在任意存储单元里,这组存储单元可以是连续的,也可以是不连续的。如下图:
逻辑结构是面向问题的,而物理结构是面向计算机的,其基本的目标就是将数据及其逻辑关系存储到计算机内存中。
3. 算法
3.1 算法的定义
算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作。通俗的讲,算法就是描述解决问题的方法。
3.2 算法的特性
1. 输入输出:算法具有零个或多个输入,至少有一个或多个输出。
2. 有穷性:算法在执行完有限步骤之后,自动结束而不会出现无限循环,并且每个步骤在可接受的时间内完成。
3. 确定性:算法的每一步都具有确定的含义,不会出现二义性。
4. 可行性:算法的每一步都必须是可行的,每一步都能通过执行有限的次数完成。
3.3 算法的设计要求
1. 正确性:算法至少应该具有输入、输出和加工处理无歧义性、能正确反映问题的需求,能够得到问题的正确答案。
2. 可读性:算法设计的另一个目的是为了便于阅读、理解和交流。
3. 健壮性:当输入数据不合法时,算法也能够做出相应的处理,而不是产生异常或莫名其妙的结果。
4. 时间效率高和存储量低:设计算法应该满足时间效率高和低存储量的需求。
3.4 算法的时间复杂度
3.4.1 大O阶表示法
大O表示法规则:
- 用常数1取代运行时间中的所有常数。3->1 O(1)
- 在修改后的运行次数函数中,只保留最高阶项。n³+2n+5 -> O(n³)
- 如果最高阶存在且不等于1,则去除与这个项相乘的常数 2n³ -> n³
3.4.2 常用算法复杂度
1. 常数阶
/* 1. 常数阶时间复杂度计算 O(1) */
//1+1+1 = 3 O(1)
void testSum1(int n){
int sum = 0; //执行1次
sum = (1+n)*n/2; //执行1次
printf("testSum1:%d\n",sum);//执行1次
}
//1+1+1+1+1+1+1 = 7 O(1)
void testSum2(int n){
int sum = 0; //执行1次
sum = (1+n)*n/2; //执行1次
sum = (1+n)*n/2; //执行1次
sum = (1+n)*n/2; //执行1次
sum = (1+n)*n/2; //执行1次
sum = (1+n)*n/2; //执行1次
printf("testSum2:%d\n",sum);//执行1次
}
//x=x+1; 执行1次
void add(int x){
x = x+1;
}
2. 线性阶
//x=x+1; 执行n次 O(n)
void add(int x,int n){
for (int i = 0; i < n; i++) {
x = x+1;
}
}
//1+(n+1)+n+1 = 3+2n -> O(n)
void testSum3(int n){
int i,sum = 0; //执行1次
for (i = 1; i <= n; i++) { //执行n+1次
sum += i; //执行n次
}
printf("testSum3:%d\n",sum); //执行1次
}
3. 对数阶
/*2的x次方等于n x = log2n ->O(logn)*/
void testA(int n){
int count = 1; //执行1次
//n = 10
while (count < n) {
count = count * 2;
}
}
4. 平方阶
//x=x+1; 执行n*n次 ->O(n^2)
void add(int x,int n){
for (int i = 0; i< n; i++) {
for (int j = 0; j < n ; j++) {
x=x+1;
}
}
}
5. 立方阶
void testB(int n){
int sum = 1; //执行1次
for (int i = 0; i < n; i++) { //执行n次
for (int j = 0 ; j < n; j++) { //执行n*n次
for (int k = 0; k < n; k++) {//执行n*n*n次
sum = sum * 2; //执行n*n*n次
}
}
}
}
常用的时间复杂度所耗费的时间从小到大依次是:
3.5 最坏情况和平均情况
最坏情况运行时间是一种保证,那就是运行时间将不会再坏了,通常,除非特别指定,我们提到的运行时间都是最坏情况的运行时间。
平均运行时间是所有情况中最有意义的,因为它是期望的运行时间。
3.6 算法的空间复杂度
算法的空间复杂度通过计算算法所需的存储空间实现,算法空间复杂度的计算公式记做: S(n) = n(f(n)),其中n为问题的规模,f(n)为语句关于n所占存储空间的函数。
程序空间计算因素:
- 寄存本身的指令
- 常数
- 变量
- 输入
- 对数据进行操作的辅助空间
在考量算法的空间复杂度,主要考虑算法执行时所需要的辅助空间。
// 问题: 数组逆序,将一维数组a中的n个数逆序存放在原数组中.`
int main(int argc, const char * argv[]) {
int n = 10;
int a[10] = {1,2,3,4,5,6,7,8,9,10};
//算法实现(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;
}
//算法实现(2)
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];
}
return 0;
}
很显然,算法实现(2)的空间复杂度大于算法实现(1)的空间复杂度。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。