人人都说408难,我也这么觉得,所以想早做准备,之前数据结构也学过,但是是Java版本的。
所以c语言方面还是白纸,只自学过c语言的基础语法,所以趁着这个暑假,系统的学习一下c语言版本的数据结构,因为其中有一个指针,语法和内置函数有很多不一样,需要下狠功夫。
教材参考:严蔚敏的《数据结构(C语言 第2版)》,也是考研大纲的指定书目。
基础知识概念
一.导论
1.前言
这是第一篇c版数据结构的文章,先学习一下对于数据结构的认识,对于未来学习的路线,各种数据结构的初步认识。
2.基本语法
- C语言的文件格式,头文件
- 数据类型,声明变量,定义函数
- 控制语句,if-else,while循环,for循环
- 数组,数组的遍历
- 字符串的相关知识
3.指针
3.1含义
-
指针是一种特殊的变量类型,它保存了一个内存地址。通过指针,我们可以间接地访问和操作内存中的数据。指针变量存储着所指向内存块的起始地址。
-
指针两个要点:
指针是内存中的一个最小单元的编号,也就是地址
指针变量,是用来储存内存地址的变量
3.2指针的声明
-
声明和符号含义
*:用来声明一个指针变量,如int *p,声明了一个整数型的指针p。
&:取址符,如&a,返回储存a变量的存储地址。
其中&在输入函数scanf中叶出现过。 -
解析:
int a=1;
int *p=&a;
说明:
声明了一个整数变量a,值为1,。
声明了一个指针变量p,p指向a的地址&a,
*p可以理解为解指,根据p指向的地址,顺藤摸瓜,找到了对应的值1
3.3. 示例代码和解析
#include <stdio.h>
int main(){
//在内存中开辟一块空间
int a=10;
//*声明p为一个指针变量
//*p用来访问p所指向地址的值
//p指向a存储地址
int *p=&a;
//输出结果对比,后两者相同
printf("*p:%p\n",*p);
printf("p:%p\n",p);
printf("&a:%p",&a);
return 0;
}
//输出结果为:
//*p:000000000000000A,是16进制的10
//p:000000000061FE14
//&a:000000000061FE14
3.4.常见应用
- 使用指针来传递和操作数组:
求数组的长度:sizeof(arr)/sizeof(arr[0]) - 数组名在C语言中,指的是指向数组第一个元素的指针,代表了该数组在内存中的起始地址,因此,当数组名作函数参数时,实参与形参之间不是"值传递",而是"地址传递。
- 所以函数想要传入整个数据中直接传入arr是错误的,需要传入*arr才行。
- 示例代码:
#include <stdio.h>
// 函数声明,参数为int类型的指针和数组长度
void printArray(int *arr, int length);
int main() {
int arr[] = {1, 2, 3, 4, 5};
int length = sizeof(arr) / sizeof(arr[0]);
// 调用函数并传入数组和长度作为参数
printArray(arr, length);
return 0;
}
// 函数定义,打印数组元素
void printArray(int *arr, int length) {
for (int i = 0; i < length; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
4.结构体
4.1. 声明形式为:
- struct 结构体类型名{
结构体的属性定义;
}; - 类型:标量,数组,指针,其他结构体
4.2.结构体变量的定义和初始化:
(1)声明类型时定义变量
(2)struct 结构体类型名 变量名={初始化化赋值};
(3)示例代码:
//定义一个二维平面的点结构体
//第一种方法:同时声明点结构体变量p1
struct Point{
int x;
int y;
}p1;
//第二种方法:定义结构体变量p2,并初始化
struct Point p2={10,12};
4.3结构体的访问
因为c语言中参数传递是地址的传递而不是值传递,注意函数的调用。
示例代码如下:
# include <stdio.h>
//声明学生结构体
struct Stu { //类型声明
char name[15]; //名字
int age; //年龄
};
struct Stu s = {"张三", 20};//初始化
//编写输出函数
//其中*表明ps为一个指针变量,ps指向的是地址
void print(struct Stu* ps){
printf("name=%s age=%d\n",(*ps).name,(*ps).age);
}
int main(){
printf("结构体的调用.\n");
//结构体地址传参
//传进去之后,ps指向s的地址,*ps就是指向s地址对应的数据
print(&s);
return 0;
};
二.基本概念和术语
程序=数据结构+算法
- 数据 (Data) :客观事物的符号表示,是所有能输入到计算机中并被计算机程序处理的符号的总称。
- 数据元素(Data Element):数据的基本单位,在计算机中通常作为一个整体进行考虑和处理。
- 数据项 (Data Item) :组成数据元素的、有独立含义的、不可分割的最小单位。例如,学生基本信息表中的学号、姓名、性别等都是数据项。
- 数据对象 (Data Object) 是性质相同的数据元素的集合,是数据的一个子集。如整数集,字母集等。
三.数据结构
数据结构 (Data Structure) 是相互之间存在一种或多种特定关系的数据元素的集合。换句话说,数据结构是带 ”结构" 的数据元素的集合, “结构” 就是指数据元素之间存在的关系。
数据结构包括逻辑结构和存储结构两个层次。
1.逻辑结构
1.1概念
数据的逻辑结构是从逻辑关系上描述数据,它与数据的存储无关,是独立千计算机的。
数据的逻辑结构有两个要素: 一是数据元素;二是关系。数据元素的含义如前所述,关系是指数据元素间的逻辑关系。根据数据元素之间关系的不同特性, 通常有四类基本结构。
1.2分类
总体分为两大类,线性和非线性。
非线性:树,集合,图。
- 集合结构:元素除同属一个集合,无其他关系。
- 线性结构:元素之间存在一对一的关系,有且个开始元素和终端元素。
- 树形结构:一对多的层次关系,只有一个开始元素,但是可以有多个终端元素。
- 图形结构:多对多的关系。
1.3 表示方式
一般用二元组来表示数据的逻辑结构:B=(D,R)。其中B是一种逻辑结构,R是所有关系的集合,可用序偶表达。表示如下:
D={d(i)|0<=i<=n-1,n>0}; n为D中数据元素的个数
R={r(i)|1<=j<=m,m>=0}; m为R中关系的个数
2.存储结构(物理结构)
数据对象在计算机中的存储表示称为数据的存储结构,也称为物理结构。把数据对象存储到计算机时,通常要求既要存储各数据元素的数据,又要存储数据元素之间的逻辑关系,数据元素在计算机内用一个结点来表示。
数据元素在计算机中有两种基本的存储结构,分别是:
顺序存储结构和链式存储结构。
图片对比一下:
2.1顺序存储结构
- 顺序存储结构是借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系,通常借助数组类型来描述。
- 它的存储地址编号是连续的,一个数组占用的是一整块连续的存储空间。
2.2链式存储结构
- 顺序存储结构要求所有的元素依次存放在一片连续的存储空间中,而链式存储结构,无需占用一整块存储空间。但为了表示结点之间的关系,需要给每个结点附加指针字段,用于存放后继
元素的存储地址。所以链式存储结构通常借助于指针类型来描述。 - 可以不连续也可以不连续,第一个元素存储在地址1号位,第二个地址在100号位,但是通过一条链把它们串联起来。p.next=q;找到p的后继结点。
第三种:索引储存结构
第四种:哈希(散列)储存结构
四.数据类型和抽象数据类型
4.1数据类型
数据类型 (Data Type) 是高级程序设计语言中的一个基本概念,前面提到过顺序存储结构可以借助程序设计语言的数组类型描述,链式存储结构可以借助指针类型描述,所以数据类型和数据结构的概念密切相关。每一个数据都有自己的类型,整型啊,数组啊…
4.2抽象数据类型
- 抽象就是抽取出实际问题的本质。
- 抽象数据类型 (Abstract Data Type, ADT) 一般指由用户定义的、表示应用问题的数学模型,以及定义在这个模型上的一组操作的总称,具体包括三部分:数据对象、数据对象上关系的集合以及对数据对象的基本操作的集合
- 抽象数据类型的定义格式如下:
C语言中:抽象数据类型使用结构体完成,操作使用函数来实现。
五.算法的基本概念
算法 (Algorithm) 是为了解决某类问题而规定的一个有限长的操作序列。
5.1 五个重要特性。
- 有穷性:一个算法必须总是在执行有穷步后结束,且每一步都必须在有穷时间内完成。
- 确定性:对千每种情况下所应执行的操作,在算法中都有确切的规定,不会产生二义性,
使算法的执行者或阅读者都能明确其含义及如何执行。 - 可行性:算法中的所有操作都可以通过已经实现的基本操作运算执行有限次来实现。
– 输入:一个算法有零个或多个输入。当用函数描述算法时,输入往往是通过形参表示的,
在它们被调用时,从主调函数获得输入值。 - 输出:一个算法有一个或多个输出,它们是算法进行信息加工后得到的结果,无输出的
5.2 算法的优劣标准
一个算法的优劣应该从以下几方面来评价。
(1)正确性
(2)可读性
(3)健壮性
(4)高效性
六.时间复杂度
6.1定义
一般情况下, 算法中基本语句重复执行的次数是问题规模n的某个函数f(n), 算法的时间量度 。记作T(n) = 0(f(n))
它 表示随问题规模n的增大, 算法执行时间的增长率 和 f(n)的增长率相同, 称做算法的渐近时间复杂度, 简称时间复杂度(TimeComplexity)。
6.2计算
- 一条语句的重复执行次数称作语句频度(FrequencyCount)。
- 设每条语句执行一次所需的时间均是单位时间, 则一个算法的执行时间可用该算法中所有语
句频度之和来度量。 - 示例如下
当问题规模趋于无穷时,也就是n趋于无穷,取极限,n充分大时,f(n)只与最高阶相关.
我们记作O(n^3) - 具体情况还可以分为:最好时间复杂度,最坏时间复杂度,平均时间复杂度。例如数组要查找一个数组i如果数组第一个元素就是i时间复杂度最好,如果数字在最后,则要检查完所有数据,最坏情况。
对算法时间复杂度的度量,人们更关心的是最坏情况下和平均情况下的时间复杂度。然而在
很多情况下,算法的平均时间复杂度难千确定。因此,通常只讨论算法在最坏情况下的时间复杂
度,即分析在最坏情况下,算法执行时间的上界。在本书后面内容中讨论的时间复杂度,除特别
指明外,均指最坏情况下的时间复杂度。
6.3常见的时间复杂度:
常量阶:O(1)
平方阶:O(n^2)常见有选择排序,双重for循环
对数阶:O(log(n))常见有二分查找…
线性阶:O(n) 常见有简单查找
指数阶:O(2^n)
阶乘阶:O(n!)常见有旅行商问题
七.空间复杂度
- 关千算法的存储空间需求,类似千算法的时间复杂度,我们采用渐近空间复杂度(Space
Complexity)作为算法所需存储空间的扯度,简称空间复杂度,它也是问题规模n的函数。 - 示例如下
//【算法1】
for(i=O;i<n/2;i++)
{ t=a[i];
a[i]=a[n-i-1];
a[n-i一l]=t;
}
//【算法2】
for (i=O; i<n; i++) {
b[i]=a[n-i-1];
for(i=O;i<n;i++) {
a [i] =b [i];
}
}
-
解析:
算法1仅需要另外借助一个变批t’ 与问题规模n大小无关,所以其空间复杂度为0(1)。
算法2需要另外借助一个大小为n的辅助数组b, 所以其空间复杂度为O(n)。 -
通常情况下, 鉴于运算空间较为充足, 人们都以算法的时间复杂度作为算法优劣的衡
址指标。所以一般空间复杂度不怎么考虑,除非题目特殊要求。
第一绪论章,主要是回顾一下c语言需要的知识储备,以及一些数据结构相关的概念。了解了有哪些数据结构,逻辑结构和物理结构之间的关系。最重要的就是知道了时间复杂度,唯一的难点就是时间复杂度,要熟练掌握它。绪论完结撒花✿✿ヽ(°▽°)ノ✿。 |