计算机软件基础讲义.docx
《计算机软件基础讲义.docx》由会员分享,可在线阅读,更多相关《计算机软件基础讲义.docx(63页珍藏版)》请在冰豆网上搜索。
计算机软件基础讲义
第—部分数据结构
第一章绪论
计算机应用相当普遍,计算机的应用已不再局限于科学计算,而更多用于控制、管理及数据处理等非数值计算的处理工作。
与此相应,计算机加工处理的对象由纯粹的数值发展到字符、表格和图像等各种具有一定结构的数据,这就给程序设计带来一些新的问题。
为了编写出一个好的程序,必须分析待处理的对象的特性以及各处理对象之间存在的关系。
这就是数据结构学科形成和发展的背景。
1.1数据结构
一般来说,用计算机解决一个问题时,需要经过如下几个步骤:
首先要从具体问题中抽象出一个适当的数学模型,然后设计一个对此数学模型进行操作的算法,最后编写出程序直至得到解答。
例l:
图书馆的书目检索系统。
当你想借阅一本参考书时,你需要到图书馆去查阅图书目录卡片。
如果利用计算机实现自动检索,则计算机处理的对象便是这些目录卡片上的书目信息,列在卡片上的一本书的书目信息可由登录号、书名、作者名、分类号、出版单位和出版时间等各项组成。
每一本书都有唯一的一个登录号。
在书目自动检索系统中建立一张按登录号顺序排列的书目文件,如图1.1,这个文件就是书目自动检索系统中的数学模型。
计算机的主要操作就是按照某个特定要求(如给定书名)对书目文件进行查询。
001
高等数学
樊映川
S01
……
002
理论力学
罗远祥
L01
……
003
高等数学
华罗庚
S01
……
004
线性代数
栾汝书
S02
……
…
…
…
…
…
图1.1
1.2基本概念
1.2.1数据
是对客观事物的符号表示,在计算机科学中是指所有能输入到计算机中并被计算机程序处理的符号的总称。
如数值、字符串、图像、声音都是数据。
1.2.2数据元素
是数据的基本单位,在计算机程序中通常作为一个整体进行考虑和处理单位,通常—个数据元素可由若干个数据项组成。
如书目文件中一本书的书目信息就是一个数据元素。
书目信息中的每一项(如书名、作者名)为一个数据项,数据项是不可分割的最小单位。
1.2.3数据对象
是性质相同的数据元素的集合,是数据的子集。
1.2.4数据结构
简单的说,是相互之间存在一种或多种特定关系的数据元素的集合。
数据结构没有一个明确的定义,它包括三个要素:
1.数据的逻辑结构
数据的逻辑结构抽象地反映数据元素之间的逻辑关系,而不管这种逻辑关系在计算机中是如何表示的。
数据的逻辑结构分为线性结构和非线性结构。
若各个数据元素之间的逻辑关系可以用一个线性序列简单的表示出来,则称之为线性结构,否则称为非线性结构。
如书目文件中
表示一个数据元素,书目文件可表示成(
……
),所以它是一个线性结构。
如图二:
数据元素之间的逻辑关系不能用一个线性序列表示出来,所以数据的逻辑结构是非线性结构。
图二
2.数据的存储结构
数据的存储结构是逻辑结构在计算机存储器里的实现。
数据的逻辑结构在存储器中的映像应包括数据元素自身值和数据元素之间关系的表示。
这样在存储器中,某个结点有两个域,一个是存放自身值的域,用标识符info表示这个域;另一个是存放该结点与其它结点关系的域,用标识符1ink表示这个域。
3.数据的运算
数据的运算是定义在数据的逻辑结构上的,但运算的具体实现要在存储结构上进行。
数据的各种逻辑结构都有相应的运算,常用的运算有检索、插入、删除、更新和排序等。
1.3主要的数据存储方式
数据之间的逻辑关系在计算机中有两种不同的表示方法:
顺序映像和非顺序映像,相应的得到两种不同的存储结构:
顺序存储结构和链式存储结构。
1.3.1顺序存储结构
把逻辑上相邻的数据元素存储在物理上相邻的存储单元里。
特点:
只有信息域,没有指针域。
可以通过计算直接确定第i个结点的存储地址
。
,其中
是第一个结点的存储地址,m是每个结点所占用的存储单元数。
插入操作和删除操作不方便。
1.3.2链式存储结构
链式存储结构就是每个结点至少包括一个指针域,用指针来体现数据元素之间的逻辑关系。
特点:
除了有信息域,还有指针域;逻辑上相邻,物理上不必相邻;插人操作和删除操作方便。
1.4C语言简介
第二章线性表
2.1线性表
1.定义线性表的逻辑结构是
个数据元素的有限序列,(
),其中
,
称为空表,
称为始结点,
称为终结点,其余的结点
有且仅有一个后继结点
,有且仅有一个前趋结点
。
2.线性表具有以下特性
(1)线性表中所有数据元素,其性质是相同的,即数据类型是一致的。
(2)数据元素之间的相对位置是线性的。
3.对线性表经常进行的一些操作
(l)查找操作
●查找第i个结点
●查找值为x的结点
(2)插入操作
●在线性表的第i个结点前面插入一个新结点
●在线性表的第i个结点后面插入一个新结点
●在线性表的值为x的结点前面插入一个新结点
●在线性表的值为x的结点后面插入一个新结点
(3)修改操作
●用新结点替换线性表中的第i个结点
●用新结点替换线性表中值为x结点
(4)排列操作
●按结点值递增的顺序重新排列线性表的结点
●按结点值递减的顺序重新排列线性表的结点
这是常见的对线性表进行的几种操作。
2.2线性表的顺序存储结构
用一连续的存储空间依次存储线性表的所有元素。
在C语言中顺序存储的线性表是一个一维数组,这样,在C语言中用如下形式来说明一个顺序存储的线性表。
intnode[max];
对顺序存储的线性表进行的操作主要有查找、插入和删除。
1.下面是顺序表的查找算法:
intfind(intx)
{
inti,findi;
intflag;
findi=0;
flag=0;
i=0;
WHILE((i<=last-1)&&(!
flag))
{
IF(node[i]==x)
{
findi=i;
flag=1;
}
ELSE
i=i+1;
}
IF(!
flag)
findi=-1;
RETURN(findi);
}
2.下面是顺序表的插入算法:
首先看一个例子如表,在第四个结点前面插人一个新结点。
首先把单元3到6之间的所有结点向后移动一个单元,空出一个位置,然后将x=100插入到该位置。
0
35
1
67
2
88
3
100
4
108
5
125
6
140
7
215
8
9
10
0
35
1
67
2
88
3
108
4
125
5
140
6
215
7
8
9
10
0
35
1
67
2
88
3
4
108
5
125
6
140
7
215
8
9
10
下面是在第i个结点之前插入一个新结点的算法:
voidinsert(intp,intx)
{
inti;
IF(last==max)/*max是表的最大长度*/
printf(“listisfull”);
ELSE
{
FOR(i=last-1;i>=p;i--)
node[i+1]:
=node[i];
node[p]:
=x;
last:
=last+1;
}
}
3.下面是顺序表的删除算法
首先看一个例子如表,将3号单元结点删除,把单元4到6之间的所有结点向前移动一个单元。
0
35
1
67
2
83
3
108
4
125
5
140
6
215
7
8
9
10
0
35
1
67
2
88
3
125
4
140
5
215
6
7
8
9
10
下面是删除一个结点的算法:
voiddelete(intp)
{
inti;
IF((p>last-1)||(p<0))
printf(“positioniswrong”);
ELSE
{
FOR(i=p+1;i<=last-1i++)
node[i-1]:
=node[i];
last:
=last-l;
}
}
2.3线性表的链式存储结构
1.单链表
线性表的另外一种存储结构是利用指针把线性表中的各个元素依次链接起来形成一个单向链接表。
在这种表示法中,一个线性表由若干结点组成,每个结点含有两个域(信息域和指针域)。
信息域:
它用来存放表中的一个元素;
指针域:
用来存放指向下一个结点的指针。
设线性表为
,则存放
的结点中含有一个指向
的指针,其中
,存放
的结点的指针为nil。
图2.2
有的单向链接表设一个特殊的表头结点,它的信息域不含有表中的任何元素,而指针域为指向表中第一个结点的指针,如图2.2。
在C语言中,结点的类型定义如下:
structnode
{
intdata;
structnode*next;
}
单向链接表的主要运算有:
插入和删除。
单向链接表在插入和删除操作时,首先考虑的问题是新结点从何处取,删除的旧结点送往何处去。
计算机存储器中,设置一个空间称为可利用空间,在获得一个新结点时,从可利用空间中取结点,调用语句p=(structnode*)malloc(sizeof(structnode))来实现。
在删除一个结点时,将空结点归还给可利用空间,调用过程free(p)来实现。
在单链表中插入一个结点,不需移动线性表中其它结点。
插入操作的最大问题是找插入位置。
插入位置可能有四种情况(不设特殊表头结点)。
(1)原来的链表是空表,则插入的结点为表头结点;
(2)插入位置在表中第一个结点之前,则插入的结点为新的表头结点;
(3)插人的位置在表的中间;
(4)如果链表中根本不存在所指定的结点,则把新结点作为新的表尾。
下面是对单向链接表进行插人操作的算法:
在单向链接表中,值为
的结点前面插入一个新结点。
(structnode*)insertlink(structnode*head;inta,intb)
{structnode*q,p,s;
s=(structnode*)malloc(sizeof(structnode));
s—>data=b;
IF(head==NIL)
{
head=s;
s—>next=NIL;
}
ELSE
{
IF(head—>data==a)
{
s—>next=head;
head=s;
}
ELSE
{
p=head;
WHILE((p—>data<>a)&&(p—>next!
=NIL))
{
q=p;
p=p—>next;
}
IFP—>data=a
{
q—>next=s
s—>next=p;
}
ELSE
{
p—>next=s;
s—>next=p;
}
}
}
RETURE(head);
}
下面是对单向链接表进行删除操作的算法:
从单向链接表head中删去值为
的结点;如果找不到,则打印出错信息。
(structnode*)deletelink(structnode*head;inta)
{structnode*q,p;
IF(head==NIL)
printf(“thisisaemptylist”)
ELSE
{
IF(head—>data==a)
{
p=head;
head=head—>next;
free(p);
}
ELSE
{
p=head;
WHILE((p—>data<>a)&&(p—>next!
=NIL))
{
q=p;
p=p—>next;
}
IFP—>data=a
{
q—>next=p—>next;
free(p);
}
ELSE
printf(“nothisnodeinthelist”)
}
}
RETURE(head);
}
2.双链表
在单链表中通过每一个结点的指针域只能查到该结点的后继结点,不能查到该结点的前趋结点,这样一些操作很不方便,如果采用双链表,就可以克服上述缺点。
在双链表中,每一个结点有两个指针域,有一个线
性表(
),它对应的双链表的形式如下:
图2.3
在C语言中,结点的类型定义如下:
structnode
{
intdata;
structnode*llink;
structnode*rlink;
}
设某个结点的地址为p,
它的前趋结点的地址是p—>llink,
它的后继结点的地址是p—>rlink,
它的信息域的内容是p—>data。
对双链表的操作有插入和删除,在地址为p的结点后面插人一个新结点,算法如下:
voidinsert(structnode*p,intx)
{
structnode*r,*q;
IF(p==nil)
printf(‘voidinsert’);
ELSE
{
q=(structnode*)malloc(sizeof(structnode));
q—>data=x;
r=p—>rlink;
r—>llink=q;
q—>rlink=r;
q—>llink=p;
p—>rignt=q;
}
}
删除地址为p的结点算法如下:
voiddelete(structnode*p)
{
structnode*l,*r;
IF(p==nil)
printf(“voiddelete”);
ELSE
{
1=p—>llink;
r=p—>rlink;
l—>rlink=r;
r—>llink=1;
free(p);
}
}
在双链表上插人和删除结点的操作如图2.4和图2.5。
图2.4
图2.5
2.3栈和队列
无论线性表的链式存储结构还是线性表的顺序存储结构,插入和删除运算都比较麻烦。
在顺序结构中将导致数据元素的大量移动,在链式结构中要逐个结点查询,把插入和删除操作限制在一端进行。
2.3.1栈
限定只能在一端进行插入和删除操作的线性表为栈。
进行插入和删除的一端为栈顶,表的另一端为栈底。
栈的操作在日常生活中经常见到。
栈的存储结构有两种:
栈的线性存储结构:
用stack[n]表示栈,n是栈中允许最多的元素的个数,top指向栈顶元素所在的位置,stack[top]表示栈顶元素,当top=-1时表示栈空,当top=n-1时表示栈满,栈空时不能做出栈操作,栈满时不能作入栈操作。
栈的顺序存储的算法
voidinstack(inttop,intx)
{
if(top==n-1)
printf(“overflow”)
else
{
top=top+1;
stack[top]=x
}
}
voidoutstack(inttop,)
{
inty;
if(top==-1)
printf(“underflow”);
else
{
y=stack[top];
top=top-1;
}
}
栈的链式存储结构:
栈的入栈和出栈操作实际是对链表的表头进行插入和删除操作。
用top表示一个栈的栈顶指针。
structnode
{
intdata;
structnode*next;
}
下面给出两个过程,其中inlinks的功能是将元素x插入到栈顶;outlinks的功能是删除栈顶元素,并将此元素送给y。
(structnode*)inlinks(structnode*top,intx)
{structnode*q;
q=(structnode*)malloc(sizeof(structnode));
q—>data=x;
q—>next=top;
top=q;
return(top);
}
(structnode*)outlinks(structnode*top)
{inty;
structnode*q;
IF(top==nil)
printf(“underfIow”);
ELSE
{
q=top;
y=q—>data;
top=q—>next;
free(q);
return(top);
}
}
2.3.2队列
队列是限定在一端进行插入操作,在另一端进行删除操作的线性表。
插入的一端为队尾,删除的一端为队首。
队列的存储结构有两种:
队列的顺序存储结构,队列的链式存储结构。
1.队列的线性存储结构:
队列可以用一个向量q[m]表示,front、rear分别表示排头指针和排尾指针;front指向实际排头元素的前一个位置(表格中用指针F表示),rear指向实际排尾元素所在的位置(表格中用指针R表示)。
初始条件是F=R=-1。
下面看一下队列进排出排的情况。
可以看出队列上溢的条件是R=m-1,而下溢的条件是R=F。
从图2.6中看出,当R=4时若有
需要加人队列中,这时满足上溢条件,拒绝进入队列,但此时队列还有三个空额,这种现象称为假溢出。
如何避免假溢出现象呢?
我们把队列看成一个循环表,假设q[m]为循环数组,则视q[0]接在q[m-1]之后形成循环。
如图2.7
假定循环队列以顺时针方向伸长,头指针总是在顺时针方向落后于队列中的第一个元素的一个位置,尾指针指向最后加入的元素位置。
初始状态(队列为空)时front=rear=m-1,每加入一个元素时,尾指针向顺时针方向移动一个位置,即
rear=rear+1;
ifrear=m;
thenrear=1;
同样,每删除一个元素时,头指针便向顺时针方向移动一个位置。
front=front+1;
iffront=m;
thenfront=1;
在循环队列中.如何判别是空还是满呢?
当队列为空或满时,都有front=rear,一种办法就是尾指针从后面追上头指针,作为队列满的特征;另一种办法设一个标志位以区别是空还是满。
在C语言中定义队列类型:
structqueue
{
intitem[max];
intfront;
intrear;
}
structqueueq;
其中数据项item定义一个容量为0~max-1顺序分配的队列,队列中结点的数据是整数,q.front和q.rear分别是排首和排尾的指针。
其中,item[max]:
item[0]…item[max-1];
初始有:
q.front=q.rear=m-1,q.rear=-1;
下面的两个程序insersq和removesq,描述了在用数组表示的顺序分配的队列中,执行进排和出排操作算法。
在insersq中它是把数据x添加到已知的队列中去。
在removesq中,它把当前队列中的排首结点出排,并把出排结点的数据存人变量y中。
设q.rear=-1表示栈空,初始时有q.rear=-1,q.front为0~max-1之间的任意一个值。
顺序分配的队列上的进排操作算法如下:
voidinsertq(structqueueq,intx)
{
if(q.rear==q.front)
printf(“queueisfull”);
else
{
if(q.rear==-1)
q.rear=q.front;
if(q.rear==max-1)
q.rear=0;
else
q.rear=q.rear+1
q.item[q.rear]=x;
}
}
顺序分配的队列上的出排操作算法如下:
voidremoveq(structqueueq)
{inty;
if(q.rear==-1)
printf(“queueisempty”);
else
{
if(q.front==max-1)
q.front=0;
else
q.front=q.front+1;
y=q.item[q.front];
if(q.front==q.rear)
q.rear=-1;
}
}
2.队列的链式存储结构:
在队列的链接表示法中,每个结点由两个域data和next组成,还有两个指针变量front和rear来指示队列的排首和排尾,如图2.8。
在队列上执行进排操作,实际上是在rear指向的链接表的表尾处插入一个结点,出排操作实际是在front指向的链接表的表首删除一个结点,一旦front=nil,即发出队列为空的信息。
图2.8
链接分配队列上的插入操作算法:
voidinsertlq(structnode*front,structnode*rear,intx)
{
structnode*p;
p=(structnode*)malloc(sizeof(structnode));
p—>data=x;
p—>next=nil;
if(front==nil)
front=p;
else
rearnext=p;
rear=p;
}
链接分配队列上的删除操作算法:
voidremovelp(structnode*front,structnode*rear)
{
inty;
structnode*p;
if(front==nil)
printf(“queueisempty”);
else
{
p=front;
front=pnext;
y=pdata;
free(p);
}
}
第三章树
树形结构是一类重要的非线性结构
3.l树及二叉树
3.1.1树的定义
树是一个结点或多个结点有限集合
,有一个特定的结点称为根,其余的结点分为
个不相交集合
,每个集合又是一棵树,称为子树。
如图3.1:
图3.1
子女:
结点的各个子树的根称为该结点的子女;
双亲:
该结点是其子女的双亲;
兄弟:
具有相同双亲的结点称为兄弟;
结点的度:
一个结点子树的个数;
叶:
度为0的结点为叶结点;
分支结点:
度不为0的结点为分支结点;
结点的层数:
根结点的层数为0,其余结点的层数等于其双亲结点的层数加1;
树林或森林:
0棵或多棵不相交的树的集合。
3.1.2二叉树的定义
二叉树是结点的有限集合,这个集合或者为空,或者由一个根结点及两棵不相交的称为根的左子树和右子树的二叉树组成,注意二叉树不是树的特殊情况,二叉树的子树有左右之分,而树没有左右之分。
如图3.2:
图3.2
.完全二叉树