动态内存分配链表
合集下载
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
要在链表插入一个元素时,如果已经确定了该元素的插入位置,需要做的事情只有两点:
把为要插入的元素分配空间,生成一个新结点;二:是通过指针域的修改,把新结点插入到链表中。
1
2
3
3-2链表结点的插入
一、开辟新结点
5
8
6
22
∧
head
10
p
p->next
∧
q
struct node * q; q=(struct node *) malloc (sizeof(struct node)); q->data=10; q->next=NULL;
第10章 链表
CLICK HERE TO ADD A TITLE
开 始
演讲人姓名
CONTENTS
目录
批量数据的存储
链表: 不需要事先指定空间大小,动态分配与释放
批量数据的存储方式:
数组
数组存储的缺陷: 必须预先指定数组的大小
链表
CONTENTS
主要内容
动态内存分配ห้องสมุดไป่ตู้
单链表的建立
单链表概述
单链表的应用
5
8
6
22
∧
head
p
q
q=p->next; p->next=q->next; free(q);
3-3链表结点的删除
删除该结点是通过修改其前驱结点的指针域来实现的,因此,在删除之前首先应找到被删结点的前驱结点。
1
单链表的实质是一个结点的序列,建立单链表实质上就是逐个生成每一个结点,并把结点插入链表的过程。
1-1 C程序的内存划分
栈区(stack):编译器自动分配释放,存放函数参数、局部变量等。
堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由操作系统回收。
全局区(静态区)(static):存放全局变量和静态变量,其中初始化的全局与静态变量在一块区域,未初始化的全局与静态变量在相邻的另一块区域。程序结束后由系统释放。
1-3 动态内存分配函数
动态数组实现的一般过程: (1) 定义数组元素类型(t)的指针(p) (2) 指定数组要存放的元素个数(n) (3) 动态申请(n * sizeof(t))大小的存储空间,并让p指向该存储空间首地址 (4) 通过p对数组进行赋值和操作 (5)使用结束后释放数组空间
头指针
头指针
有时为了操作方便,在第一个结点之前虚加一个“头结点”,以指向头结点的指针为链表的头指针。
空指针
链表为空表时, 头结点的指针域为空
p
p=head->next;
p=p->next;
2 -2单链表结构
例如:简单的链表结点定义: struct stud_node{ int num; char name[10]; struct stud_node *next; /*指针域,指向下一学生记录*/ }n1 = {1, "Zhang", NULL}, n2 = {2, "Wang", NULL}; n2.next = &n1; struct stud_node n3 = {3, "Huang", NULL}; n1.next = &n3;
内存分配方式
堆区分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意内存,程序员负责用free 或delete 释放内存。生存期由程序员决定,使用非常灵活。
如何使用一个大数组: 栈区分配? 静态区分配 && 堆区分配?
1
2
3
静态分配与动态分配的注意事项
1-2 内存分配方式
本质上看,链表就是一个结点的序列。
1
2
2 链表概述
2-1 结点的结构
data
next
数据域
指针域
图9-1 结点的结构
2 -1结点结构
2 -2单链表结构
以链表中第一个数据元素的存储地址作为链表的地址,称作链表的头指针
a1 a2 … ... an ^
头指针
int *p; p=(int *) malloc(100*sizeof(int)); for(i=0;i<100;i++) scanf(“%d”,&p[i]); …… freep(p);
动态内存分配
单链表概述
单链表结点的基本操作
单链表的建立
单链表的应用
循环链表与约瑟夫环问题
主要内容
链表是一种常用的动态数据结构,链表中的每一个元素称为结点 每个结点是类型相同的一个结构变量 与数组不同,每个结点的存储空间不一定相邻
3-2链表结点的插入
与结点的插入相类似,结点的删除也不需要作元素的移动,而只需要作指针的修改即可。
作为动态分配的存储结构,当链表的结点不再需要时,可以在删除结点时将其所占的内存空间释放。
要删除结点,首先要找到被删结点的前一个结点,然后再对这前一个结点实施指针的修改操作。
02
01
03
3-3链表结点的删除
int i, *p = (int *)malloc(6*sizeof(int)); if(p) for(i = 0; i < 6; i++) p[i] = i; else printf("dynamic alloc failed!");
1-3 C的动态内存分配函数
#include<stdio.h> #include<stdlib.h> int main() { int i,j; int *array=NULL; scanf("%d",&i); array=(int*)malloc(i*sizeof(int)); for(j=0;j<i;j++) scanf("%d",&array[j]); for(j=0;j<i;j++) printf("%d\n",array[j]); return 0; }
1-3 C的动态内存分配函数
realloc函数:
void * realloc(void * p_block, unsigned int size);
对指针所指向的已动态分配的存储空间重新进行动态存储分配 如果缩小空间则返回的地址是原地址; 如果是扩大空间,则有三种可能:
1-3 C的动态内存分配函数
原空间位置有足够的空间可以扩充,返回的地址与扩充前的地址相同;
01
如果原位置无足够的空间而其它位置有足够的空间,按照size指定的大小重新分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来指针所指内存区域,同时返回新分配的内存区域的首地址;
02
否则说明内存无足够的空间可以分配,分配失败,返回值为NULL。
数组由于是用地址连续的空间存放元素,利用数组名+下标的方式可以实现对元素的随机查找;
单链表是用地址离散的空间存放元素的,不能直接知道每一个结点的存放地址,只能从头指针所指结点开始逐个往后才能找到要访问的结点,因此也称单链表是一种“顺序存取”的结构。
3-1结点的查找
其中头指针提供了在链表中查找的起始位置;
单链表结点的基本操作
循环链表与约瑟夫环问题
01
C程序的内存划分
02
内存分配方式
03
动态内存分配函数
1 动态内存分配
栈区stack
静态区static
堆区heap
代码区code
数据区
代码区
memory
存放程序的代码
存放程序的全局数据和静态数据
存放程序动态申请的数据
存放程序的局部数据和参数
文字常量区:存放常量字符串,程序结束后由系统释放。
程序代码区:存放函数体的二进制代码。
1-1 C程序的内存划分
根据内存分配的时机,可分为:
根据内存分配区域的不同,又可以分为:
静态内存分配; 动态内存分配;
静态存储区分配; 栈区分配; 堆区分配;
1-2 内存分配方式
内存分配方式
静态存储区域分配。编译阶段完成分配,在程序结束后内存空间才会被释放。例如全局变量,static 变量。 栈区创建。在执行函数时,函数局部变量可在栈区创建,函数执行结束时自动释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
二、修改指针域
5
8
6
22
∧
head
10
p
p->next
∧
q
q->next=p->next; p->next=q;
3-2链表结点的插入
链表中结点的插入并不需要元素的移动,只需要作指针域的修改即可。 首先修改新结点的指针域,指向下一个元素;再修改前一个元素的指针域,指向新元素。 这两者的顺序是否可以颠倒?为什么?
/*数据域,存放一个学生记录*/
2-2单链表结构
执行n2.next = &n1;之前
1
Zhang
NULL
2
Wang
NULL
n1
n2
NULL
执行n2.next = &n1;之后
n1
n2
NULL
Zhang
1
Wang
2
执行n1.next = &n3;之后
NULL
Huang
3
n3
3单链表结点的基本操作
查找结点 插入结点 删除结点
空指针
head
p
p=head;
p=p->next;
p
p
一个指向结点的指针变量,依次指向链表的每一个结点,从而实现对每个结点的逐个访问,这样的指针变量被称为游动指针。
头指针与游动指针的变量定义: struct node *head,*p;
头结点
a1 a2 … ... an ^
head
∧
4-1逆序建链表
(2)在空链表头结点之后插入第一个结点。 需要执行的操作是: p=(struct node *) malloc ( sizeof ( struct node ) ); scanf(“%d”,&p->data); p->next=head->next; head->next=p;
1-3 C的动态内存分配函数
1-3 C的动态内存分配函数
#include<stdio.h> #include<stdlib.h> int main() { int i,j; int *array=NULL; scanf("%d",&i); array=(int*)malloc(i*sizeof(int)); for(j=0;j<i;j++) scanf("%d",&array[j]); for(j=0;j<i;j++) printf("%d\n",array[j]); free(array); return 0; }
静态内存分配把内存的控制权交给了编译器,而动态内存分配则把内存的控制权交给了程序员 动态内存管理水平严重依赖于程序员的水平,如果处理不当容易造成内存泄漏 动态内存的分配与释放需要占用额外的CPU资源 要避免频繁的使用动态内存分配
1-3 C的动态内存分配函数
1-3 C的动态内存分配函数
malloc函数:void * malloc(unsigned int size); 向系统申请分配指定字节数的存储空间 : 分配成功:返回新分配存储空间的首地址,新分配存储空间未被初始化; 分配失败:返回NULL 例如:
单链表的查找过程:
设置头指针与游动指针;
游动指针从该起始位置开始逐个往后“游走”,直到找到要找的结点或者到了链表结束停止查找。
3-1结点的查找
3-1结点的查找
单链表与数组的不同之处还体现在单链表是一个动态存储的结构,可以在需要时再分配空间,用完后马上释放空间。而且,与数组做元素插入删除需要移动元素不同,单链表无论是插入还是删除元素,都不需要做元素的移动,实现过程非常简洁。
03
如果是扩大空间,则有三种可能:
1-3 C的动态内存分配函数
1-3 C的动态内存分配函数
#include<stdio.h> #include<stdlib.h> int main() { int i; int *pn=(int *)malloc(5*sizeof(int)); printf("%p\n",pn); for(i=0;i<5;i++) scanf("%d",&pn[i]); pn=(int *)realloc(pn,100*sizeof(int)); printf("%p\n",pn); for(i=0;i<5;i++) printf("%3d",pn[i]); printf("\n"); free(pn); return 0; }
2
单链表的建立是一个结点插入的过程,只不过需要插入的不是一个结点,而是多个结点而已。
3
多个结点的插入方式是相同的,因此可以借助循环过程来实现。
4单链表的建立
顺序建链表。 逆序建链表。
01
4单链表的建立
02
4-1逆序建链表
(1)首先建立一个只包含头结点的空链表。 需要执行的操作是: head=(struct node *) malloc ( sizeof ( struct node ) ); head->next=NULL;