一、什么是数据结构
1. 定义
数据结构 = 研究 数据 与 数据之间的关系。
数据处理规模的递进:
1.C语言语法2.基本编程思维3.处理单个或几个变量的数据4.处理批量数据(数组+结构体)5.数据规模更大->研究 数据与数据之间的关系(数据结构)->结合处理数据的思路(算法)->最终实现好的编程效果一句话:数据结构就是研究"数据元素"与"数据元素"之间的关系。
2. 怎么学习
- 数据结构的特点:抽象
- 数据的操作(指针)
- 代码实现?
- 学习建议:画图(让抽象关系清晰明了)
3. 优秀程序员的基本功
一门编程语言+数据结构和算法(好的)程序设计=算法(好)+数据结构(合适的)二、数据结构的内容与术语
| 术语 | 含义 |
|---|---|
| 数据 | 描述客观事物的符号 |
| 数据元素 | 组成数据的、有一定意义的基本单位 |
| 数据项 | 一个数据元素由若干个数据项组成(数据项是数据不可分割的最小单位) |
| 数据对象 | 性质相同的数据元素的集合,是数据的子集 |
| 数据结构 | 相互之间存在一种或多种特定关系的数据元素的集合 |
例:单个学生 = 数据元素(一个有意义的基本单位);学生这种类型 = 数据对象。
三、数据的逻辑结构(数据之间的关系)
| 结构 | 关系 | 说明 / 例子 |
|---|---|---|
| 集合 | 元素间同属一类(无对应) | 无关系 |
| 线性 | 一对一的 | 0-1-2-3,除首尾元素外,每个元素都有前驱节点和后继节点 |
| 树形 | 一对多(层次) | 班主任 -> 班长 -> 副班 / 学委 / 纪委 / 劳委 |
| 图形 | 多对多 | 地图、抢票软件 |
四、数据的存储结构
数据最终还得存储到计算机中,怎么把具有逻辑关系的数据存储到计算机?
| 存储结构 | 说明 |
|---|---|
| 顺序存储 | 数组 |
| 链式存储 | 链表(链 = 铁链、狗链) |
| 索引结构 | index(目录/索引),专门维护一个索引表 |
| 散列(哈希)结构 | 哈希查找:哈希表,值 <–哈希函数–> 位置 |
顺序存储(数组)优缺点
优点:随机访问方便 缺点:增、删(麻烦)链式存储(链表)优缺点
优点:增、删除(方便) 缺点:访问需要遍历顺序存储的增删(数组)
intsno[10]={0,1,2,3,4,5,6,7,8,9};// 增加一个学号 -> 思路:// 1. 新创建一个多一个元素的数组// 2. 将原数组数据拷贝过来,再新加一个数值// 3. 释放原有空间intsno1[11]={0,1,2,3,4,5,6,7,8,9,10};// 删除一个学号intsno1[10]={0,1,2,3,4,5,6,7,8,9};同一逻辑关系的数据可以用不同存储结构存储,但不同存储结构对应处理思路(算法)也不同。用什么样的算法,取决于处理怎样关系(逻辑 + 存储)的数据。
五、数据结构知识体系
数据结构 ├── 逻辑结构(逻辑模型) │ ├── 集合结构 │ ├── 线性结构 │ ├── 树形结构 │ └── 图状结构 ├── 存储结构(映像到内存) │ ├── 顺序结构 │ ├── 链式结构 │ ├── 索引结构 │ └── 散列结构 └── 抽象数据类型(数据对象 + 数据关系 + 基本操作)六、算法
定义:算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作。算法就是解决问题的步骤(是解决问题思路的具体描述)。
算法五大基本特性
1.输入2.输出3.有穷性4.确定性5.可行性好算法的评判标准
1.正确性2.可读性3.健壮性(鲁棒性)4.时间效率和存储量低(时间复杂度 和 空间复杂度)七、线性表(顺序表)
线性表是一对一的数据结构,顺序存储:C语言、C++、Java、Python 都支持。
数组(顺序表)的算法:创建、增、删、改、查、销毁。
// 1.创建inta[10];int*p=malloc(sizeof(int)*10);// 2.增inputArray();// 输入数据insertArray();// 插入数据// 3.删deleteArray();// 4.改modifyArray();// 找到 修改// 5.查findArray();// 6.销毁(堆区)free(p);// 数据销毁 / 空间销毁八、链表(用链式方式实现线性结构)
1. 节点(结点)结构
节点包含:a. 数据 b. 下一个节点的地址
typedefintdata_t;structnode{data_tdata;// 数据域structnode*next;// 指针域:下一个节点的地址};typedefstructnodenode_t;2. 带头链表
数据结构中 ——带头链表:
头节点->首节点->...->尾节点 数据域xxx 数据域1数据域3指针域 指针域 指针域NULL- 头节点:带头,数据域不关心
- 首节点:第一个有效数据节点
- 尾节点:最后一个有效数据节点(特点:指针域为 NULL)
- 空链表:只有头节点,而且头节点的指针域为 NULL
3. 创建链表(创建空链表)
node_t*linklist_create(void){// 创建一个节点,充当头节点node_t*head=malloc(sizeof(node_t));// 头节点数据域不关心,指针域为 NULLhead->next=NULL;returnhead;// 返回头节点的地址}4. 头插法(每次插入到头节点的后面)
voidlinklist_insert_head(node_t*phead,data_tdata){// 1.创建新节点node_t*pNew=malloc(sizeof(node_t));// 2.把 data 放到数据域中pNew->data=data;// 3.连起来pNew->next=phead->next;// (1) 新节点指向原首节点phead->next=pNew;// (2) 头节点指向新节点return;}5. 判断链表是否为空
intis_empty(node_t*phead){// 空链表:只有头节点,phead->next == NULLreturnphead->next==NULL;}6. 遍历打印
voidlinklist_print(node_t*phead){// 有效性验证:// 1. phead 有可能传的是 NULL// 2. 空链表:phead->next == NULLnode_t*p=phead->next;while(p!=NULL){printf("%d ",p->data);// 打印此时 p 所在节点的数据域p=p->next;// 向后移动}putchar('\n');}7. 尾插法(每次插入到尾节点的后面)
voidlinklist_insert_tail(node_t*phead,data_tdata){// 1.创建新节点node_t*pNew=malloc(sizeof(node_t));// 2.把 data 放到数据域中pNew->data=data;pNew->next=NULL;// 3.连起来// a.先找到尾节点node_t*p=phead;while(p->next!=NULL){p=p->next;}// 写法1p->next=pNew;// 写法2(等价)// pNew->next = p->next;// p->next = pNew;return;}8. 查找(按 key 找节点,返回节点地址)
node_t*linklist_find_key(node_t*phead,data_tkey){node_t*p=phead->next;while(p!=NULL){if(p->data==key){returnp;// 找到,返回节点地址}p=p->next;}returnNULL;}9. 修改(找到位置上修改)
node_t*linklist_update(node_t*phead,data_told,data_tnew){node_t*p=linklist_find_key(phead,old);if(p!=NULL){p->data=new;// 找到位置上修改}returnp;// 返回节点地址}10. 头删(砍头法:每次删除首节点)
intlinklist_delete_head(node_t*phead){// 1.删除:让头节点的指针域指向首节点的下个节点node_t*p=phead->next;phead->next=p->next;// 2.释放 p 指向的节点(原先的首节点)free(p);p=NULL;return0;}11. 尾删(每次删除尾节点)
intlinklist_delete_tail(node_t*phead){// 思路:单向链表// 让 p 停在尾节点的前一个节点node_t*p=phead;while(p->next->next!=NULL)// 找到尾节点的前一个节点{p=p->next;}free(p->next);// 释放尾节点p->next=NULL;// p 所在节点成为新的尾节点return0;}注:笔记中尾删循环条件
while(p->next == NULL)是笔误,正确应为while(p->next->next != NULL)(让 p 停在尾节点的前一个节点),上面已按正确写法整理。