数据结构与算法
数据结构: 描述 从现实中 抽象出 数据模型 即 数据关系的抽象
逻辑结构: 数据之间的 逻辑关系
表: 线性结构 队列,栈, 数据之间是线性关系 一一对应
树: 非线性结构 1对多
图: 非线性结构 多对多
存储结构: 对数据 以及 数据关系的 存储
连续存储: 将所有数据放到一起 数组
离散存储: 数据可能分布在内存的 不同位置
对数的操作(算法):
不是简单的数值操作 而是一些 逻辑操作
增: 增加数据
删: 删除数据
改: 修改数据
查: 查找或遍历数据
算法就是对 某个动作的 操作流程 或步骤
算法特性
1. 可行性 可以实现 每个计算步骤能够在有限的时间内完成
2. 确定性 没有歧义 步骤唯一且确定
3. 有穷性 有明确的目标且可到达 步骤是有限的
4. 有一个或多个输入
5. 有一个或多个输出
求两正整数m、n的最大公因子的算法如下:
① 输入m,n;
② m/n(整除),余数→r (0≤r≤n);
③ 若r=0,则当前n=结果,输出n,算法停止;否则,转④;
④ n→m,r->n; 转②。如初始输入 m=10,n=4,则m,n,r 在算法中的变化如下:
m n r
10 4 2
4 2 0(停止)
即10和4 的最大公因子为2。
计算最大公因数
#include <stdio.h>
int main()
{
int i,m,n;
printf("请输入两个数");
scanf("%d%d",&m,&n);
p:
i=m%n;
if(i==0)
{
printf("输出最大公因数为:%d",n);
return 0;
}
else
{
m=n,n=i;
goto p;
}
return 0;
}
时间复杂度及空间复杂度
算法优劣评判标准:
时间复杂度: 语句执行的频度 与算法程序执行的时间 没有必然关系
执行的频度: 算法中 语句执行的次数 与 输入问题规模的 比值
度量一个算法的时间复杂度 通常使用 其 量级表示
例:求 1+2+3+...n = ???
算法1: 时间复杂度T 量级 O
for(i=1;i<=n;i++) sum+= i; n+1 O(n)
算法2:
sum = (1+n)*n/2 1 O(c) 常数量级
冒泡排序: 语句频度
for(i=1;i<n;i++) n
for(j=1; j<n-i-1; j++) (n)(n / 2)
if( a[j] > a[j-1] ) n(n/2)
{交换a[j] a[j-1]}n^2 + n 量级 O(n^2)
常见的 时间复杂度的 量级
最好的 O(1) 常量级 算法语句执行次数 与 输入问题规模 无关
较好
O(log n) 对数级
O(n) 线性级
一般 O(n*log n)
较差 O(n^2) 平方级
O(2^n) 次方级
O(n!) 阶乘级
空间复杂度: 占用内存的多少
交换两个数
杨辉三角:第i行j列的值
A(i,j) = i!/(j!*(i-j)!)
常见数据数据
1. 表结构 线性结构
数据逻辑组织为表形式,逻辑上,数据是连续排列的
每个节点(除头尾外),都有且仅有一个 前驱 和 一个后继
头节点 只有后继 没有前驱
尾节点 只有前驱 没有后继
节点: 数据逻辑的最小单元
C中 数组 就是 表结构
存储结构:
顺序存储: 数组存储
顺序表 :存储上连续存储,逻辑上是表结构
离散存储: 链式存储
链表: 存储上使用离散存储, 逻辑上是表结构顺序表:
C语言程序模型:
表结构定义:typedef函数的使用
在C语言中,
typedef
关键字用来为已有的数据类型起一个新的名称。typedef
的作用是定义类型别名,可以将复杂或冗长的类型名简化为一个更易读的别名,使代码更加清晰和易于理解。typedef
的语法格式为:typedef <原类型> <新类型别名>;
使用
typedef
的好处有以下几点:
- 增加代码的可读性:通过给类型起一个有意义的别名,可以增加代码的可读性,使代码更易于理解和维护。
- 缩短类型名:使用
typedef
可以将较长的数据类型名缩短为一个简洁的别名,减少代码中的冗余和重复。- 更好的可移植性:使用
typedef
可以为不同平台或编译器提供更加通用的类型名,提高代码的可移植性。- 隐藏数据类型的具体实现:通过使用
typedef
,可以将数据类型的具体实现隐藏起来,提高代码的封装性和安全性。例如,可以使用
typedef
定义一个别名来代替int
类型:
typedef int Integer;
然后可以使用
Integer
作为int
的别名,如下所示:Integer num = 10;这样就可以使用Integer来代替int类型,使代码更加清晰易读。
//1 定义节点类型 假设为int
typedef int Data_t;//2 定义顺序表 结构
//动态长度
typedef struct list_t
{
int max_len; //存储顺序表的最大长度
int cnt; //存储当前表中节点个数
Data_t *data;//存储节点的数组动态分配长度
};
//固定长度
define MAXLEN 20
typedef struct list_t
{
int cnt; //存储当前表中节点个数
Data_t data[MAXLEN]; //静态分配数组长度
};struct list_t list;
顺序表的操作:
创建表: 动态长度表为例
int create_list(struct list_t *head ,int max_len);typedef int Data_t;//typedef 将int 与Data_t互换。以后见到的Data_t都相当于int。 //2 定义顺序表 定义一个表构造类型里面存放了顺序表最大长度、节点个数(内容)、数组动态分配长度等数组成员 struct list_t { int max_len; //存储顺序表的最大长度 int cnt; //存储当前表中节点个数 Data_t *data;//存储节点的数组动态分配长度 // data_t data[MAXLEN]; //静态分配数组长度 使用时是调用宏定义的值 }; /定义对 顺序表的操作 //1 创建表create_list (*head)创建一个指针指向这个顺序表 int create_list(struct list_t *head, int max_len) { //入参检查 if(head == NULL || max_len <= 0) //指针head为空或者长度小于等于o无意义 return -1; // 返回错误码 //填充 参数 head->max_len = max_len;//将定义的顺序表的max_len传给max-len head->cnt = 0;//cnt从0开始 //申请空间 head->data = (Data_t *)malloc(max_len * sizeof(Data_t)); //malloc申请空间使用 if(head->data == NULL) { printf("malloc err!\n"); return -2; } return 0; }
销毁表
int del_list(struct list_t *head);...
//2.删除表free用于释放由malloc申请的堆区内存使用 del_list int del_list(struct list_t *head) { if(head == NULL) return -1; if(head->data)//释放堆区内存 free(head->data); head->data = NULL; head->cnt = 0; head->max_len = 0; return 0; }
增: 增加节点 插入数据
int insert_list(struct list_t *head, int index, Data_t data);
//插入数据 int insert_list(struct list_t *head, int index, Data_t data) { if(head == NULL || head->data == NULL) return -1; //判断位置能否插入 if( index > head->cnt ) { printf("插入位置%d,不存在!\n", index); return -2; } //判断满否 if(head->cnt == head->max_len) { printf("顺序表已满!!\n"); return -3; } //移开位置 //找到最后一个元素下标 //int i = head->cnt - 1; for( int i = head->cnt -1 ; i >= index ;i--) { head->data[i+1] = head->data[i]; } //写入数据 head->data[index] = data; head->cnt ++;//更新元素个数 return 0; } int show_list(struct list_t *head) { if(head ==NULL || head->data == NULL) return -1; for(int i=0; i< head->cnt; i++) printf("%d ",head->data[i]); printf("\n"); return 0; }
作业 删: 删除节点
改: 修改节点值
查: 遍历 获取某个节点数据
//获取某个下标的元素值
int get_list_data(struct list_t *head, int index, Data_t *data)
{
if(head == NULL || head->data == NULL) return -1;
//下标不存在
if(index >= head->cnt)
{
printf("%d号元素不存在!\n",index);
return -2;
}
*data = head->data[index];
return 0;
}
//根据元素的值 得到下标 找第一个值
int get_list_index(struct list_t *head, int *index, Data_t data)
{
if(head == NULL || head->data == NULL) return -1;
for(int i=0; i<head->cnt; i++)
{//找到值相同的下标
if(head->data[i] == data)
{
*index = i;
return 0;
}
}
}
int main()
{
//使用表结构 创建一个表
struct list_t L;
//要求创建一个有25个元素空间的顺序表
int ret = create_list(&L, 25);
if(ret == 0)
{
printf("表L创建成功!!\n");
printf("data=%p\n",L.data);
}
insert_list(&L, 0, 10);
insert_list(&L, 0, 20);
insert_list(&L, 0, 30);
insert_list(&L, 0, 40);
show_list(&L);
//获得表中的一个元素
Data_t dt;
if ( get_list_data(&L, 3, &dt) == 0 )
{
printf("3号元素的值为%d\n",dt);
}
int index;
if( get_list_index(&L, &index, 20 ) == 0)
{
printf("元素值为20的下标是%d\n",index);
}
//删除表
del_list(&L);
return 0;
}
上述代码实现了一个简单的顺序表结构。首先定义了一个结构体 list_t 用来表示顺序表,其中包含了最大长度、当前元素个数以及存储节点数据的数组成员。然后定义了一系列用于操作顺序表的函数,包括创建表、删除表、插入数据和显示表内容等。在 main 函数中,首先通过 create_list 函数创建了一个有25个元素空间的顺序表,然后使用 insert_list 函数在表的头部插入了一些数据,最后调用 show_list 函数显示了表的内容。最后,通过调用 del_list 函数删除表并释放内存。