6. 算法与程序:
( 1 )算法的 5 个特性
a 、 输入:
有零个或多个输入
b 、 输出:
有一个或多个输出
c 、有穷性:
要求序列中的指令是有限的;每条指令的执行包含有限的工作量;整个指令序列的执行在有限的时间内结束。
(程序与算法的区别在于,程序不需要有有穷性)
d 、确定性:
算法中的每一个步骤都必须是确定的,而不应当含糊、模棱两可。
没有歧义。
e、可行性:
算法中的每一个步骤都应当能被有效的执行,并得到确定的结果。
( 2 ) . 算法设计的要求:
1 、正确性(达到预期效果,满足问题需求)
2 、健壮性(能处理合法数据,也能对不合法的数据作出反应,不会产生不可预期的后果)
3 、可读性(要求算法易于理解,便于分析)
4 、可修改可扩展性
5 、高效率(较好的时空性能 )
✧补充内容:
1 、名词解释:
数据结构、二元组
数据结构就是相互之间存在一种或多种特定关系的数据元素的集合。
二元组就是一种用来表示某个数据对象以及各个元素之间关系的有限集合。
2 、根据数据元素之间关系的不同,数据的逻辑结构可以分为集合、线性结构、树形结构和图状结构四种类型。
3 、常见的数据存储结构一般有两种类型,它们分别是顺序存储结构、链式存储结构
4. 以下说法中,正确的是( D )
A. 数据元素是数据这个集合中的个体
B. 数据元素均由数据项组成
C .数据项是数据的基本单位
D. 数据元素是数据的最小单位
5. 以下有关抽象数据类型的描述中,正确的是( B )
A. 抽象数据类型是一个值的集合
B. 抽象数据类型是数据的逻辑结构及操作的组合
C. 抽象数据类型的操作可以没有操作结果
D. 抽象数据类型只能够用 C 语言来描述
6. 在一般情况下,一个算法的时间复杂度是问题规模的函数
7. 常见时间复杂度有:
常数阶 O ( 1 )、线性阶 O ( n )、对数阶 O ( log2n )、平方阶 O(n^2) 、指数阶 O(2^n) 。
通常认为,具有常数阶量级的算法是好算法,而具有指数阶量级的算法是差算法。
第二章 线性表
定义:
线性表是 n 个数据元素的有限序列。
一个数据元素可由若干个数据项组成。
1. 顺序表结构
线性表的顺序存储是指在内存中用地址连续的一块存储空间顺序存放线性表的各元素,用这种存储形式存储的线性表称为顺序表。
2. 单链表
( 1 ) 链表结点结构
线性表中的数据元素可以用任意的一组存储单元来存储,用指针表示逻辑关系逻辑相邻的两元素的存储空间可以是不连续的。
( 2 ) 链表操作算法:
初始化、插入、输出、删除、遍历
初始化:
p=(structstudent*)malloc(sizeof(structstudent));
插入:
p->next=head->next; head->next=p;
输出:
printf(“%d”,p->data);
删除:
q=p->next; p->next=q->next; free(q);
结点遍历:
for(p=head;p;p=p->next);
✧补充内容:
1 、线性表中,第一个元素没有直接前驱,最后一个元素没有直接后驱。
2 、在一个单链表中,若 p 所指结点是 q 所指结点的前驱结点,则删除结点 q 的操作语句为
P->next=q->next; free(q);
3 、在长度为 N 的顺序表中,插入一个新元素平均需要移动表中 N/2 个元素,删除一个元素平均需要移动( N-1 ) /2 个元素。
4 、若线性表的主要操作是在最后一个元素之后插入一个元素或删除最后一个元素,则采用顺序表存储结构最节省运算时间。
5 、已知顺序表中每个元素占用 3 个存储单元,第 13 个元素的存储地址为 336 ,则顺序表的首地址为 300 。
(第 n 个元素的地址即首地址 +(n-1)* 每个元素的存储空间,如 a[12] (第 13 个元素)的地址 =a[0]+12*3 )
6 、设有一带头结点单链表 L ,请编写该单链表的初始化,插入、输出和删除函数。
(函数名自定义)
&结点定义:
typedefintdatatype; // 结点数据类型,假设为 int
typedefstructnode{ // 结点结构
datatypedata;
structnode*next; // 双向链表还应加上 *previous
}Lnode,*pointer;// 结点类型 , 结点指针类型
typedefpointerlklist; // 单链表类型,即头指针类型
&初始化:
lklistinitlist(){
pointerhead;
head=newnode;// 这是 C++ 做法
//head=(pointer)malloc(sizeof(Lnode)); 这是 C 语言做法
head->next=NULL; // 循环链表则是 head->next=head;
// 双向链表应加上 head->previos=NULL;
returnhead;
}
&插入:
( C 语言中需要把 head 转化为全局变量才能实现此程序)
intinsert(lklisthead,datatypex,inti){
pointerq,s;
q=get(head,i-1); // 找第 i-1 个点
if(q==NULL) // 无第 i-1 点,即 i<1 或 i>n+1 时
{
cout<<” 非法插入位置 !
\n”;// 这是 C++ 做法,即 C 语言中的 printf(“ 非法插入位置 !
\n”);
return0;
}
s=newnode;// 生成新结点 即 C 语言中的 s=(pointer)malloc(sizeof(Lnode));
s->data=x;
s->next=q->next; // 新点的后继是原第 i 个点
q->next=s; // 原第 i-1 个点的后继是新点
return1; // 插入成功
}
&删除:
( C 语言中需要把 head 转化为全局变量才能实现此程序)
intdelete(lklisthead,inti){
pointerp,q;
q=get(head,i-1); // 找待删点的直接前趋
if(q==NULL||q->next==NULL) // 即 i<1 或 i>n 时
{cout<< ”非法删除位置 !
\n ” ;return0;}
p=q->next; // 保存待删点地址
q->next=p->next; // 修改前趋的后继指针
deletep; // 释放结点 即 C 语言中的 free(p);
return1; // 删除成
1. 不带头结点的单链表 head 为空的判定条件是 (A)
A. head=NULL B. head->next=NULL C. head->next=head D. head!
=NULL
2. 带头结点的单链表 head 为空的判定条件是 (B)
A. head=NULL B. head->next=NULL C. head->next=head D. head!
=NULL
3. 在一个单链表中,若 p 所指结点不是最后结点,在 p 之后插入 s 所指结点,则执行 (B)
A. s->next=p; p->next=s; B. s->next=p->next; p->next=s;
C. s->next=p->next; p=s; D. p->next=s; s->next=p;
4. 在一个单链表中,若删除 p 所指结点的后续结点,则执行 (A)
A. p->next=p->next->next;
B. p=p->next; p->next=p->next->next;
C. p->next=p->next
D. p=p->next->next
5. 从一个具有 n 个结点的有序单链表中查找其值等于 x 结点时,在查找成功的情况下,需平均比较( B )个结点。
A.n B.n/2 C.(n-1)/2 D.O(n ㏒ 2n)
6. 给定有 n 个元素的向量,建立一个有序单链表的时间复杂度( B )
A.O
(1) B.O(n) C.O(n2) D.O(n ㏒ 2n)
7. 在一个具有 n 个结点的有序单链表中插入一个新结点并仍然有序的时间复杂度是 (B)
A.O
(1) B.O(n) C.O(n2) D.O(n ㏒ 2n)
8. 在一个单链表中删除 q 所指结点时,应执行如下操作:
q=p->next;
p->next=(p->next->next);
free(q);// 这种题目靠一根指针是没有办法完成的,必须要借助第二根指针。
9. 在一个单链表中 p 所指结点之后插入一个 s 所指结点时,应执行:
s->next=(p->next)
p->next=(s) 操作。
10. 对于一个具有 n 个节点的单链表 , 在已知所指结点后插入一个新结点的时间复杂度是 (O ( 1 ) ) ;在给定值为 x 的结点后插入一个新结点的时间复杂度是( O(n) )。
11. 问答题
线性表可用顺序表或链表存储。
试问:
(1) 两种存储表示各有哪些主要优缺点 ?
答:
顺序表的存储效率高,存取速度快。
但它的空间大小一经定义,在程序整个运行期间不会发生改变,因此,不易扩充。
同时,由于在插入或删除时,为保持原有次序,平均需要移动一半 ( 或近一半 ) 元素,修改效率不高。
链接存储表示的存储空间一般在程序的运行过程中动态分配和释放,且只要存储器中还有空间,就不会产生存储溢出的问题。
同时在插入和删除时不需要保持数据元素原来的物理顺序,只需要保持原来的逻辑顺序,因此不必移动数据,只需修改它们的链接指针,修改效率较高。
但存取表中的数据元素时,只能循链顺序访问,因此存取效率不高。
(2) 若表的总数基本稳定,且很少进行插入和删除,但要求以最快的速度存取表中的元素,这时,应采用哪种存储表示?
为什么?
答:
应采用顺序存储表示。
因为顺序存储表示的存取速度快,但修改效率低。
若表的总数基本稳定,且很少进行插入和删除,但要求以最快的速度存取表中的元素,这时采用顺序存储表示较好。
第三章 栈和队列
1. 栈
( 1 ) 栈的结构与定义
定义:
限定仅在表尾进行插入或删除操作的线性表。
结构:
typedef structlist{
intlistsize; // 栈的容量
structlist*head; // 栈顶指针
structlist*base; // 栈底指针
}
( 2 ) 顺序栈操作算法:
入栈、出栈、判断栈空等(这个是使用数组进行操作的,具体内容参照书本 P46-47 )
( 3 ) 链栈的结构与定义
2. 队列
( 1 ) 队列的定义
定义:
只允许在表的一端进行插入,而在另一端删除元素。
----------------------------------------------------------------------------------------------------------------
✧补充内容:
1 、一个栈的入栈序列为“ ABCDE ”,则以下不可能的出栈序列是( B )
A.BCDAE B.EDACB C.BCADE D.AEDCB
2 、栈的顺序表示中,用 TOP 表示栈顶元素,那么栈空的条件是( D )
A.TOP==STACKSIZE B.TOP==1 C.TOP==0 D.TOP==-1
3 、允许在一端插入,在另一端删除的线性表称为队列。
插入的一端为表头,删除的一端为表尾。
4 、栈的特点是先进后出,队列的特点是先进先出。
5 、对于栈和队列,无论他们采用顺序存储结构还是链式存储结构,进行插入和删除操作的时间复杂度都是 O
(1) (即与已有元素 N 无关)。
6 、已知链栈 Q ,编写函数判断栈空,如果栈空则进行入栈操作,否则出栈并输出。
(要求判断栈空、出栈、入栈用函数实现)(详看考点 2)
7. 出队与取队头元素的区别:
出队就是删除对头的数据元素,取队头元素是获取对头的数据元素值,不需要删除。
8. 链栈与顺序栈相比,比较明显的优点是:
( D )
A. 插入操作比较容易 B. 删除操作比较容易
C. 不会出现栈空的情况 D. 不会出现栈满的情况
考点 1 :
队列的编程:
&结构:
typedefstructQNode{
intdate;
structQNode*next;
}QNode,*QueuePtr;
typedefstruct{
QueuePtrfront;
QueuePtrrear;
}LinkQueue;
&创建:
LinkQueueInitQueue(LinkQueueQ)
{
Q.front=Q.rear=(QueuePtr)malloc(sizeof(QNode));
Q.front->next=NULL;
return(Q);
}
&入队:
LinkQueueEnQueue(LinkQueueQ,inte)
{
QueuePtrp;
p=(QueuePtr)malloc(sizeof(QNode));
p->date=e;
p->next=NULL;
Q.rear->next=p;
Q.rear=p;
return(Q);
}
&出队:
LinkQueueDeQueue(LinkQueueQ)
{
inte;
QueuePtrp;
p=Q.front->next;
e=p->date;
Q.front=p->next;
printf("%d",e);
if(Q.rear==p)Q.rear=Q.front=NULL;
free(p);
return(Q);
}
考点 2 :
栈的编程:
&创建:
structlist*creat()
{
structlist*p;
p=(structlist*)malloc(LEN);
p->next=NULL;
return(p);
}
&入栈:
structlist*push(structlist*head,inta)
{
structlist*p;
p=(structlist*)malloc(LEN);
p->num=a;
p->next=head;
return(p);
}
&出栈:
structlist*pop(structlist*head)
{
structlist*p;
p=head->next;
free(head);
return(p);
}
&判断栈空:
intlistempty(structlist*head)
{
if(head->next)return0;
elsereturn1;
}
第四章 串 (不是重点内容)
1. 串是由零个或多个字符组成的有限序列
2. 串的赋值:
x=’abc’;
第五章 数组和广义表 (不是重点内容)
1. 多维数组中某数组元素的 position 求解。
一般是给出数组元素的首元素地址和每个元素占用的地址空间并组给出多维数组的维数,然后要求你求出该数组中的某个元素所在的位置。
2. 明确按行存储和按列存储的区别和联系,并能够按照这两种不同的存储方式求解 1 中类型的题。
3. 将特殊矩阵中的元素按相应的换算方式存入数组中。
这些矩阵包括:
对称矩阵,三角矩阵,具有某种特点的稀疏矩阵等。
熟悉稀疏矩阵的三种不同存储方式:
三元组,带辅助行向量的二元组,十字链表存储。
掌握将稀疏矩阵的三元组或二元组向十字链表进行转换的算法。
✧补充内容:
三元组:
&结构:
typedefstruct{
inti,j; // 元素行下标及列下标
inte; // 元素值
}Triple;
typedefstruct{
intmu,nu,tu; // 矩阵的行数、列数、非零元素个数
Tripledata[MAXSIZE+1]; // 矩阵包含的三元组表, data[0] 未用
}TSMatrix;
&十字链表:
typedefstructOLNode{
inti,j; // 元素行下标及列下标
inte; // 元素值
structOLNode*right,*down; // 行的后继以及列的后继
}OLNode , *OLink;
typedefstruct{
intmu,nu,tu; // 矩阵的行数、列数、非零元素个数
OLink *rhead,*chead; // 行和列的表头指针组的首地址
}CrossList;
CrossListCreat ( CrossListM ) {
intm,n,t;
scanf(“%d%d%d”,&m,&n,&t);
M.mu=m;M.nu=n;M.tu=t;
M.rhead=(OLink*)malloc((m+1)*sizeof(OLink)); // 开辟行表头指针组
M.chead=(OLink*)malloc((n+1)*sizeof(OLink)); // 开辟行列头指针组
M.rhead[]=M.chead[]=NULL; // 初始化
…… // 接下来就是赋值和入链,因为内容实在过多……我就不全部抄下来了……
}
第六章 树和二叉树
1 . 树
( 1 ) 树的概念及术语
树:
n ( n ≥ 0 )个结点的有限集合。
当 n = 0 时,称为空树;任意一棵非空树满足以下条件:
⑴ 有且仅有一个特定的称为根的结点;
⑵ 当 n > 1 时,除根结点之外的其余结点被分成 m ( m>0 )个互不相交的有限集合 T1,T2, … ,Tm ,其中每个集合又是一棵树,并