一、数据结构概述:
1、定义:
如何把现实中大而复杂的问题以特定的数据类型和特定的存储结构保存到主存储器(内存)中,以及在此基础上为实现某个功能(比如查找某个元素,删除某个元素,对所有元素进行排序)而执行的相应的操作,这个相应的操作也叫做算法。
数据结构 = 个体 + 个体的关系
算法 = 对存储数据的操作
算法
解题的方法和步骤
衡量算法的标准:
·时间复杂度
大概程序执行的次数,而非执行的时间。最关键的运行次数最多的哪一个步骤 执行的次数
·空间复杂度
算法执行过程中大概所占用的最大内存
·难易程度
·健壮性
2、数据结构的地位
数据结构是软件中最核心的课程
程序 = 数据的存储 + 数据的操作 + 可以被计算机执行的语言。
3、预备知识
指针:
指针的重要性 指针式C语言的灵魂。
定义:
地址:内存单元的编号。从零开始的非负整数
指针:指针就是地址地址就是指针
指针变量: 存放指针的变量
指针的本质:是一个操作受限的非负整数
分类:
1、基本类型的指针
例1:
#include <stdio.h>
int main(void){
int *p;//这里定义了一个指针,p 是一个指针变量 int *p;表示 p只能存储int 类型变量的地址。
int i = 10;
int j;
p= &i;//int *p = &i;
//指针变量也是变量 只不过存放的是地址 指针变量时可以被改写的。
j=*p;//等价于 j= i;
printf("i= %d,j=%d,*p = %d \n",i,j,*p);
return 0;
}
例子2:
如何通过被调函数修改主调函数中的普通变量的值
· 实参为相关变量的地址
· 形参为以 该普通变量的类型为类型的指针变量
· 在被调函数中通过 *形参变量名 的方式就可以修改主调函数的变量值
#include<stdio.h>
void f(int * p)
//不是定义了 一个名字叫做*p的形参,而是定义了一个形参,该形参的名字叫做p,
//他的类型是 int *;类似于java 中数组的定义int[] x;
{
*p = 100;//这里 *p 就相当于 i;
}
int main(void)
{
int i = 9;
f(&i);
printf("i = %d\n",i);
return 0;
}
2、指针和数组的关系
指针和一维数组
数组名
一维数组名是个指针常量。
它存放的是一维数组第一个元素的地址。
它的值不能被改变
一维数组名指向的是数组的第一个元素。
下标和指针的关系
a[i] <====> *(a+i);
*a + 3 == a[0]+3;
假设指针变量的名字是 p;
则p+i 的值是 p+i*(p所指的变量所占的字节数);
指针变量的运算
指针变量不能相加,不能相乘,不能相除。
如果两个指针变量属于同一个数组,则可以相减
指针变量可以加减一个整数,前提是最终结果不能超过指针
P+i 的值是 p+i*(p所指的变量所占的字节数);
P-i 的值是 p-i*(p所指的变量所占的字节数);
P++ 《==》 p+1
P-- <==> p-1;
例1:如何通过被调函数修改主调函数中一维数组的内容
两个参数
存放数组首元素的指针变量
存放数组元素长度的整形变量。
#include <stdio.h>
void show(int * p,int len)
{
int i = 0;
for(i = 0;i<len;i++)
{
printf("%d\n",p[i]);
}
//p[i] 就是主函数 a[i];
}
int main(void)
{
int a[5] = {1,2,3,4,5};
show(a,5);//a等价于 &a[0] &a[0] 本身就是int * p 类型。
//printf("%d\n",a[0])
return 0;
}
例2:指针变量永远只占4个字节。
#include<stdio.h>
int main(void){
double * p;
double x = 66.6;
p = &x;//x 占8个字节 1个字节是8位,一个字节一个地址。
// 但是 p代表的是 首字节的地址 也就代表了 x 的地址
double arr[3] = {1.1, 2.2, 3.3};
double * q;
q = &arr[0];
printf("%p\n",q);//%p 就是以十六进制输出
q = &arr[1];
printf("%p\n",q);
return 0;
}
结构体:
1、为什么会出现结构体
为了表示一些复杂的数据,而普通的基本类型变量无法满足要求。
a) 类似于java 中类的描述只不过结构体中没有函数。
Class Student{
int id;
String name;
int age;
Void set(){ ... }
}
struct Student{
int id;
String name;
int age;
}
2、什么叫做结构体
结构体是用户根据实际需要自己定义的复合数据类型。
3、如何使用结构体
例1:类似于Java中的调用
# include<stdio.h>
struct Student
{
int sid;
char name[200];
int age;
};
//分号不能省略
//结构体不是变量 结构体是一种新的数据类型
int main(void)
{
struct Student st = {1000,"zhangsan",20};
//用 前面的数据类型 定义一个st 变量。
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 = {100,"shangsan",20};
// st.age=99;第一种方式
struct Student * pst;
//第二种方式 是最常用的 数据结构中就是用的这种方式
pst = &st;
pst->sid = 99; //pst -> sid 等价于 (*pst).sid. 而 (*pst).sid 等价于 st.sid;
}
pst -> sid
pst 所指向的结构体变量中的sid这个成员。
4、注意事项
结构体变量不能加减乘除但可以相互赋值。
普通结构体变量和结构体指针变量 作为函数传参的问题。
在传递参数时最好不要传递变量,最好传递指针因为指针变量 永远只占4个字节,如果你传递了一个结构体变量,那么就可能需要占用几百上千个字节的内存 ,而你传递一个指针变量呢只需要四个字节就可以了。
例:
#include<stdio.h>
#include<string.h>
struct Student{
int sid;
char name[200];
int age;
};
void f(struct Student *pst)
{
(*pst).sid = 99;
strcpy(pst ->name,"zhangsan");
pst ->age = 22;
}
void p(struct Studet *pst)
{
printf("%d %s %d",pst.sid,pst.name,pst.age);
}
int main(void)
{
struct Student st ;
//系统已经为st 分配了内存 但是还没有赋值
//使用一个函数来改变 st的值
f(&st);
p();
return 0;
}
动态内存的分配和释放:
假设动态构造一个int型的一位数组
int len;
int * pArr = (int *)malloc (sizeof(int) *len);
①本语句分配了两块内存,一块内存是动态分配的,总共len个字节;另一块是静态分配的,是pArr变量本身所占的内存,总共4个字节(指针变量只占4个字节)。
②malloc只有一个int型的形参,表示要求系统分配的字节数
③malloc函数的功能是请求系统分配len个字节的内存空间,如果分配成功,则返回第一个字节的地址,如果分配不成功,则返回NULL
④malloc函数能且只能返回第一个字节的地址,所以我们需要把这个无任何实际意义的第一个字节的地址(俗称干地址)转化为一个有实际意义的地址,因此,malloc函数前面必须加强制类型转换(数据类型 *)(传入的是什么样的数据类型就强制转换什么数据类型),表示把这个无实际意义的第一个字节的地址转化为相应类型的地址。
⑤free(* pArr)
表示把pArr所指向的内存给释放掉
pArr本身的内存是静态的,不能有程序员手动释放,只能在pArr变量所在的函数运行终止时有系统自动释放
⑥跨函数使用内存
静态内存不可以跨函数使用:
静态内存在函数执行期间可以被其它函数使用
静态内存在函数执行完毕之后就不能在被其它函数使用
动态内存可以跨函数使用
动态内存在函数执行完毕之后仍然可以被其它函数使用
例1:动态的构造一个int类型的数组。
#include<stdio.h>
#include<stdio.h>
int main(void)
{
int a[5] = {4,5,6,7,8};
int len;
printf("请输入你需要分配的数组长度:len = ");
scanf("%d",&len);
int *pArr = (int *)malloc(sizeof(int)*len);
//我们可以把pArr当作一个普通数组来使用。
int i;
for( i = 0;i<len ;i++)
{
scanf("%d",&pArr[i]);
}
for( i = 0;i<len ;i++)
{
scanf("%d\n",*(pArr+i));
}
free(pArr);//把pArr 所代表的动态分配的20个字节的内存释放了。
return 0;
}
例2:
#include<stdio.h>
struct Student
{
int sid;
int age;
}
struct Student * CreateStudent(void)
{
struct Student * p = (struct Student *) malloc(sizeof(struct Student));
// p 所保存的 动态分配的 结构体数据类型的 变量的地址。
p -> sid = 99;
p -> age =88;
return p;
}
void ShowStudent(struct Student *pst)
{
printf("%d %d\n",pst -> sid,pst->age);
}
int main(void)
{
struct Student * ps;
ps = CraeteStudent();
ShowStudent(ps);
return 0;
}