数据结构与算法[基础]
程序 = 数据结构 + 算法。
数据结构就是指一组数据的存储结构。算法就是操作数据的一组方法。
数据结构我们常见的如: 数组、栈、队列、哈希表、二叉树、图等等,而算法如: 排序算法、哈希算法、最短路径算法、字符串匹配算法等等。数据结构是为算法服务的,算法要作用在特定的数据结构之上。 因此,我们无法孤立数据结构来讲算法,也无法孤立算法来讲数据结构。
大纲:
数据结构核心名词解释
逻辑结构与物理结构区别
算法合计要求
算法效率衡量方法
算法时间复杂度
算法空间复杂度计算
一、数据结构核心名词解释
1、数据结构基本术语
数据
程序的操作对象,用于描述客观事物。
特点:1、具有可输入到计算机 2、可以被计算机处理
数据对象
性质相同的数据元素的集合(类似于数组)
数据元素
组成数据对象的基本单位
数据项
一个数据元素由若干数据项组成
也就是说多个数据项组成一个数据元素,多个数据元素的集合就是数据对象
如下就是一个数据元素:
//声明一个结构体类型
struct Teacher{ //一种数据结构
char *name; //数据项--名字
char *title; //数据项--职称
int age; //数据项--年龄
};
int main(int argc, const char * argv[]) {
struct Teacher t1; //数据元素;
struct Teacher tArray[10]; //数据对象;
t1.age = 18; //数据项
t1.name = "LJL"; //数据项
t1.title = "平民"; //数据项
return 0;
}
二、数据结构
表述的是数据与数据之间的逻辑关系
1、逻辑结构
(1)、集合结构
无序(没有先后顺序)。例如:字典、集合(NSSet)
(2)、线性结构
数据是一对一的结构,所有一对一的都是线性结构。存在唯一的第一个元素和唯一的最后一个元素
例如:线性表、数组、栈、队列等
队列 和 栈是特殊的线性结构。特殊在读取方式,队列FIFO先进先出,栈先进后出
堆是链表结构
(3)、树性结构
一对多的关系,凡是一对多的数据关系都是树形结构。
(4)、图形结构
多对多的的关系
2、物理结构
数据最终是要存到内存中的,内存的存储方式只有两种
1、顺序存储结构
需要提前开辟一段联系的内存空间
插入删除不方便,查找不方便
2、链式存储结构
不需要提前开辟连续空间
插入删除方便,查找不方便
数据结构不是单纯的用一种存储结构完成存储的
2、数据结构与算法的关系
(1)、算法
定义:
算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有序列,并且每个指令表示一个或多个操作。
算法必须要有输入、输出和要求
算法特性:
输入输出 输入需要计算内容,输出结果
有穷性 有限的执行次数和执行时间
确定性 每一步的执行都是唯一的
可行性 每次操作切实可行
算法要求:
正确性 执行的结果必须准确
可读性 执行过程的可读性性要强便于查阅。注释、易懂
健壮性 对异常情况的处理。容错和抛出错误原因
时间效率高和储存量低 执行的时间消耗要少,占用的空间资源要少。
算法有时间复杂度和空间复杂度
1、为什么需要复杂度分析?
因为传统的统计法受代码执行平台(宿主平台)和数据规模所限,所以需要一个无关的宿主平台,粗略的来估算算法执行效率的方法。
2、什么是复杂度分析?
复杂度分析分为时间复杂度分析和空间复杂度分析,使用大O表示法
其中时间复杂度表示代码时间随数据规模增长的变化趋势,也称为渐进时间复杂度;
同理,空间复杂度表示算法存储空间随数据规模增长的变化趋势,也称为渐进空间复杂度。
3、怎么来做复杂度分析
首先,时间复杂度分析有三个实用的方法,分别是
只关心循环次数最多的一段代码
加法法则:总复杂度等于量级最大的那段代码的复杂度
乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积
时间复杂度的量级分为两类
多项类:O(1) O(log n) O(nlog n) O(m+n) O(m*n)
非多项式:O(2的n次方) O(n!)
而空间复杂度分析则简单一些,一般来说就是O(1)、O(n)、O(n的2次方)
时间复杂度:
包含
- 算法的输入时间
- 编译可执行代码
- 执行重复指令
常见时间复杂度
用大O表示法
- 用常数1 取代运行时间中所有常数 3->1 O(1);
- 在修改运行次数函数中,只保留最高阶项 n^3+2n^2+5 -> O(n^3)
- 如果在最高阶存在且不等于1,则去除这个项目相乘的常数 2n^3 -> O(n^3)
时间复杂度常用术语:(大多数都是如下7种)
指数阶(不考虑)O(2^N)或者O(n!) 除非是非常小的n,否者会造成噩梦般的时间消耗,这是一种不切实际的算法时间复杂度,一般不考虑。
时间复杂度的计算并不是让精确到每分每秒,所以通过大O表示法来计算大概的时间
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.线性阶时间复杂度
//(n+1)+n; 执行n次 O(n)
void add2(int x,int n){
for (int i = 0; i < n; i++) { //执行n+1 次
x = x+1; //执行n次
}
}
//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(log n)。大O表示法会把2省略掉
O(log n) 其实也也就是下图的 K。由于输入的问题具体的log2n 见下图
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 add3(int x,int n){
for (int i = 0; i< n; i++) {
for (int j = 0; j < n ; j++) {
x=x+1;
}
}
}
//n+(n-1)+(n-2)+...+1 = n(n-1)/2 = n^2/2 + n/2 = O(n^2)
//sn = n(a1+an)/2
void testSum4(int n){
int sum = 0;
for(int i = 0; i < n;i++)
for (int j = i; j < n; j++) {
sum += j;
}
printf("textSum4:%d",sum);
}
//1+(n+1)+n(n+1)+n^2+n^2 = 2+3n^2+2n -> O(n^2)
void testSum5(int n){
int i,j,x=0,sum = 0; //执行1次
for (i = 1; i <= n; i++) { //执行n+1次
for (j = 1; j <= n; j++) { //执行n(n+1)
x++; //执行n*n次
sum = sum + x; //执行n*n次
}
}
printf("testSum5:%d\n",sum);
}
5.立方阶
//1+n+n^2+n^3+n^3 = 1+n+n^2+2n^3 -> O(n^3)
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次
}
}
}
}
空间复杂度
算法的空间复杂度通过计算算法所需的存储空间实现,算法空间复杂度的计算公式记做:
S(n)=n(f(n))
其中n为问题的规模,f(n)为语句关于n所占存储空间的函数。
程序空间计算因素:
- 寄存本身的指令
- 常数
- 变量
- 输入
- 对数据进行操作的辅助空间
在考量算法的空间复杂度,主要考虑算法执行时所需要的辅助空间
空间复杂度计算:
问题:数组逆序,将一维数组a中的n个数逆序存放在原数组中
int main(int argc, const char * argv[]) {
printf("Hello, World!\n");
int n = 5; //这是临时变量不是辅助空间
int a[10] = {1,2,3,4,5,6,7,8,9,10};
//1、算法实现(1)
//使用了一个赋值空间,常数阶 空间复杂度是O(1) O(1)
int temp; //这是辅助空间。这里就算多写几个 temp 一样是 O(1),这是常数阶
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 个数组,所以就是 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]);
}
return 0;
}
算空间复杂度算的是辅助空间
算法好坏的衡量
衡量算法的时候衡量的是最坏的情况,如果最坏的情况一样就衡量平均情况
总结:
主要是一些基本该你,总结是针对本人的总结。如果都比较熟悉可以忽略下面的内容。
1、数据结构
数据
程序的操作对象,用于描述客观事物。
特点:1、具有可输入到计算机 2、可以被计算机处理
数据对象
性质相同给的数据元素的集合(类似于数组)
数据元素
组成数据对象的基本单位
数据项
一个数据元素由若干数据项组成
2、大O表示法
- 用常数1 取代运行时间中所有常数 3->1 O(1);
- 在修改运行次数函数中,只保留最高阶项 n^3+2n^2+5 -> O(n^3)
- 如果在最高阶存在且不等于1,则去除这个项目相乘的常数 2n^3 -> O(n^3)
3、