数据结构与算法——超基础!

前言 顺序是按照上课接触的知识点展开的

回顾C++部分

一、结构体攻略

1.什么是结构体

数组(Array),它是一组具有相同类型的数据的集合。但在实际的编程过程中,我们往往还需要一组类型不同的数据,例如对于学生信息登记表,姓名为字符串,学号为整数,年龄为整数,所在的学习小组为字符,成绩为小数,因为数据类型不同,显然不能用一个数组来存放。

故而有了结构体的使用!结构体(关键字 Struct)可以用来存放一组不同类型的数据。结构体的定义形式为:

> struct 结构体名(即结构体类型){
>      结构体所包含的变量或数组
>       }

结构体是一种集合,它里面包含了多个变量或数组,它们的类型可以相同,也可以不同,每个这样的变量或者数组都称为结构体的成员(Member)。
例如:

> struct stu{ 
> char name;   //姓名 
> int num;   //学号 
> int age;   //年龄 char
> group;   //所在学习小组 
> float score;   //成绩 
> };

stu为结构体名,它包含了5个成员,分别是name、num、age、group、score。

ps:注意大括号后面的分号不能少

2.结构体变量

既然结构体是一种数据,那么就可以用它来定义变量。例如:

struct stu stu1,stu2;

理解如下:
struct关键字声明了结构体类型stu,同时结构体类型stu定义了stu1stu2结构体变量。两个变量stu1,stu2,他们都是stu类型,都分别由5个成员组成(name、num、age、group、score)。注意关键字struct不能少。

这里再举个例子:

#include <iostream>
using namespace std;
struct StructTest //自定义结构体类型
{
	int t1;   //创建结构体成员
	int t2;
};
//typedef StructTest TEST;

int main()
{
	StructTest my_Var;  //定义结构体变量
	//TEST my_Var;
	cout << &my_Var << "  " << &my_Var.t1 << "  " << &my_Var.t2 << endl;
	return 0;
}

输出结果:

006FFCF0  006FFCF0  006FFCF4

分析如下:

  1. 结构体是一种自定义的数据类型,是创建变量的模板,不占用内存空间;结构体变量才包含了实实在在的数据,需要内存空间来存储。即在 int t1,t2;这里不会分配内存,只有在StructTest my_Var; 才开始分配内存。
  2. 结构体和数据类似,也是一组数据的集合,整体使用没有太大意义。数据使用下标[]获取单个元素,结构体使用点号.获取单个成员。获取结构体成员的一般格式为:
    结构体变量.成员名;
  3. main()是程序的入口
  4. &表示取地址运算,故而&my_Var是表示取结构体变量my_Var的地址运算。
    还有一点,结构体的名字(这里是my_Var)就是结构体变量的起始地址,即给my_Var赋值时要先从t1开始赋值,my_Var的输出结果与my_Var.t1的相同的。
  5. 地址会默认转换为16进制,故而根据输出结果判定改结果为地址,故而明白&在此处的意思是取地址运算。

在这里插入图片描述
详情请看以下链接:
https://mp.weixin.qq.com/s?src=3&timestamp=1584102600&ver=1&signature=4hyMUanj1SnNtBH7MCclQzBxh2TZijDM5z-fAGKDr7UNQ9PRuwp7tgfQ3cIkpZYfUaeYuxjcHKgQ5Naii0tYRt011eLpXbpBVeinPPu7yCLe2cIZBzw9frIDbj43rFmhA2PO0yZJkQCGyXuxE9NmPHB3HCT-QZtdNWY177U1Q=

3. 结构体在内存中的对齐规则

c++中基本数据类型的内存字节占比:
在这里插入图片描述

具体的对其规则详见下方链接:
https://blog.csdn.net/grantxx/article/details/7577730

此处谈一谈上方链接的最后一道题:

struct X{
       char a;
       int b;
       double c;
 };
 struct Y {
       char a;
       X b;
};

经测试,可知sizeof(X)为16,sizeof(Y)为24。由第一原则和第二原则可知sizeof(X)的大小,而计算sizeof(Y)的大小,需要借助struct X

struct Y中的X b;的计算要用向上取整法,向struct X看齐,取struct X的最宽元素长度double,而double的字节数为8,

故而可得在struct Y中,最宽的元素长度为8,首先系统会将字符变量 a 存入第0个字节(相对地址,指内存开辟的首地址);然后在存放 X 变量b 时,会以8个字节为单位进行存储,由于第一个八字节模块已有数据,因此它会存入第二个四字节模块,同时X类型本身占据16个字节,即sizeof(Y)=8+16=24.

二、指针攻略大全

1.先谈谈什么是指针

题外话
int p; 与 int *p;的区别
int p;是定义一个整型变量p,并且p里面放的全是整型(int)数据。
而int *p; 中*是起声明作用,声明变量p为指针变量,只能用来存放地址。

指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。
要搞清楚一个指针就需要搞清楚指针四方面的内容:指针的类型,指针所指向的类型、指针的值或者叫做指针所指向的内存区,还有指针本身所占据的内存区。
先声明几个指针放着做例子:
例一:
int *ptr;
char *ptr;
int **ptr;
int (*ptr)[3];
int *(*ptr)[4];

2.开始深入——指针的类型

从语法的角度看,只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。这是指针本身所具有的类型。
例如:
int *ptr; //指针的类型是int *
char *ptr; //指针的类型是char *
int **ptr; //指针的类型是 int **
int (ptr)[3]; //指针的类型是 int()[3]
int *(*ptr)[4]; //指针的类型是 int ()[4]

3.继续深入——指针所指向的类型

当通过指针来访问指针所指向的内存区域时,指针所指向的类型决定了编译器将那片内存区里的内容当做什么来看待。

这里解释一下,什么是指针所指向的类型
int *a;
a是指针,int是什么呢?int是 指针a 指向 变量a为首地址的内存区域的类型。

从语法上看,只需要把指针声明语句里的指针名字和**名字左边的指针声明符***去掉,剩下的就是指针所指向的类型。
例如:
int *ptr; //指针所指向的类型是int
char *ptr; //指针所指向的的类型是char
int **ptr; //指针所指向的的类型是 int *
int (*ptr)[3]; //指针所指向的的类型是 int()[3]
int *(*ptr)[4]; //指针所指向的的类型是 int *()[4]

4.易混淆点——指针的值

指针的值是指针本身存储的数值,这个被编译器当做一个内存区域的首地址,而不是一个一般的数值。、
指针所指向的内存区域就是从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型)的一篇内存区。

(sizeof的用法可以看结构体那部分)

例如:
一个指针的值为a,则说明 该指针指向以a为首地址的一片内存区域;
一个指针指向某块内存区域,则说明 该指针的值是这块内存区域的首地址。

5.指针本身所占据的内存区

指针本身占据了4个字节的长度

6.不懂就问!(划重点)

以后每遇到一个指针,就对自己进行三连问:

  1. 这个指针的类型是什么?
  2. 指针指向的类型是什么?
  3. 该指针指向了哪里

7.取地址运算符& 和 间接运算符*

&a的运算结果是一个指针,指针的类型是a的类型加个*,指针所指向的类型是a的类型,指针所指向的地址就是a的地址。(看不懂没关系,尝试结合例子进行理解)
例如:
int a=12;
int b;
int p;
int **ptr;
p=&a; //&a的结果是一个指针,类型是int
,指向的类型是int,指向的地址是a的地址。
*p=24; //p的结果,在这里它的类型是int,它所占用的地址是p所指向的地址,显然,p就是变量
a。
ptr=&p; //&p的结果是个指针,该指针的类型是p的类型加个
,在这里是int**。该指针所指向的
类型是p的类型,这里是int
。该指针所指向的地址就是指针p自己的地址。
*ptr=&b; //ptr是个指针,&b的结果也是个指针,且这两个指针的类型和所指向的类型是一样的,所以&b来给ptr赋值就是毫无问题的了。
**ptr=34; //ptr的结果是ptr所指向的东西,在这里是一个指针,对这个指针再做一次运算,结果
就是一个int类型的变量。

小总结:
变量 + 取地址运算符(&)= 指针
指针 + 间接运算符(*)= 变量
例如:
int *p;(p为指针名)
int a;(a为变量名)
即 p=&a
得 a=*p (此时的*是做运算符,取指针变量指向的值 赋予a)

8.值传递、指针传递、引用传递

#include <iostream>
using namespace std;
void swap1(int x,  int y);//值传递
void swap2(int *px,  int *py);//指针传递
void swap3(int &x,  int &y);//引用传递

int main()
{	
	int a=1, b=2;
	int *pa=&a, *pb=&b;

	swap1(a,b); //断点①
	cout<<"After calling swap1:a="<<a<<", b="<<b<<endl;

	a=1,b=2;
	swap2(pa,pb); //断点③
	cout<<"After calling swap2:a="<<a<<", b="<<b<<endl;

	a=1,b=2;
	swap3(a,b); //断点⑤ 
	cout<<"After calling swap3:a="<<a<<", b="<<b<<endl;

	return 0;
}
//值传递
void swap1(int x, int y)
{
	int t; 
	t=x;  x=y;  y=t; //断点②
}

//指针传递
void swap2(int *px, int *py)
{
	int t; 
	t=*px;  *px=*py;  *py=t; //断点④
}

//引用传递
void swap3(int &x, int &y)
{
	int t; 
	t=x;  x=y;  y=t; //断点⑥
}

指针传递:
在这里插入图片描述
引用传递:
在这里插入图片描述

首先,什么是引用?
引用就是别名。
int& x; 中&不做运算符,而是起定义的作用,去声明一个引用。整体是声明一个int型的引用变量x。看上述例子, int& x 中x是形参,是对实参a进行的引用,可以说x就是a。(当&做运算符时,&a表示取a的地址)
其次,整理下知识点,*也会有起定义作用的时候。
int *p; 就是起声明作用,声明p是个指针变量。(当*做运算符时,a = *p 此时的* 表示取指针变量指向的值 赋予a)

参考链接:
https://mp.weixin.qq.com/s?src=3&timestamp=1584262868&ver=1&signature=pemKJhXzfKW0Lr6sSQy8CNTvz7gR2aX3rolRcCmmDoih0aUK65vXyskE1Kqt5MBqGI7qjAqiqKwc5l2YkzEqJjCSmIZPuwW80rl8c0lRRdsMzidCnoUiC7qwPdAizUkVh83IEwxFcrPhAUWUNyFD0We35a5bT0Gia8HGJonwk=

三、形参和实参

形参出现在函数定义中,在整个函数体内都可以使用,离开该函数则不能使用。实参出现在主调函数中,进入被调函数后,实参变量也不能使用。
形参和实参的功能是作数据传送。发生函数调用时,主调函数把实参的值传送给被调函数的形参,从而实现主调函数向被调函数的数据传送。

可能有人会问,主调函数和被调函数是什么? 主调函数应该为主函数,而被调函数应该为外部函数。

  1. 主函数的标志是 int main(){ …函数内容}
  2. 外部函数是声明在主函数外在主函数中调用,或在其他外部函数中调用,它必须在主函数面前声明,在主函数外任意地方定义
    一般声明格式为:函数返回值类型 函数名(形参表); 定义格式为:函数返回值类型 函数名(形参表){…函数内容}
    或者也可以声明和定义一起,
    如: 函数名(形参表){…函数内容}
主调函数和被调函数的应用例子如下:
#include <iostream>
using namespace std;
void swap1(int x,  int y);//值传递,外部函数声明
void swap2(int *px,  int *py);//指针传递
void swap3(int &x,  int &y);//引用传递

int main()
{	
	int a=1, b=2;
	int *pa=&a, *pb=&b;

	swap1(a,b); //断点①,调用外部函数
	cout<<"After calling swap1:a="<<a<<", b="<<b<<endl;

	a=1,b=2;
	swap2(pa,pb); //断点③
	cout<<"After calling swap2:a="<<a<<", b="<<b<<endl;

	a=1,b=2;
	swap3(a,b); //断点⑤ 
	cout<<"After calling swap3:a="<<a<<", b="<<b<<endl;

	return 0;
}
//值传递,定义外部函数
void swap1(int x, int y)
{
	int t; 
	t=x;  x=y;  y=t; //断点②
}

//指针传递
void swap2(int *px, int *py)
{
	int t; 
	t=*px;  *px=*py;  *py=t; //断点④
}

//引用传递
void swap3(int &x, int &y)
{
	int t; 
	t=x;  x=y;  y=t; //断点⑥
}

简单点来说,实参与形参的关系。用上述例子来说,main()方法中要调用swap1(a,b)方法,而a,b为main()方法中的两个变量,则会在外部临时生成两个变量int x和int y。int x和int y的作用域只有
void swap1(int x, int y)
{
int t;
t=x; x=y; y=t; //断点②
}
这一块,在调用结束后两个临时变量将被撤销,进程会从断点2直接跳回断点一。
注意事项:

  1. 形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只有在函数内部有效。函数调用结束返回主调函数后则不能再使用该形参变量。
  2. 实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们必须具有确定的值,以便把这些值传送给形参。因此应预先用 赋值、输入 等方法使实参获得确定值。
  3. 实参和形参在数量上、类型上、顺序上应严格一致,否则会发生“类型不匹配”的错误。
  4. 函数调用中发生的数据传送是单向的。即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。因此在函数调用过程中,形参的值发生改变,而实参中的值不会变化。
  5. 当形参和实参不是指针类型时,在该函数运行时,形参和实参是不同的变量,他们在内存中位于不同的位置,形参将实参的内容复制一份,在该函数运行结束的时候形参被释放,而实参内容太不会改变
    而如果在函数的参数是指针类型变量,在调用该函数的过程中,传给函数的是实参的地址,在函数体内部使用的也是实参的地址,即使用的就是实参本身。所以在函数体 内部可以改变实参的值

四、词汇基础概念

1.typedef

功能:为现有类型创建一个新名字
例如:
Typedef int size
声明定义了 int 的同义词,名字为size,可以在任何需要int的上下文里使用size。

五、小知识

  1. 断点
    功能:让程序执行到某个地方停止下来
  2. 在visual studio中,一个项目只能创建一个解决方案,否则可能会运行失败。

数据结构与算法部分

一、关于数据结构部分基本概念

1.数据结构

(1)基本概念

数据结构是一门研究非数值计算程序设计问题中计算机的操作对象以及它们之间的关系和操作等的学科。
简单点说,结构就是关系。数据结构是指
所有数据元素以及数据元素之间的关系,可以看作是相互之间存在着某种特定关系的数据元素的集合

(2)形式定义

在这里插入图片描述

(3)逻辑结构

逻辑结构是对数据元素之间存在的逻辑关系的描述;
可以用一个数据元素的集合和定义在此集合上的若干关系表示;
数据的逻辑结构可看作是从具体问题抽象出来的数学模型。

(4)存储结构

在这里插入图片描述

(5)数据的运算

a. 数据运算是指对数据实施的操作;
b. 数据运算最终需要在对应的存储结构上用算法实现
可分为两个层次:
运算定义:是基于逻辑结构的,是抽象的
运算实现:是基于存储结构的,是具体的

(6)逻辑结构与存储结构的关系

同一逻辑结构可以对应多种存储结构;
同样的运算,在不同的存储结构中,其实现过程是不同的。

研究每一种具体的数据结构的步骤:
1.理解其逻辑结构
2.设计出对应的有利于解决问题的存储结构;
3.在此存储结构的基础上,设计出相应的一系列运算以解决问题

2.数据、数据对象、数据元素、数据项的理解与区别

数据:是描述客观事物的数和字符的集合。例如,人们在日常生活中使用的各种文字、数字和特定符号都是数据。从计算机的角度看,数据是所有能被输入到计算机中,且能被计算机处理的符号的集合,它是计算机操作的对象的总称,也是计算机所处理信息的某种特定的符号表示形式(例如,200902班学生数据就是该班全体学生记录的集合

数据,也就是符号,必须具备两个前提:
a.可以输入到计算机中
b.能被计算机程序处理

数据元素:是数据的基本单位(例如,200902班中的每个学生记录都是一个数据元素)。在有些情况下,数据元素也称为元素、节点、顶点或者记录等。一个数据元素可以由若干个数据项组成。
数据项:是具有独立含义的数据最小单元(不可再分割),也称为字段或域。例如,200902班中的每个数据元素(即学生记录)是由学号、姓名、性别和班号等数据项组成的。
数据对象:是指性质相同的数据元素的集合,它是数据的一个子集。在数据结构课程中讨论的数据通常指的是数据对象。
在这里插入图片描述

3.什么是线性表

在这里插入图片描述

(1). 定义:线性表是由n(n>= 0)个属性相同的数据元素a1,a2…an组成的一个有限序列
线性表或是空表,或可以表示为:
A=(a1,a2,…,ai,…,an)
其中:
ai  (i=1,2,…,n)是线性表中的一个元素。
i  表示元素存在的位置。
(2). 逻辑特征:

有且只有一个表头元素a1,它无前驱;
有且只有一个表尾元素an,它无后继;
表头元素与表尾元素外,其它所有元素有且只有一个前驱,也有且只有一个后继。
线性表中结点的个数n称为线性表的长度。当n=0时,称为空表。
A=(a1,a2,…,ai,…,an)

(3). 线性表结构定义

Linear_list=(D,R)
在这里插入图片描述
操作P:
初始化(InitList):初始化线性表
求表长(ListLength):求线性表中数据元素个数
判空表(ListEmpty):判定线性表是否为空
插入表(ListInsert):在指定位置插入数据元素
删除(ListDelete):删除指定位置上的数据元素
取值(GetElem):获取指定位置数据元素的值

(4). 线性表的存储

线性表以顺序存储链式存储两种方式存储到计算机的内存中。
在这里插入图片描述

(5)线性表的顺序存储结构及实现
a. 顺序表的定义

用一组地址连续的存储单元依次存储线性表的每个数据元素,这种存储结构称为线性表的顺序存储结构,用这种结构表示的线性表称为顺序表。

b. 顺序表的特点

用数据元素在计算机内物理位置相邻来表示线性表中数据元素的逻辑关系。(形象地说,逻辑关系一般是指在草稿纸上画出来的)

c. 数据元素的存储位置

在这里插入图片描述

d. 顺序存储结构内存视图

在这里插入图片描述

补充一点,一般是用数组来分配连续的存储空间(即顺序表)

e. 顺序表的应用
#define MaxSize 100  //定义一个常量
typedef int ElemType;      //声明顺序表的元素类型
typedef struct {	 
  ElemType data[MaxSize];  //存放顺序表数据,常用数组来分配连续的空间(顺序表)
  int length;
} SqList;          //SqList为用户定义的顺序表类型

MaxSize 表示顺序表中最多元素个数。
ElemType 是一个抽象类型。在实现时,要定义为具体的数据类型。
如:typedef char ElemType; length 表示线性表的当前表长,非负值且<=MaxSize;

f. SqList(顺序表)类型变量的内存视图

在这里插入图片描述

SqList my_list; //定义顺序表类型的变量
my_list SqList //线性表数据类型
my_list //是SqList类型的实例化变量,my_list拥有两个数据域
即 数组:data 存放线性表数据
变量:length 保存线性表数据个数

g. 顺序表程序实现方法
  1. 声明线性表的数据类型
    一般在头文件中声明(该头文件中还应包括操作接口的声明);
    告诉编译器该数据类型占内存空间的大小。
  2. 定义线性表类型的变量
    一般在函数中或实现代码中定义;
    告诉编译器为变量分配内存空间。
  3. 操作线性表类型变量
    操作变量中保存的数据
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值