数据结构教案Word格式文档下载.docx
《数据结构教案Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《数据结构教案Word格式文档下载.docx(32页珍藏版)》请在冰豆网上搜索。
1.算法:
解决问题的方法和步骤。
2.算法的描述方法
框图
非形式语言:
如中文
类C语言程序
C语言程序
1.4算法分析
1.对同一问题,可以设计多种不同的算法,但必有一种算法的时间效率最高。
2.估算一个算法的运行时间
确定问题的输入规模n。
根据问题的特点,选择一种操作作为“标准操作”。
(通常以条件判断或赋值语句为标准操作)
确定在给定输入下共执行多少次标准操作,从而算出运行时间T。
3.算法的时间复杂度
对算法的运行时间T(n),忽略所有的常数、低次项,忽略最高项的系数,称为算法的时间复杂度,以O表示。
运行时间
时间复杂度
T(n)=c
常数阶O
(1)
T(n)=cn
线性阶O(n)
T(n)=cn2
平方阶O(n2)
指数阶O(
)
对数阶O(
1.5指针和结构
一、什么是指针
1.存储单元的地址
每一个存储单元由一个或多个字节组成,存储单元中第一个字节的编号称为存储单元的地址。
2.什么叫指针?
指针总是指向某个变量。
指针的值是所指向变量的地址,指针的类型是所指向变量的类型。
二、指针变量
1.指针变量的定义
类型*指针变量名;
例:
int*p;
解释:
定义一个指针p,它只能指向int型变量。
2.两个运算符
&
:
取地址运算符,例&
i
*:
指针运算符,例*p
int*p,i=3;
p=&
i;
printf("
%d,%d\n"
i,*p);
说明:
①&
和*互为逆运算,即:
*p=p,*&
i=i
②定义指针变量时,指针变量名前面的“*”不是指针运算符。
指针可以与整数进行加、减运算。
指针±
n=指针的原值±
sizeof(指针的类型)×
n
同类型的两个指针可以相互赋值。
三、指针与数组
1.数组名代表该数组的首地址,例a==&
a[0]
2.设inta[6],则
a[i],*(a+i)是等价的
a[i],a+i是等价的
3.表示数组元素的方法
下标法:
例a[i]
指针法:
例*(a+i)
4.设指针p指向数组a的某一个元素,则p++:
使p指向数组的下一个元素;
四、结构
1.定义结构类型
struct结构名
{成员定义列表}
structperson
{intno;
charname[6];
};
2.定义结构变量
structpersonx;
1.引用结构变量的成员
结构变量名.成员名
2.结构变量的初始化
3.结构指针
已知structpersonx,*p;
x;
则表示x的no成员有三种形式:
x.no,p->
no,(*p).no
第2章线性表
2.1线性表的定义
1.线性表的表示形式:
L=(a1,a2,a3,…,an)
2.线性表的基本操作
每种操作都采用一个函数来完成,这些函数是自定义函数,使用之前必须先定义。
2.2线性表的顺序存储结构
一、顺序表的类型定义
顺序表实际是一个结构变量,包括两个域:
datas:
存放线性表的元素,last:
存放线性表的长度。
typedefstruct
{类型datas[maxsize];
intlast;
}sequenlist;
sequenlistL;
二、为线性表L=('
a'
,'
b'
c'
d'
,……)创建一个顺序表,要求L的第1个元素存入数组的1号元素中。
typedefstruct
{chardatas[20];
intlast;
voidmain()
{sequenlistL;
charch;
inti=1;
ch=getchar();
while(ch!
='
\n'
{L.datas[i]=ch;
i++;
}
L.last=i-1;
for(i=1;
i<
=L.last;
i++)
%4c"
L.datas[i]);
\n"
);
}
三、基本操作在顺序表上的实现
1.insert(a,x,i):
将元素x插入到顺序表a的第i号元素之前
2.delete(a,i):
删除顺序表a的第i号元素
第3章链式存储结构
3.1线性表的链式存储结构
一、顺序表的优缺点
优点:
空间利用率高,可以随机读取表中任一元素。
缺点:
插入、删除操作要移动大量的数据,时间性能差。
二、单链表
1.单链表的组成
每个单链表由多个结点组成,每个结点包含两个域:
数据域data:
存放线性表的元素
指针域next:
存放下一个结点的地址
2.单链表的类型定义
typedefstructnode
{类型data;
structnode*next;
}linklist;
linklist*head;
不带头结点的单链表为空的条件:
head==null
带头结点的单链表为空的条件:
head->
next==null
3.单链表的建立(尾插入法)
为L=('
,……)创建单链表。
#include"
malloc.h"
stdio.h"
{chardata;
{charch;
//定义三根指针,head指向头结点,t指向新产生的结点,last指向最后的结点
linklist*head,*t,*last;
t=malloc(sizeof(linklist));
t->
next=NULL;
head=t;
last=t;
{t=malloc(sizeof(linklist));
data=ch;
last->
next=t;
last=t;
}
4.单链表的插入需设置两支指针:
p、t。
p:
指向待插入结点的前一个结点
t:
指向新产生的结点
5.单链表的删除需设置两支指针:
指向待删除结点的前一个结点
指向待删除结点
三、其它链表
单链表
单向循环链表(循环链表)
双向循环链表(双向链表)
1.循环链表
最后一个结点的指针域不是NULL,而是指向头结点。
2.双链表
每个结点包含三个域:
一个数据域和两个指针域。
双链表的特点是找结点的前趋和后继都很容易。
第4章栈和队列
4.1栈
一、栈的定义
1.基本概念
栈顶、栈底、进栈、出栈、空栈
2.栈的表示形式
S=(a1,a2,a3,…,an)
按a1,a2,a3,…,an顺序进栈,但按an,…,a3,a2,a1顺序出栈。
a1称为栈底元素,an称为栈顶元素。
栈又称后进先出线性表(LIFO)表。
二、栈的顺序存储结构
1.顺序栈的类型定义
顺序栈实际上是一个结构变量,包括两个域:
data:
存放栈中元素,top:
存放栈顶元素所在单元的编号。
typedefstruct
{类型data[maxsize];
inttop;
}seqstack;
seqstacks;
栈空条件:
s.top=0;
栈满条件:
s.top=maxsize-1
2.为S=('
,……)创建一个顺序栈,要求S的第1个元素存入数组的1号元素中。
{chardata[20];
{seqstacks;
{s.data[i]=ch;
s.top=i-1;
=S.top;
%-4c"
s.data[i]);
三、栈的链式存储结构
1.链栈的类型定义
}linkstack;
linkstack*top;
链栈总是以栈顶指针top开头,top用于标识整个链栈。
链栈只会出现栈空情况,栈空条件为:
top==NULL。
2.链栈的建立(头插入法)
为S=('
,……)创建一个链栈。
{linkstack*top,*t;
top=NULL;
{t=malloc(sizeof(linkstack));
next=top;
top=t;
printf("
出栈顺序为:
while(top->
next!
=NULL)
{printf("
top->
data);
top=top->
next;
4.2队列
一、队列的定义
队头、队尾、空队
2.队列的表示形式:
Q=(a1,a2,a3,…,an)
按a1,a2,a3,…,an顺序进队,仍按a1,a2,a3,…,an顺序出队。
队列又称先进先出线性表(FIFO表)
二、队列的顺序存储结构
1.顺序队的类型定义
顺序队实际上是一个结构变量,包括三个域:
存放队列的元素;
front:
存放队头元素所在单元的前一个单元的编号;
rear:
存放队尾元素所在单元的编号。
intfront,rear;
}seqqueue;
seqqueueq;
2.顺序队的建立
为Q=('
,……)创建一个顺序队。
{seqqueueq;
inti=0;
{q.data[i]=ch;
q.front=-1;
q.rear=i-1;
//输出队列元素
for(i=0;
=q.rear;
q.data[i]);
3.顺序队的队空、队满
队空条件:
q.front=q.rear
队满条件:
q.rear=maxsize-1
队真满:
q.front=-1;
q.rear=maxsize-1
队假满:
q.front≠-1;
q.rear=maxsize-1
4.顺序队的插入、删除操作
插入新元素:
rear后移而front不变
q.rear=q.rear+1;
q.data[q.rear]=x;
删除元素:
front后移而rear不变
q.front=q.front+1;
三、循环队
1.为充分利用存储空间,克服“假满”,可以把数组看作首尾相接的圆环,形成“循环队”。
2.循环队的性质
存储单元的编号从0开始,按顺时针方向,编号逐渐增大,最后一个存储单元的编号为maxsize-1。
在循环队中,当q.rear=maxsize-1时,只要数组有两个以上的存储单元为空,就可以把新元素插入到空单元中。
当队列中元素的个数为maxsize-1时,就认为队满。
3.循环队的插入、删除操作
rear顺时针移动而front不变
q.rear=(q.rear+1)%maxsize;
front顺时针移动而rear不变
q.front=(q.front+1)%maxsize;
4.循环队的队空、队满
(q.rear+1)%maxsize=q.front
四、队列的链式存储结构
1.链队的类型定义
链队是一个含有队头指针front和队尾指针rear的单链表;
②front指向队头结点的前一个结点,rear指向队尾结点;
链队由包含front和rear的结构变量lq标识。
typedefstructnode_st
structnode_st*next;
}node;
{node*front;
node*rear;
}linkqueue;
linkqueuelq;
2.链队的建立(尾插入法)
,……)创建一个链队。
linkqueuelq;
node*p;
p=malloc(sizeof(node));
p->
lq.front=p;
lq.rear=p;
{p=malloc(sizeof(node));
lq.rear->
next=p;
//输出队列中的元素
p=lq.front->
while(p!
{printf("
p->
p=p->
3.链队的队空条件
lq.front=lq.rear
4.各式链式存储结构比较表
有无头结点
用何指针标识
创建办法
链表
有
头指针head
尾插入法
链栈
无
栈顶指针top
头插入法
链队
由包含front和rear的结构变量lq标识。
第6章树和二叉树
6.1树的定义和基本操作
一、树型结构和线性结构
树型结构:
每个结点可以有多个直接后继
每个结点只有一个直接后继
二、树的定义
树是n(n≥0)个结点的有限集合,任意一棵非空树满足:
有且只有一个根结点;
其余结点被分成若干个互不相交的集合,每个集合又是一棵树。
三、树的特点
除根结点外,每个结点有且只有一个直接前趋;
除最底层的结点外,每个结点可以有多个直接后继;
若某棵树有多个结点,则每个结点可以看作根结点,要么是整棵树的根结点,要么是某棵子树的根结点。
四、基本术语
结点的度、树的度
叶子结点、分支结点
度为0的结点称为叶子结点;
度大于0的结点称为分支结点。
孩子结点、双亲结点、兄弟结点
具有同一双亲的结点互为兄弟
结点的子孙、结点的祖先
结点的层数、树的高度
结点的层数:
从树根开始算起,根的层数为1;
树的高度:
树中所有结点层数的最大值。
6.2二叉树
一、二叉树的定义:
参考P73
二、二叉树的性质
(1)二叉树的第i层上最多有
个结点。
(2)深度为k的二叉树最多有
(3)满二叉树:
除最底层的结点外,其余结点的度均为2的二叉树。
(4)完全二叉树:
如果对一棵满二叉树的最底层从最右边开始,连续删去若干个结点,就得到完全二叉树。
(5)对一棵完全二叉树的结点进行编号,则对编号为i的结点,其左孩子的编号为2i,右孩子的编号为2i+1,双亲结点的编号为
三、二叉树的存储结构
1.顺序存储结构:
◆先将二叉树的结点依次编号,再将结点存入一维数组中,数组元素的序号对应结点的编号。
◆对二叉树的结点进行编号,编号原则是:
根结点的编号为1。
对于编号为i的结点,其左孩子的编号为2i,右孩子的编号为2i+1。
◆满二叉树、完全二叉树一般采用顺序存储结构,一般二叉树则采用链式存储结构。
2.链式存储结构:
二叉链表:
每个结点包括三个域:
数据域data,左指针域lchild,右指针域rchild
对二叉树的访问只能从根指针root开始,二叉树为空的条件:
root=NULL。
四、二叉树的遍历
1.什么叫二叉树的遍历?
按照一定规律访问二叉树的所有结点,使得每个结点均被访问一次且仅被访问一次。
2.二叉树由三部分组成:
根结点、左子树、右子树
3.三种遍历次序
先根遍历:
中根遍历:
左子树、根结点、右子树
后根遍历:
左子树、右子树、根结点
6.3树和森林
一、对树中各结点编号
从根结点开始,按层依次编号,且根结点的编号为0。
二、树的存储结构
双亲链表
孩子链表
孩子兄弟链表
1.双亲链表
(1)每个结点包含两个域名:
数据域:
存放该结点的数据元素
指针域:
存放该结点之双亲的编号
(2)将所有结点组织成一维数组,并以各结点的编号作为数组元素的序号。
2.孩子链表
(1)为每个结点建立一个“孩子链表”。
(2)结点x的孩子链表是一个带头结点的单链表,用于存储该结点的所有孩子的编号。
(3)将所有头结点组织成一维数组。
3.孩子兄弟链表
(1)每个结点含有三个域:
孩子域:
用于指向该结点的第一个孩子
兄弟域:
用于指向该结点的第一个兄弟
(2)二叉树的二叉链表与树的孩子兄弟链表在组织结构完全相同。
二叉链表
孩子兄弟链表
数据域
左指针域
孩子域
右指针域
兄弟域
三、树与二叉树的转换
1.树转换为二叉树
将树转换为二叉树,只要将树中各结点的第一个孩子看作左孩子,第一个兄弟看作右孩子即可。
任一棵树对应的二叉树的右子树必空。
2.森林转换为二叉树
将每棵树先转换为二叉树B1,B2,…,Bn
以B1为基准,将B2作为B1根结点的右子树,将B3作为B2根结点的右子树,…
3.二叉树转换为森林
将二叉树根结点的右子树撤去,得到多棵二叉树B1,B2,…,Bn
将二叉树分别转换为树T1,T2,…,Tn。
四、树的遍历
先根遍历:
根结点、各棵子树
后根遍历:
各棵子树、根结点
层次遍历
6.4哈夫曼树和判定树
一、基本术语
1.叶子结点的路径长度:
从根结点到某个叶子结点所经过的分支数。
2.树的路径长度:
树中各叶子结点的路径长度之和。
3.叶子结点的权:
各叶子结点出现的概率。
4.带权路径长度(WPL)
各个叶子结点的权wi与相应的路径长度li乘积之和,称为树的带权路径长度。
二、哈夫曼树
1.什么叫哈夫曼树?
带权路径长度WPL最小的二叉树,称为哈夫曼树。
特点:
一般地说,权值越大的叶子结点离根越近。
哈夫曼树的时间性能最好,是最优的二叉树。
哈夫曼树中各结点的度只能是0或2。
具有n个结点的哈夫曼树共有2n-1个结点。
2.如何构造一棵哈夫曼树?
(参考P88)
3.哈夫曼编码
对一棵哈夫曼树约定:
指向左孩子的分支表示为0,指向右孩子的分支表示为1。
取从根到叶子结点一路上的“0”或“1”组成的序列,称为叶子结点的前缀编码。
三、分类和判定树
1.用于描述分类问题的二叉树称为判定树。
判定树的每个分支结点对应一种判断,每个叶子结点对应一种分类结果。
2.一个分类问题对应着若干棵判定树,其中必有一棵判定树的WPL最小,WPL又称平均比较次数。
3.一棵判定树对应着一种算法,哈夫曼树对应的算法的时间性能最好。
4.如何对一个分类问题写最优的算法?
①对分类结果画哈夫曼树;
②根据哈夫曼树写算法;
第7章图
7.1图的定义和术语
一、图的定义
图G由顶点集V和边集E组成,记为G=(V,E)。
最简单的图只有一个顶点;
每条边由其连接的两个顶点表示:
无向边(v1,v2);
有向边<
v1,v2>
,<
v2,v1>
二、术语
1.邻接点
若顶点vi,vj存在一条边,则vi,vj互为邻接点。
在有向边<
vi,vj>
中,称vi为起点,vj为终点。
2.顶点的入边,出边
若存在一条有向边<
,则称它为vi的出边,vj的入边。
3.顶点的入度,出度
顶点的度:
与顶点v相关联的边数,记为D(v);
顶点的