数据结构概述
定义
我们如何把现实中大量而复杂的问题以特定的数据类型和特定的存储结构保存到主存储器(内存)中,以及在
此基础上为实现某个特定功能(比如查找某个元素,删除某个元素,对所有元素进行排序)而执行的相应操作,
这个相应的操作也叫作算法。
数据结构 = 个体 + 个体的关系
算法 = 对存储数据的操作
算法
解题的方法和步骤
衡量算法的标准(研究算法前两者最重要)
1、时间复杂度
大概程序要执行的次数,而非执行的时间
2、空间复杂度
算法执行过程中大概所占用的最大内存
3、难易程度
4、健壮性
数据结构的地位
数据结构是软件中最核心的课程
程序 = 数据的存储 + 数据的操作 + 可以被计算机执行的语言
预备知识
指针
CPU通过地址线(32位,4GB),数据线,控制线来操作内存
指针即地址,地址即指针
指针变量是存放内存单元(编号)的变量
指针的本质是一个操作受限的非负整数
分类:
1.基本类型的指针
2.指针和数组的关系
变量并不一定连续分配,随机分配内存。
内存:
内存是多字节组成的线性一维存储空间。 内存的基本划分单位是字节。
每个字节含有8位,每一位存放1个0或1个1. 内存和编号是一一对应的。
软件在运行前需要向操作系统申请存储空间。在软件运行期间,该软件所占空间不再分配给其他软件。当软件运行完毕后,操作系统将回收该内存空间(操作系统并不清空该内存空间中遗留下来的数据)。
NOTE:
1)指针变量也是变量,普通变量前不能加*,常亮和表达式前不能加&。
2)局部变量只在本函数内部使用。
如何通过被调函数修改主调函数中普通变量的值。
1)实参为相关变量的地址;
2)形参为以该变量的类型为类型的指针变量;
3)在被调函数中通过 *形参变量名的形式的形式就可以修改主函数。
CASE 1
#include<stdio.h>
int main(void)
{
int *p; //p 是个变量名字, int* 表示该 p 变量只能存储 int 类型变量的地址
int i=10;
int j;
// j=*p;
// printf("%d\n",j); //error,p 未指定
// char ch='A';
// p=&ch; //error ,类型不一致
p=&i; //p 保存 i 的地址, p 指向 i;修改 p 的值不影响 i 的值,修改 i 的值不影响 p 的值;任何场合下, *p 和 i 可以互换。 *p 等价于 i。
//p=10; //error
j=*p;// 等价于 j=i;
printf("i=%d,j=%d,*p=%d\n",i,j,*p);
return 0;
}
CASE 2
#include<stdio.h>
void f(int * i)// 不是定义了一个名字叫做 *i 的形参,而是定义了一个形参, 该形参名字叫做 i,
它的类型是 int*
{
*i=100;
}
int main(void)
{
int i=9;
f(&i); // 局部变量只在本函数内部使用。
printf("i=%d\n",i);
}
指针变量的运算
指针变量不能相加,不能相乘,不能相除。
如果两指针变量属于同一数组,则可以相减。
指针变量可以加减一整数,前提是最终结果不能超过指针变量
p+i的值是p+i*(p所指向的变量所占的字节数)
p-i的值是p-i*(p所指向的变量所占的字节数)
p++<==>p+1 p--<==>P-1
CASE 1
double *p; double x=66.6; // 一个 double 占 8 个字节
p=&x;//x 占 8 个字节, 1 个字节是 8 位, 1 个字节一个地址, p 内只存放了一个地址,通常
是字节的首地址
double arr[3]={1.1,2.2,3.3};
double *q;
q=&arr[0];
printf( “ %p n” ,q); //%p 实际就是以十六进制输出
q=&arr[1];
q=printf( “ %p n” ,q); //p,q 相差 8
所有的指针变量只占4个子节用第一个字节的地址表示整个变量的地址
结构体
为什么会出现结构体:
为了表示一些复杂的数据,而普通的基本类型变量无法满足要求
什么叫做结构体:
结构体是用户根据实际需要自己定义的复合数据类型
如何使用结构体:
两种方式---
struct Stduent st=,1000,"zhangsan",20-;
struct Stduent* pst = &st;
1)通过结构体变量名来实现
st.sid
2)通过指向结构体变量的指针来实现(重点!!!)
pst->sid
pst所指向的结构体变量中的sid这个成员
CASE 1
#include<stdio.h>
#include <string.h>
struct Student
{
int sid;
char name[200];
int age;
}; // 分号不能省
int main(void)
{
struct Student st=,1000, ” zhagnsan ” ,20-;
printf( “ %d,%s%d n, ” ,st.sid,st.name, st.age);
printf( “ %d,%s%d n, ” ,st); //error
st.sid=99; // 第一种
//st.name= ” lisi ”;//error
strcpy(st.name, ” lisi ” );
st.age=22;
struct Student*pst;
pst=&st; // 第二种
pst->sid=99; //pst-> 等价于 (*pst).sid ,而(*pst).sid 等价于 st.sid,所以 pst->sid 等价于 st.sid
Return 0;
}
注意事项:
结构体变量不能加减乘除,但可以相互赋值
普通结构体变量和结构体指针变量作为函数传参的问题
CASE 2
#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; // 已经为 st 分配好了内存
f(&st);
//g(st);
g2(&st);
// printf( “ %d %s %d n” ,st.sid,st.name,st.age); // 输出方法一
return 0;
}
void g(struct Student st) // 整体变量赋值 // 输出方法二,速度慢,耗空间,耗内存,不推荐
{
printf( “ %d %s %d n” ,st.sid,st.name,st.age);
}
void g2(struct Student *pst)
{
printf ( “ %d %s %dn” ,pst->sid,pst->name,pst->age);
}
void f(struct Student *pst)
{
(*pst).sid=99;
strcpy(pst- >name, ” zhagnsan ” );
pst->age=22;
}
动态内存的分配和释放
#icclude<stdio.h>
#include<malloc.h>
int main(void)
{
int a[5]={1,2,3,4,5}; // 静态数组
int len;
printf( “请输入你需要分配的数组长度: len= ” );
scanf( “ %d” ,&len);
int *pArr=(int *)malloc(sizeof(int)*len); //(int *) 为强制转换, 强制使 pArr 指向前四个字节。可以将 pArr 当做数组名来操作
//*pArr=4;// 类似于 a[0]=4;
// pArr[1]=10;// 类似于 a[1]=10;
// printf( “ %d %d n” ,*pArr,pArr*1+);
// 我们可以把 pArr 当做一个普通数组来使用
for (int i=0;i<len;++i)
scanf( “ %d” ,&pArr);
for (i=0;i<len;++i)
printf( “ %d n” ,*(pArr+i));
free(pArr); // 把 pArr 所代表的的动态分配的 20 个字节的内存释放
return 0;
}