1. 数据存储结构包括哪几种类型?数据逻辑结构包括哪几种类型?
答:存储结构包括顺序存储,链式存储,索引存储和散列存储;
逻辑结构包括线性结构和非线性结构。更细分的说,逻辑结构包括集合、线性结构、树形结构和图形(网状)结构。
2. 数据结构是一门研究什么内容的学科?
答:数据结构是一门研究在非数值计算的程序设计问题中,计算机的操作对象及 对象间的关系和施加于对象的操作等的学科。
3. 数据元素之间的关系在计算机中有几种表示方法?各有什么特点?
答:四种表示方法。
1、顺序存储方式。数据元素顺序存放,每个存储结点只含一个元素。存储位置反映数据元素间的逻辑关系。存储密度大,但有些操作(如插入、删除)效率较差。
2、链式存储方式。每个存储结点除包含数据元素信息外还包含一组(至少一个)指针。指针反映数据元素间的逻辑关系。这种方式不要求存储空间连续,便于动态操作(如插入、删除等),但存储空间开销大(用于指针),另外不能折半查找等。
3、索引存储方式。除数据元素存储在一地址连续的存储空间外,尚需建立一个索引表,索引表的索引项指示存储结点的存储位置(下标)或存储区间端点(下标,非稠密索引)兼有静态和动态特性。
4、散列存储方式。利用散列函数和解决冲突的方法,将关键字散列在连续的有限的地址空间内,并将散列函数的值解释成关键字所在元素的存储地址。其特点是存取速度快,只能按关键字随机存取,不能顺序存取,也不能折半存取。
4. 评价一个好的算法,你是从哪几方面来考虑的?
答:1、算法的正确性。 2、算法的易读性。 3、算法的健壮性 4、算法的时空效率。
5. 对于一个数据结构,一般包括哪三个方面的讨论?
答:逻辑结构,存储结构,操作(运算)。
6. 数据结构与数据类型有什么区别?
“数据结构”这一术语有两种含义,一是作为一门课程的名称;二是作为一个科学的概念作为科学概念,目前尚无公认定义,一般认为,讨论数据结构要包括三个方面,一是数据的逻辑结构,二是数据的存储结构,三是对数据进行的操作(运算)。而数据类型是值的集合和操作的集合,可以看作是已实现了的数据结构,后者是前者的一种简化情况。
7. 算法的五个重要特征是什么?
答:有穷性,确定性,可行性,零个或多个输入和1至多个输出。
答案:A
顺序存储结构的地址在内存中是连续的,所以可以通过计算地址实现随机存取。而链式存储结构的存储地址不一定连续,只能通过逐个结点的指针顺序存取。
答案:A
顺序表的优点之一是随机存取,即时间复杂度为O(1),而插入和删除的时间复杂度都是O(n),但是在最后插入结点和删除结点的时间复杂度都是O(1)。
答案:D
带有尾指针的单循环链表删除尾结点时要遍历整个链表,时间复杂度是O(n)。只有用带头结点的双循环链表完成要求的操作最节省时间,时间复杂度是O(1)。
线性表的顺序存储结构具有三个弱点:第一,在作插入或删除操作时,需要移动大量元素;第二,由于难以估计,必须预先分配较大的空间,往往使存储空间不能得到充分利用;第三,表的容量难以扩充。试问,线性表的链式存储结构是否一定能够克服上述三个弱点?请简述之。
答:一般说链式存储结构克服了顺序存储结构的三个弱点。首先,插入、删除不需移动元素,只修改指针,时间复杂度为O(1);其次,不需要预先分配空间,可根据需要动态申请空间;其三,表容量只受可用内存空间的限制。其缺点是因为指针增加了空间开销,当空间不允许时,就不能克服顺序存储结构的缺点。
在单链表和双向链表中,能否从当前结点出发访问到任何一个结点?
答:在单链表中不能从当前结点(若当前结点不是第一结点)出发访问到任何一个结点。因为链表运算只能从头指针开始,访问到链表中每个结点。在双链表中从当前结点反向可以到第一结点,正向可以到最后结点,因而从当前结点出发可以访问到链表中任何一个结点。
解释链表的头指针、头结点、首元结点这三个概念
头结点:头结点是在链表的首元结点之前附设的一个结点,数据域通常用来保存跟链表有关的信息,比如链表的长度;
首元结点:首元结点是链表的开始结点,指链表中存储线性表中第一个数据元素a1的结点。
头指针:头指针是指向链表中第一个结点的指针,如果链表存在头结点则头指针就是指向头结点的地址,反之指向首元结点的地址。
设双向循环链表中结点的数据域、前驱和后继指针域分别为data、pre和next,试写出在指针P所指结点之前插入一s结点的C语言描述语句。
答:在指针P所指结点前插入结点s的语句如下:
s->pre=p->pre; s->next=p; p->pre->next=s; p->pre=s;
请简要说明下列函数的主要功能。
void func( LinkList LI, LinkList L2)
{LNode*p, *q, *r;
q=L2->next;
while (g)
{p= LI;
while (p->next)
{if (p->next->data = q->data)
{r=p->next; p->next=r->next; free®;}
p = p->next;
}
q=q->next;
}
return;
}
答:算法实现集合的差运算C=A-B。将链表L2中的元素依次到链表L1中查找,有则从L1中删除。
void MergeList_L(LinkList &La,LinkList &Lb,LinkList &Lc){
//已知单链线性表La和Lb的元素按值非递减排列
//归并La和Lb得到新的单链线性表Lc,Lc的元素也按值非递减排列
pa=La->next; pb=Lb->next;
Lc=pc=pa; //用La的头结点作为Lc的头结点
while(pa && pb){
if(pa->data <= pb->data){
pc->next=pa; pc=pa; pa=pa->next;
}
else{pc->next=pb; pc=pb; pb=pb->next;}
}
pc->next=pa?pa:pb; //插入剩余段
free(Lb); //释放Lb的头结点
}
void CreateList_L(LinkList &L,int n){
L=(LinkList)malloc(sizeof(LNode));
L->next = NULL; //先建立一个带头结点的单链表
for(i=n; i>0; --i){
p=(LinkList)malloc(sizeof(LNode)); //生成新结点
scanf(&p->data); //输入元素值
p->next = L->next;
L->next = p; //插入到表头
}
}
核心语句是:
for(i=0; i<=(n-1)/2; i++) a[i]<-->a[n-i-1];
核心语句段如下:
while(p!=NULL) //head是链表头结点的指针,p初值指向第一元素结点
{r=o->next; //暂存p的后继
p->next=head->next; head->next=p; //逆置
p=r;} //恢复待逆置结点
本题未说单链表有序,因此要依次从每个结点出发,遍历整个链表,删除重复元素。删除结点要记住被删结点的前驱,不能断链。核心语句段如下:
while(pre!=null) //用pre控制依次从每个结点出发,初始指向第一元素
{m=pre->data; q=pre; p=pre->next;
while(p!=null)
if(p->data==m) {u=p; p=p->next; free(u);} //删重复结点
else{q->next=p; q=p; p=p->next;}
q->next=p; //有可能链表尾结点值是m
pre=pre->next; //下一趟
}
若有一个一维数组A,它的元素下标从1开始到MAX。要在数组A中建立两个栈共享同一空间,栈Sl的栈顶指针为topl,栈S2的栈顶指针为top2,为了最大限度地利用数组A的空间,则应该如何共享?栈满和栈空的条件是什么?
答:两站共享数组A,top1=0时S1栈空,top2=MAX+1时,S2栈空,top2-top1=1时栈满。
(1)什么是递归程序?
答:一个函数在结束本函数运行之前,直接或间接调用函数自身,称为递归。
例如,函数f在执行中,又调用函数f自身,称为直接递归;若函数f执行中,调用函数g,而g执行中,又调用函数f,这称为间接递归。在实际应用中,多为直接递归,也常简称为递归
(2)递归程序的优、缺点是什么?
答:递归程序的优点是程序结构简单、易读、清晰,易证明其正确性。缺点是执行中占内存空间较多,运行效率低,不易优化。
(3)递归程序在执行时,应借助于什么来完成?
答:递归程序执行中需借助栈这种数据结构来实现。
(4)递归程序的入口语句、出口语句一般用什么语句实现?
答:递归程序的入口语句和出口语句一般用条件判断语句来实现。
简述顺序存储队列的假溢出的避免方法及队列满和空的条件。
答:设顺序存储队列用一维数组q[m]表示,其中m为队列中元素个数,队列中元素在向量中的下标从0到m-1。设队头指针为front,队尾指针是rear,约定font指向队头元素的前一位置,rear指向队尾元素。当front等于-1时队空,rear等于m-1时为队满。由于队列的性质(“删除”在队头而“插入”在队尾),所以当队尾指针rear等于m-1时,再无法入队。若经过几次退队,队列中会有空闲单元,所以队列并不是真满,这称为“假溢出”。其解决办法有二,一是将队列元素向前“平移”(占用0至rear-front-1);二是将队列看成首尾相连即看做循环队列(0…m-1)。在循环队列下,仍定义front=rear时为队空,而判断队满则常用两种方法:一种是用“牺牲一个单元”,即rear+1=front(准确记是(rear+1)%m=front,m是队列容量)时为队满;另一种方法是“设标记”,如设标记tag,tag=0时,若因删除导致front=rear为队空;tag=1时,若因插入导致font=rear则为队满。
假设称正读和反读都相同的字符序列为“回文”,例如, ‘abcba’是回文, 'abcde’和 ‘ababab’则不是回文。试写一个算法判別读入的一个以’@’为结束符的字符序列是否是“回文”。
设以字符数组A存储读入的字符串,字符串长度为n。核心语句端如下:
for(int i=0l i<n/2; i++)
if(A[i]!=A[n-1-i]){count<<"字符串非中心对称"<<endl; exit(0);}
cout<<"字符串中心对称"<<endl;
在字符串模式匹配的KMP算法中,求模式的next数组值的定义如下:
N e x t [ j ] = { 0 , 当 j = 1 时 m a x ( k ∣ 1 < k < j 且 ′ P 1.... P k − 1 ′ = ′ P j − k + 1... P j − 1 ′ ) 1 , 其 他 情 况 Next[j]= \begin{cases} 0,当j=1时\\ max(k | 1
请问:
(1)当j=1时,为什么要取next[1]=0?
答:当模式串中第一个字符与主串中某字符比较不等(失配)时, next[1]=0表示模式串中已没有字符可与主串中当前字符s[i]比较,主串当前指针应后移至下一字符,再和模式串中第一个字符进行比较。
(2)为什么要取max{k},k最大是多少?
答:当主串第i个字符与模式串中第j个字符失配时,若主串i不回溯,则假定模式串第k个字符与主串第i个字符比较,k值应满足条件1
答:在上面两种情况外发生失配时,主串指针i不回溯,在最坏情况下,模式串从第1
个字符开始与主串第i个字符比较,以便不丢失可能的匹配。
设s、t为两个字符串,分别放在两个一维数组中,m、n分别为其长度,判断t是否为s的子串。如果是,输出子串所在位置(第一个字符),否则输出0。(注:用程序实现。)
首先应查找字符串s的pos位置,将第pos个字符到字符串s尾的子串向后移动字符串t的长度,然后将字符串t复制到字符串s的第pos位置后。对插入位置pos要验证其合法性,题目假设给字符串s的空间足够大,故对插入不必判溢出。
while(*p!='\0' && i<pos) {p++; i++;} //查pos位置
if(*p == '\0') {cout<<"pos位置大于字符串s的长度"<<endl; exit(0);}
else //查找字符串的尾
while(*p!='\0'){ p++; i++;} //查到尾时,i为字符'\0'的下标,p也指向'\0'
while(*q!='\0'){q++; x++;} //查找字符串t的长度x,循环结束时q指向'\0'
for(j=i; j>=pos; j--){*(p+x)=*p; p--;} //串s的pso后的子串右移
q--; //指针q回退到串t的最后一个字符
for(j=1; j<=x; j++) *p--=*q--; //将t串插入s的pos位置上
答案:B
答案:D
3.设有一个n行n列的对称矩阵A,将其下三角部分按行存放在一个一维数组B中,A[0][0]存放于B[0]中,那第i行的对角元素A[i][i]存放于B中( )处。
A.(i+3)*i/2 B.(i+1)*i/2 C.(2n-i+1)*i/2 D(2n-i-1)i/2
答案:A
答案:C
tail(LS) = ((d,e,f))
head(tail(LS)) = (d,e,f)
tail(head(tail(LS))) = (e,f)//无论如何都会加上这个()括号
head(tail(head(tail(LS)))) = e//head可以去除单个元素
答案:D
数组的存储结构采用 顺序存储结构 存储方式。
二维数组A[10…20,5…10]采用行序为主序方式存储,毎个数据元素占4个存储单元,且A[10,5]的存储地址是1000,则A[18,9]的存储地址是 1208
设数组a[1…50,1…80]的基地址为2000,每个元素占2个存储单元,若以行序为主序顺序存储,则元素a[45,68]的存储地址为 9174 ;若以列序为主序顺序存储,则元素a[45,68]的存储地址为 8788。
答案:C
答案:A
答案:A
答案: D
前缀编码是指一个编码不是另一个编码的前缀
答案:C
答案:B
答案:D
答案:B
对任何一棵二叉树T,如果其终端结点数为n0,度为2的节点数为n2,则n0=n2+1
答案:E
因为n=n0+n1+n2,n2=n0-1,所以n=2n0+n1-1。在完全二叉树中,n1取1或0,这里n=1001,n1只能为0,所以n=2n0-1,n0=501,选E
答案:B
答案:D
度为2的结点没空指针,度为1的结点有一个空指针,度为0的结点(叶子)有两个空指针
答案:AC
答案:CD
答案:ABD
答案:D
答案:C
线索二叉树是利用二叉树的空链域加上线索,n个结点的二叉树有n+1个空链域
答案:D
答案:C
答案:B
Huffman树只有叶子节点才是编码字,而Huffman树中的节点,要么度为0,要么度为2。本题中除了树根总共有214个结点,叶子节点为214的一半再加一个等于108,所以能得到108个不同的码字
答案:×
中序序列和后序序列相同的二叉树为:空树和任何结点都缺右子树的单支树
设一棵二叉树的先序、中序遍历序列分别为
先序遍历序列: ABDFCEGH 中序遍历序列: BFDAGEHC
(1)画出这棵二叉树。
(2)画出这棵二叉树的后序线索树。
(3)将这棵二叉树转换成对应的树(或森林)。
答案:略
typedef struct node
{ int weight; //结点的权值,设为整型
struct node *left, *right; //指向结点左、右子女的指针
}BiNode, *BiTree;
(3)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。
int WPL=0; //权路径长度初值为0,是全局变量
void inorder(BiTree bt, level lv)
//bt是二叉树,1y是结点的层次,初值为1,本算法求二叉树bt的带权路径长度
{if(bt)
{inorder(bt->left, lv+1); //中序遍历左子树
if(bt->left==null && bt->right==null) //判断该结点是否为叶子
WPL+=(lV-1)*bt->weight; //累加结点带权路径长度
inorder(bt->right, lv+1); //中序遍历右子树
}
}
设计一算法分别求出二元树的叶结点,度数为1的结点,度数为2的结点的个数。
答:结点计数可以在遍历中解决。根据“访问根结点”、“递归调用左子树”、“递归调用右子树”三者位置的不同,而有前序、后序和中序遍历。叶子结点是左右子女均无的结点,度为1的结点是只有一个子女的结点,度为2的结点是左右子女均有的结点。
给定一组项及其权值,假定项都存放于二叉树的树叶结点,则具有最小带权外部路径长度的树称为 Huffman树。
(1)给出构造 Huffman树的算法。
答:哈夫曼树的构造过程:
①根据给定的n个权值{W1,W2,W3,…,Wn}构成n棵二叉树的集合F={T1,T2…,Tn},其中
毎棵二叉树Ti只有权值为Wi的根结点,其左右子树均为空。
②在F中选取两棵根结点的权值最小的树作左右子树构造一棵新二叉树,新二叉树根结
点的权值为其左右子树上根结点的权值之和。
③在F中删除这两棵树,同时将新得到的二叉树加入F中。
④重复②和③,直到F中只剩一棵树为止。这棵树便是哈夫曼树。
(2)给定项及相应的权如下表,画出执行上述算法后得到的Huffman树。
序号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
项 | A | B | C | D | E | F | G | H | I |
权 | 15 | 6 | 7 | 12 | 25 | 4 | 6 | 1 | 15 |
(3)用C语言编写构造 Huffman树的程序。
答:含有n个叶子结点的哈夫曼树共有2n-1个结点,采用静态链表作为存储结构,
设置大小为2n-1的数组。核心语句段如下:
for(i=0; i<2*n-1; i++ ) //置初值,结点的权、左右子女、双亲
{ T[i].parent=-1; T[i].lc=-1; T[i].rc=-1;
if (i<n) T[i].weight=w[i]; else T[i].weight=0;
}
for(i=n; i<2*n-1; i++ ) //构造新二叉树
{ p1=p2=0; small1=small2=maxint;
//赋初值,p1、p2是最小和次小权值结点的下标
for(j=0; j<i; j++ )
if(T[j].weight<small1 && T[j].parent == -1 ) //最小值
{ p2=p1; small2=small1; pl=j; small1=t[j].weight; }
else if(T[j].weight<small2 && T[j].parent == -1) //次小值
{ p2=j; small2=T[j].weight; }
T[i].weight =T[p1].weight+T[p2].weight; } //合并成一颗新二叉树
T[i].lc =p1; T[i].rc=p2; //置双亲的左右子女
T[p1].parent=i; T[p2].parent=i; //置左、右子女的双亲
要保证n个顶点的无向图在任何情况下都是连通的,则需要先由n-1个顶点组成完全图,从第n个顶点引一条到n-1任一顶点的边,则图肯定是连通的。本题先由6个顶点组成完全图,需要6(6-1)/2=15条边,故本题需要的最少边数是16条
答案:A
答案:C
假设工期单位为天,可以得出工期为27天,有三条路径,分别为1356,13246,13256.想要缩短工期,就必须同时缩小这三条路径上的权重,最优选择当然是缩短b了,可是选项没有,只能退而求其次,同时缩小d和f的权重,故选c
答案:B
邻接表中每条边存储两次,所以有n(n-1)/2条边就有 n(n-1)个结点
答案:B
注意A选项是树不是图
答案:B
答案:A
答案:D
答案:B
答案:A
答案:A
答案:B
答案:B
答案:B
这道题反过来的说法是对的,若一个N个结点的连通图,则其边数必大于或等于N-1
该图必是连通图是连通是比较强的条件
以下两种说法是对的:
在n个结点的无向图中,若该图是连通图,则其边数大于等于n-1,
在n个结点的无向图中,若边数大于(n-2)(n-1)/2,该图必是连通图
有n-1条边的图肯定都是生成树。( × )
最小代价生成树是唯一的。( )
答案:×。最小生成树是不一定唯一,最小生成树代价唯一
答案:×。BFS广度优先算法需要借助于一个队列来实现,DFS深度优先算法不需要
答案:×。用顶点表示活动,用弧表示活动间的优先关系的有向图称为顶点表示活动的网,简称AOV网。
在数据结构中,线性结构、树形结构和图形结构数据元素之间分别存在
一对一 ,一对多 和 多对多 的联系
有n个顶点的有向图,至少需要 n 条弧才能保证是连通的。
n个顶点e条边的图采用邻接表存储,则空间复杂度是 O(n+e)
求最短路径的Dijkstra算法的时间复杂度为 O(n2)
设无向图G有n个顶点,m条边。试编写用邻接表存储该图的算法。(设顶点值用1~ n或0~ n-1编号)
邻接表存储结构是顶点向量和顶点的邻接点链表相结合的存储结构。核心语句段如下:
cin>>n>>m; //n个顶点和m条边
for(i=0; i<n; i++) //输入顶点信息,建立定点向量
{cin>>g[i].vertex;g[i].firstarc=null;}
for(k=0; k<m; k++) //输入边信息
{cin>>v1>>v2; //输入两个顶点
i=GraphLocateVertex(g,v1); j=GraphLocateVertex(g,v2); //顶点定位
p=new(ArcNode); //申请边结点
p->adjvex=j; p->next=g[i].firstarc; g[i].firstarc=p; //将边结点链入
p=new(ArcNode)(ArcNode); //申请边结点,链入j到i的一条边
p->adjvex=i; p->next=g[j].firstarc; g[j].firstarc=p;
}//for
答案:A
答案:A
答案:D
下列内部排序算法中:
A、快速排序 B、直接插入排序 C、二路归并排序
D、简单选择排序 E、起泡排序 F、堆排序
(1)其比较次数与序列初态无关的算法是(CD)
(2)不稳定的排序算法是(ADF)
(3)在初始序列已基本有序(除去n个元素中的某k个元素后即呈有序,k<
(4)排序的平均时间复杂度为O( nlogn)的算法是(ACF),为O(mn)的算法是(BDE)
对一组数据(84,47,25,15,21)排序,数据的排列次序在排序的过程中的变化为
(1) 84 47 25 15 21 (2) 15 47 25 84 21
(3) 15 21 25 84 47 (4) 15 21 25 47 84
则采用的排序是( )。
A、选择 B、冒泡 C、快速 D、插入
答案:A
答案:A
答案:B
答案:C
答案:B
内排序要求数据一定要以顺序方式存储。(×)
排序算法中的比较次数与初始元素序列排序无关。(×)
排序的稳定性是指排序算法中的比较次数保持不变,且算法能够终止。(×)
时间复杂度为O(n2)、空间复杂度为O(1)且与文件初始状态无关的排序算法是直接插入排序.( ×)
由于希尔排序的最后一趟与直接插入排序过程相同,因此前者一定比后者花费的时间更多.( ×)
对于n个记录的集合进行冒泡排序,在最坏情况下所需要的时间是O(n^2).(√)
直接选择排序是不稳定排序.(√)
堆肯定是一颗平衡二叉树.( ×)
堆是满二叉树.( ×)
直接选择排序算法在最好情况下所做的交换元素次数为0。
设用希尔排序对数组{98,36,-9,0,47,23,1,8,10,7}进行排序,给出的步长(也称增量序列)依次是4,2,1,则排序需3趟,写出第一趟结束后,数组中数据的排列次序 (10,7,-9,0,47,23,1,8,98,36)。
堆排序的算法时间复杂度为 O(nlog2n)。
在堆排序中,首先需要进行的操作是 建堆。
每次使两个有序表合并成一个有序表,这种排序方法叫做 归并排序。