数据结构概述
定义
算法
1. 时间复杂度 大概程序执行的次数,而非执行的时间
2. 空间复杂度 所占内存
3. 难易程度
4. 健壮性
计算算法时间复杂度 推倒大o阶方法
- 用常数1取代运行时间中的所有加法常数
- 在修改后的运行次数函数中 只保留最高阶项
- 如果最高阶项存在且不是1 则去除与这个项相乘的常数
常数阶
线性阶 (单循环)
平方阶(嵌套循环)
对数阶
- 数据类型
- 原子类型 整形 浮点型 字符型
- 结构类型 整形数组
预备知识
指针
定义
地址 内存单元的编号
指针 指针变量是存放内存单元地址的变量
分类
1: 基本类型指针
int i=10;
int *p = &i; // 等价于 int *p; p=&i;
详解这两部操作:
1) p存放了i的地址.所以我们说p指向了i
2) p和i是完全不同的两个变量,修改其中的任意一个变量的值不相互影响
3) p指向i *p就是i变量本身
指针变量也是变量 他存放的不是内存单元的内容, 只能存放内存单元的地址
普通变量前不能加*
常量和表达是前不能加&
如何通过被调函数修改主调函数中普通变量的值
1) 实参为相关变量的地址
2) 形参为以该变量的类型为类型的指针变量
3) 在被调函数中通过 *形参变量名 的方式就可以修改主函数相关变量的值
1). 修改 i 中的内容
# include <stdio.h>
void f(int *p) //不是定义了一个名字叫*p的形参,而是定义了一个形参,该形参的名字叫做p,他的类型是int*
{
*p = 100;
}
int main(void)
{
int i = 9;
f(&i);
printf("i = %d\n", i);
return 0;
}
2). 修改指针 p 的地址
# include <stdio.h>
void f(int **q);
int main(void)
{
int i = 9;
int *p = &i;//int *p; p = &i;
printf("%p\n", p);
f(&p);
printf("%p\n", p);
return 0;
}
void f(int **q)
{
*q = (int *)0xffffffff;
}
2: 指针和数组的关系
指针和一维数组
数组名
一维数组名是个指针常量,
他存放的是一维数组第一个元素的地址
他的值不能被改变
一维数组名指向的是数组的第一个元素
下标和指针的关系
a[i] << == >> *(a+i)
假设指针变量的名字为p
则p+i的值是p+i*(p所指向的变量所占的字节数)
指针变量的运算
指针变量不能相加,不能相乘,不能相除
如果两指针变量属于同一数组,则可以相减
指针变量可以加减一整数,前提是最终结果不能超过指针的长度
# include <stdio.h>
void show_array(int *p, int len)
{
//p[0] = -1; //p[0] == *p p[2] == *(a+2) == a[2]
//p[i] 就是主函数的a[i]
int i = 0;
for (i = 0; i < len; i++)
printf("%d\n", p[i]);
}
int main(void)
{
int a[5] = { 1, 2, 3, 4, 5 };
show_array(a, 5);
//printf("%d\n", a[0]);
return 0;
}
如何通过被调函数修改主调函数中一维数组的内容
两个参数
存放数组首元素的指针变量
存放数组元素长度的整形变量
结构体
如何使用(两种使用)
1. 第一种定义使用
# include <stdio.h>
struct Student
{
int sid;
char name[200];
int age;
};//分号不能省略
int main(void)
{
struct Student st = { 1000, "zhangsan", 20 };
printf("%d %s %d\n", st.sid, st.name, st.age);
st.sid = 99;
//st.name = "lisi"; //error
strcpy(st.name, "lisi");
st.age = 22;
printf("%d %s %d\n", st.sid, st.name, st.age);
return 0;
}
2. 第二种定义使用
# include <stdio.h>
struct Student
{
int sid;
char name[200];
int age;
};//分号不能省略
int main(void)
{
struct Student st = { 1000, "zhangsan", 20 };
printf("%d %s %d\n", st.sid, st.name, st.age);
struct Student *pst = &st; // struct Student *pst; pst = &st;
(*pst).sid = 99;
strcpy(pst->name, "lisi");
pst->age = 22;
printf("%d %s %d\n", st.sid, st.name, st.age);
return 0;
}
3. 函数传参
# include <stdio.h>
struct Student
{
int sid;
char name[200];
int age;
};//分号不能省略
void f(struct Student *pst);
void g(struct Student st);
void g2(struct Student *pst);
int main(void)
{
struct Student st;
f(&st);
g(st);
g2(&st);
return 0;
}
void f(struct Student *pst)
{
(*pst).sid = 99;
strcpy(pst->name, "zhangsan");
pst->age = 22;
}
void g(struct Student st)//耗时间 耗内存 不推荐
{
printf("%d %s %d\n", st.sid, st.name, st.age);
}
void g2(struct Student *pst)
{
printf("%d %s %d\n", (*pst).sid, (*pst).name, (*pst).age);
}
注意事项
结构体变量不能加减乘除, 但可以相互赋值
普通结构体变量和结构体指针变量作为函数传参的问题
动态内存的分配和释放
# include <stdio.h>
# include <malloc.h>
int main(void)
{
int a[5] = { 4, 10, 2, 8, 6 };
int len;
printf("请输入你需要分配数组的长度:len= ");
scanf_s("%d", &len);
int *pArr = (int*)malloc(sizeof(int)*len);
// 将分配返回的的第一个地址强制转换
//*pArr = 4; //类似于 a[0]=4;
//pArr[1] = 10;//类似于 a[1] = 10;
for(int i=0; i<len; i++)
{
scanf_s("%d", &pArr[i]);
};
for (int i=0; i < len; i++)
{
printf("%d\n", *(pArr + i));
};
free(pArr);//把pArr所代表的动态分配的20个字节的内存释放
return 0;
}
跨函数分配内存
调用函数fun 使main函数中的指针变量p指向一个合法的整型单元
main()
{
int *p;
fun1(&p);// p指向分配出的一块合法内存
fun2(&p);//p在fun2函数运行时分配出一块合法内存,func2运行结束后内存被释放
...
}
int fun1(int **q);
{
int *q = (int *)malloc(4);
}
int fun2(int **q);
{
int s;
*q = &s;
}
实例
# include <stdio.h>
# include <malloc.h>
struct Student
{
int sid;
int age;
};
struct Student *CreateStudent(void); //定义一个函数 返回值为结构体的地址
void ShowStudent(struct Student *pst);//传入一个结构体的地址 显示
int main(void)
{
struct Student *ps;
ps = CreateStudent();
ShowStudent(ps);
}
void ShowStudent(struct Student *pst)
{
printf("%d %d\n", pst->sid, pst->age);
}
struct Student *CreateStudent(void)
{
struct Student *p = (struct Student*)malloc(sizeof(struct Student));//分配一块内存Student结构体大小的内存
p->sid = 20;
p->age = 44;
return p;
}