数据结构——第一章:预备知识(衡量算法的标注、指针、结构体、动态内存)

216 阅读8分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路

一、整体架构

使用教材:严蔚敏,吴伟民编写的《数据结构》 但是书中的算法都是伪算法(不是程序),都是解题的思路 具体的程序由高一凡主编的书里面有。 黄国瑜写的数据结构也可以。

模块一: 线性结构

  • 连续存储(数组) 优点:存取速度很快,可以直接找到某一个元素 缺点: (1)插入删除元素比较慢(后面的元素都得移动) (2)需要大片的内存 (3)事先知道数组的长度(防止内存污染)

  • 离散存储(链表) 优点: (1)插入删除元素比较方便(只需要切换指针域即可) (2)并不需要大片的内存 缺点:存取速度比较慢

  • 线性结构的两种常用应用之一 (

  • 线性结构的两种常用应用之一 (队列

  • 专题:递归 (1)1+2+3+4+5+... 100 的和 (2)求阶乘 (3)汉诺塔 (4)走迷宫

模块二:非线性结构

模块三:查找和排序

  • 折半查找
  • 排序:冒泡、插入、选择、快速、归并

1、预备知识

(1)数据结构概述

定义: 1、我们如何把现实当中,大量而复杂的问题以,特定的数据类型特定的存储结构,保存到内存当中。

2、在此基础上,为实现某个功能(比如查找某个元素、删除某个元素、对所有元素进行排序),而执行的相应操作,这个相应的操作叫做算法

举例:如何保存一个班级学生的信息?

(1)少数较少的时候,使用数组就可以。(需要内存是连续的) (2)但是保存1000 个学生信息,就没有这么大的连续空间,可以使用链表实现。(使用链表,可以将其他零散的内存利用起来)

(3)如保存人事单,如果使用链表则不知道他们之间的关系谁是领导,这种结构需要使用树结构。(树的结构:就可以知道谁是领导,谁是下属)

(4)再如交通图,几个站点之间修路,则需要使用图结构实现。

总结: 数据结构:解决数据如何存储的问题。(个体和个体的关系) 算法:如何对已经存储的数据,进行操作

2、衡量算法的标准

1、时间复杂度:大概程序要执行的次数、而非执行的时间。(因为硬件不同)

2、空间复杂度:算法执行过程中,大概所占用的最大内存

3、难易程度:不能只有自己一个看懂

4、健壮性:不能给一个非法立即数就挂掉了。

3、数据结构的地位

数据结构是软件中最核心的课程

程序 = 数据的存储 + 数据的操作 + 可以被计算执行的语言。

4、指针

在这里插入图片描述

(1)内存是 cpu 能够直接访问的,唯一的存储器

(2)通过地址线,来确定,对哪块内存来进行存储。(32位——0 -- 4G-1)

(3)通过控制线,来确定,对内存是 读取还是 写入。

(4)通过数据线,来确定,传输什么数据。

总结: (1)地址:内存单元的编号, 0000 0000 -- FFFF FFFF, 从0 开始的非负整数。

(2)指针:指针就是地址,地址就是指针。是一个操作受限的非负整数。

(3)指针变量:是存放,内存单元地址(编号),的单元。

(1)基本类型的指针

int *p;

p : 是一个变量的名字 int * :表示 p 只能存放整形变量的地址

必须理解,只能存放,这个概念。

int * p;
char a = 'A';
p = &a; //error:&a 不是整形变量的地址
p = 10//error:10 是一个整数,不是一个地址

(2)数组和指针

int a[5] = {1,2,3,4,5};

在这里插入图片描述

数组名的理解: (1)一维数组名:它是一个,指针常量。 (2)一维数组名:它里面存放的是,第一个元素的地址。 (3)一维数组名:指向,数组当中的第一个元素。

下标的理解:

a[3] <=====> *(a+3)

3[a] <=====> *(a+3)

5、结构体的使用

(1)为什么会出现结构体? 为了表示一些复杂的数据类型,而单个基本类型无法满足需要。

(2) 什么是结构体? 用户根据实际需要,自己定义的复合数据类型。

分号不能省略,分号表示结构体定义结束

   struct Student
 {
int aga;
char address;
char sex;
}; //分号不能省略,分号表示结构体定义结束
  • 定义了一个,新的数据类型。(类似于 int )
  • 只是单纯的定义了一个类型,而不定义一个变量,也就是说并不分配内存
  • 这个数据类型的名字struct Student

(3)如何使用结构体

  两种方式:
	struct Student st = {1000, "zhangsan", 20};
	struct Student * pst = &st;
                
1.
	st.sid
2.
	pst->sid
 pst所指向的结构体变量中的sid这个成员

注意事项

  • 结构体变量不能加减乘除,但是可以相互赋值
  • 普通结构体变量 和 结构体指针变量,作为函数参数传参的问题
#include <stdio.h>
#include <string.h>

struct Mystruct
{
	int id;
	char name[200];
	int age;
};
void fcun(struct Mysturct s1) // 输入型参数,不会修改本身的值
{
printf("%d,%s,%d \n", s1.id,s1.name,s1.age);
} 

void func1(struct Mysturct *ps1) // 输出型参数,会修改实参的值
{
	*ps1.s1 = 10086;
	strcpy(ps1->name,"zhangsan");
	ps1->age = 20;	
}

int main(void)
{
	struct Mysturct s1;
	func1(&s1);
	func(s1);
}

(1)当结构体变量,变为函数参数时候:

void fcun(struct Mysturct s1) // 输入型参数,不会修改本身的值
{
printf("%d,%s,%d \n", s1.id,s1.name,s1.age);
} 

这样传参:会发生结构体的赋值,整个结构体单元都会被拷贝。 这种方法:耗用内存、耗费时间,不推荐。(发送了 200多个字节)

解决办法:结构体指针传参

void fcun(struct Mysturct *s1) // 输入型参数,不会修改本身的值
{
printf("%d,%s,%d \n", s1->id,s1->name,s1->age);
} 

这样传参:只传送了 4 个字节。

6、动态内存的分配

怎么区分动态内存和静态内存?

只要使用了 malloc 函数就是动态的。 没使用,就是静态的。

(1)举例:动态构造一个int型数组

char a[5] = {1,2,3,4,5};

这个数组的空间大小,在运行当中是不可以改变的,只能重新编写。

int len;
printf("请输入你需要的数组的长度 len = ");
scanf("%d",len);
int *pArr = (int *) malloc(sizeof(int) * len);

*pArr = 4; // 类似于 a[0] = 4
pArr[1] = 5; // pArr[1] <=====> *(pArr + 1)

free(pArr); // 释放这个内存空间

这个数组的空间大小,在运行当中,取绝于用户的输入,所以是动态的。

(1)malloc 函数的形参:是一个 int 类型的整数,表示申请多少个字节大小的内存空间。 (sizeof 返回值是一个整数,int 占用4个字节)

(2)malloc函数的功能是请求系统len个字节的内存空间,如果请求分配成功,则返回第一个字节的地址,如果分配不成功,则返回NULL.

第一个字节地址:它并没有实际的含义,因为我们根据这个地址,可以把 8 个字节当一个变量,也可以把4个字节当一个变量

(3)(int *) 强制转换:就是根据这个地址,把4个字节当作一个变量。

(4)malloc 申请的空间使用

*pArr = 4; // 类似于 a[0] = 4
pArr[1] = 5; // pArr[1] <=====> *(pArr + 1)

可以当作一个普通的数组来使用。

(2)跨函数使用内存

通过调用 fun ,使main 函数当中的指针变量 p 指向一个合法的变量单元。

void fun(int *q)
{
	int s;
	q = &s;
}

int main(void)
{
	int *p;
	fun(p);
}

不可以:因为这是一个 值传递,调用完函数,实参p 的值根本没有改变

void fun(int **q)
{
	int s;
	*q = &s;
}

int main(void)
{
	int *p;
	fun(&p);
}

不可以: (1)虽然变成了地址传递,最终 p 的值也发生了改变。 (2)但是 s 变量的内存,在fun 执行完毕之后,就销毁了。

void fun(int *q)
{
	q = (int *) malloc(4);
}

int main(void)
{
	int *p;
	fun(p);
}

不可以:因为这是一个 值传递,调用完函数,实参p 的值根本没有改变

void fun(int **q)
{
	*q = (int *) malloc(4);
}

int main(void)
{
	int *p;
	fun(&p);
}

可以访问:因为malloc 函数申请的内存,只有 free 函数才能释放。

使用案例:

#include <stdio.h>
#include <malloc.h>

struct Student
{
	int id;
	int age;
};

struct Student * CreatStudent(); // 返回值使 struct Student *  类型
{
	return (struct Student *) malloc(sizeof(struct Student));
}
void ShowStudent(struct Student *ps1)
{
	printf("%d, %d\n", ps1->id, ps1->age);
}

int main()
{
struct Student *ps1; //申请了 4 个字节的内存空间
ps1 = CreatStudent(); // 又申请了 8 个字节(1个结构体变量)的空间
ShowStudent(ps1); // 输入型参数
return 0;
}

7、typedef 的用法

我们自己构建的数据类型,struct Mystruct 的名字太长用起来不方便

typedef int zhangsan 
int i = 0; <======> zhangsan i = 0;
typedef struct Student
{
	int sid;
	char name[20]'
	int age;
}ST;
// typedef  给 struct Student 新起了一个名字叫做 ST

struct Student st;   <=======>	ST st;
struct Student *pst; <=======>	ST *pst; 
typedef struct Student
{
	int sid;
	char name[20]'
	int age;
}* PST;
// typedef  给 struct Student * 新起了一个名字叫做 PST
struct Student *pst; <=======>	PST pst; 
typedef struct Student
{
	int sid;
	char name[20]'
	int age;
}* PSTU,STU;
// typedef  给 struct Student * 新起了一个名字叫做 PSTU
// 给 struct Student 新起了一个名字叫做 STU
struct Student *pst; <=======>	PSTU pst; 
struct Student st <=======> STU st;