数据结构复习2010

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

数据结构复习2009
第一部分基本概念
基本知识点:数据结构和算法的概念
重点:数据结构的逻辑结构、存储结构、数据运算三方面的概念及相互关系;算法时间复杂度分析。

难点:分析算法的时间复杂度。

第二部分线性结构
1、线性表
基本知识点:线性表的逻辑结构特征,线性表的基本运算,线性表的两种存储结构以及为两种存储结构下线性表基本运算算法的实现,顺序表和链表的优缺点比较。

重点:掌握线性表的定义和特点,线性表的存储结构;顺序表和链表的组织方法和算法设计。

难点:单链表和双链表的各种算法设计。

2、栈和队列
基本知识点:理解栈和队列的定义、特点及与线性表的异同;掌握顺序栈和链栈的组织方法,栈满、栈空的判断及其描述;掌握顺序队列、循环队列和链队列的组织方法,队满、队空的判断及描述。

递归的相关概念。

重点:栈和队列的特点,顺序栈和链栈的基本运算的实现算法;顺序队列、循环队列和链队列的基本运算算法。

递归模型,递归算法的执行过程和递归设计思想。

难点:灵活运用栈和队列设计解决应用问题的算法。

将递归算法转化为非递归算法。

3、串
基本知识点:串的基本概念和串的基本运算。

重点:串的顺序存储方法和串的链接存储方法;串的基本运算算法的实现。

难点:模式匹配Brute-Force算法和KMP算法。

4、数组、稀疏矩阵、广义表
基本知识点:数组的顺序存储结构;特殊矩阵的压缩存储方法;稀疏矩阵的压缩存储方法。

广义表的定义及其与线性表的关系;广义表的存储结构。

重点:数组的复杂算法设计。

广义表的存储结构以及基本运算的实现算法。

难点:稀疏矩阵和各种存储结构及基本运算的实现算法。

广义表的递归算法设计。

第三部分树形结构
基本知识点:树的定义及树的相关术语、树的表示和树的性质。

二叉树的定义、二叉树的性质、满二叉树和完全二叉树的定义、二叉树的顺序存储和链式存储、二叉树的遍历过程、二叉树的线索化过程、哈夫曼树的定义与构造方法、二叉树与森林之间的相互转换。

重点:掌握二叉树的性质、二叉树的遍历(二叉树的各种遍历方法及它们所确定的序列之间的关系)、二叉树的线索化方法,构造哈夫曼树。

难点:二叉树的各种算法,包括递归算法和非递归算法的设计。

(注:要求掌握二叉树的二叉链表的相关算法)
第四部分图结构
基本知识点:图的基本概念、图的存储结构、图的遍历算法(深度优先遍历和广度优先遍历)、图的生成树和最小生成树、最短路径、关键路径和拓扑排序。

重点:图的各种存储结构和遍历算法(递归和非递归算法)设计,构造最小生成树,生成
最短路径、生成图的关键路径,拓扑排序的应用。

难点:图的遍历算法和图的各种复杂算法的设计。

第五部分查找与排序
1、查找
基本知识点:查找及相关概念,各种顺序表的查找算法和性能分析,各种树表的查找算法和
性能分析,哈希表的构造、查找和性能分析。

重点:各种顺序表和树表的查找算法和性能分析,构造哈希表、冲突处理和性能分析。

难点:各种查找算法设计和性能分析。

2、内部排序
基本知识点:内排序的概念;各种排序的方法。

(排序算法中用到的一些概念。


重点:各种排序算法的性能特点;各种排序算法的比较和选择。

难点:复杂排序算法设计。

考试题目类型:
1、单项选择题。

2、填空题。

3、判断题。

4、解答题(应用题、简答题)。

5、算法设计与分析题。

例题
第一部分基本概念
例题1_1:用C/C++语言描述下列算法,并给出算法的时间复杂度。

(1)求一个n阶方阵的所有元素之和。

(2)对于输入的任意三个整数,将它们按照从小到大的顺序输出。

(3)对于输入的任意n个整数,输出其中的最大和最小元素。

解:(1)算法如下:
int sum(int A[n][n], int n)
{ int i, j, s=0;
for(i=0;i<n;i++)
for(j=0;j<n;j++) s+=A[i][j];
return s;
}
本算法的时间复杂度为O(n2)。

(2)算法如下:
void Order(int a, int b, int c)
{ if(a>b)
{if(b>c) cout<<c<<b<<a;
else if(a>c) cout<<b<<c<<a;
else cout<<b<<a<<c;
}
else
{ if(c<a) cout<<c<<a<<b;
else if(c<b) cout<<a<<c<<b;
else cout<<a<<b<<c;
}
}
本算法的时间复杂度为O(1)。

(3)算法如下:
void maxmin(int a[], int n, int &max, int &min)
{ int k;
min=a[0]; max=a[0];
for(k=1;k<n;k++)
if(a[k]>max) max=a[k];
else if(a[k]<min) min=a[k];
}
本算法的时间复杂度为O(n)。

第二部分线性结构
例题2_1_1:
在线性表的如下链表存储结构中,若未知链表头结点的指针,仅已知p指针指向的结点,能否将它从该结构中删除?为什么?
(1)单链表;(2)双链表;(3)循环链表。

答:(1)不能删除。

因为无法知道*p结点的前趋结点的地址。

(2)能删除。

由*p结点的左指针找到其前趋结点的地址,然后实施删除。

(3)能删除。

循环查找一周,可以找到*p结点的前趋结点的地址,然后实施删除。

例题2_1_2:已知长度为n的线性表A采用顺序存储结构,编写一个时间复杂度为O(n)、空间复杂度为O(1)的算法,该算法删除线性表中所有值为item的数据元素。

解:存储结构如下:
typedef struct SeqList
{ ElemTyep *elem;
int length;
int ListSize;
}SeqList;
用k记录顺序表A中等于item的元素个数,边扫描A边统计k,并将不为item的元素向前移动k个位置,最后修改A的长度。

算法如下:
void DeleteNode(SeqList &A, ElemType item)
{ int k=0, i=0;
while(i<A.length)
{ if(A.elem[i]==item) k++; else A.elem[i-k]=A.elem[i];
i++;
}
A.length=A.length-k;
}
算法中只有一个while循环,时间复杂度为O(n)。

算法中只用了两个临时变量i和k,辅助空间的太小与表的长度无关,空间复杂度为O(1)。

例题2_1_3:设C={a1, b1, a2, b2, ……, an, bn}为一线性表,采用带头结点的单链表存放,编写一个就地算法,将其拆分为两个线性表,使得:
A=(a1, a2, ……, an),B=(b1, b2, ……, bn)。

解:存储结构如下:
typedef struct LNode
{ Elemtype data;
struct LNode *next;
}LNode, *LinkList;
算法如下:
void Fun(LinkList &C, LinkList &A, LinkList &B)
{LNode *pa, *pb, *p, *s;
p=C->next;
A=C; A->next=0; B=new LNode; B->next=0; pa=A; pb=B;
while(p)
{ s=p->next;
pa->next=p; pa=p;
p=s; s=s->next;
if(p) {pb->next=p; pb=p; p=s; s=s->next;}
}
pa->next=0; pb->next=0;
C=0; //C已经被拆分,拆分后不再存在。

}
//A和B采用尾插法建立。

例题2_1_4:设计一个算法,将x插入一个有序(从小到大排序)的线性表(顺序存储结构)的适当位置上,并保持线性表的有序性。

解:设存储结构如下:
typedef struct SeqList
{ElemType *elem;
int length;
int ListSize;
}SeqList;
算法如下:
void fun(SeqList &L, ElemType x)
{ int i;
if(L.length==L.ListSize)
{ElemType *p=new ElemType[2*L.ListSize];
L.ListSize=2*L.ListSize;
for( i=0;i<L.Length;i++) p[i]=L.elem[i];
delete []L.elem;
L.elem=p;
}
i=L.length-1;
while(i>=0 && x<L.elem[i]) {L.elem[i+1]=L.elem[i]; i=i-1;}
L.elem[i+1]=x;
L.length++;
}
例题2_1_4B: 设计一个算法,将顺序表L逆置。

解:假设存储结构如下:
typedef struct SqList
{ ElemType data[Maxsize];
int Length;
}SqList;
算法如下:
void Reverse(SqList &L)
{ int i, j;
i=0; j=L.Length-1;
while(i<j)
{ ElemType temp= L.data[i];
L.data[i]=L.data[j];
L.data[j]=temp; i++; j--;
}
}
例题2_1_5:设计一个算法,将x插入一个有序(从小到大排序)的线性表(链接存储结构)的适当位置上,并保持线性表的有序性。

解:设存储结构如下:
typedef struct LNode
{ElemType data;
struct LNode *next;
}LNode, *LinkList;
假定单链表是带头结点的单链表,算法如下:
void Fun(LinkList &L, ElemType x)
{ LNode *pre, *p;
p=new LNode; p->data=x;
pre=L;
while(pre->next && pre->next->data<x) pre=pre->next;
p->next=pre->next;
pre->next=p;
}
例题2_1_6:设计一个算法判断带头结点的单链表L是否是按值递增的。

解:设存储结构如下:
typedef struct LNode
{ElemType data;
struct LNode *next;
}LNode, *LinkList;
算法如下:
int IsIncrease(LinkList &L)
//若递增返回1,否则返回0。

{ LNode *pre, *p;
p=L->next;
if(p==0) return 1;
while(p->next)
{pre=p; p=p->next;
if(pre->data>p->data) return 0;
else p=p->next;
}
return 1;
}
例题2_1_7: 设计一个算法,将一个带头结点的单链表逆置。

解:设存储结构如下:
typedef struct LNode
{ElemType data;
struct LNode *next;
}LNode, *LinkList;
算法如下:
void Reverse(LinkList &H)
{ LNode *p=H->next, *q;
H->next=0;
while(p)
{q=p->next;
p->next=H->next; H->next=p;
p=q;
}
}
例题2_1_8: 设计一个算法,在带头结点的单链表L中删除所有结点值为最小的结点(可能有多个结点值最小的结点)。

解:设存储结构如下:
typedef struct LNode
{ElemType data;
struct LNode *next;
}LNode, *LinkList;
算法如下:
void DeleteMin(LinkList &L)
{ ElemType min;
LNode *pre, *p;
if(L->next==0) return;
min=L->next->data;
p=L->next;
while(p) { if(p->data<min) min=p->data; p=p->next;}
pre=L; p=L->next;
while(p)
{ if(p->data==min) {pre->next=p->next; delete p; p=pre->next;}
else { pre=p; p=p->next;}
}
例题2_1_9:分别设计有关单链表、单循环链表、双链表、双循环链表的插入、删除、查找、遍历等算法。

(结点个数、最大值、次大值、最小值、次小值、排序、统计、分拆、合并、归并、销毁、复制、有序表)
(插入算法包括:前插法、后插法、第i个位置插入、值为x的结点前或后插入)
(删除算法包括:删除第i个结点、删除值为x的结点、删除值为x的结点前或后的结点)(查找算法包括:查找值为x的结点、查找第i个结点)
例题2_2_1:设有5个元素,其入栈次序为:A,B,C,D,E,在各种可能的出栈次序中,以元素C,D最先出栈(即C第一个且D第二个出栈)的次序有哪几个?
答:要使C第一个且D第二个出栈,应是A入栈,B入栈,C入栈,C出栈,D入栈,D 出栈,之后可以有以下几种情况:
(1)B出栈,A出栈,E入栈,E出栈,输出序列为CDBAE;
(2)B出栈,E入栈,E出栈,A出栈,输出序列为CDBEA;
(3)E入栈,E出栈,B出栈,A出栈,输出序列为CDEBA。

例题2_2_2:假设以不带头结点的循环单链表表示队列,并且只设一个指针rear指向队尾结点,但不设头指针,请写出相应的队初始化、入队、出队和判队空的算法。

解:设存储结构如下:
typedef struct LNode
{ElemType data;
struct LNode *next;
}LNode, *LinkQueue;
则相应的算法如下:
(1)队列的初始化
void InitLinkQueue(LinkQueue &rear)
{rear=0;}
(2)入队列
void EnQueue(LinkQueue &rear, ElemType x)
{LNode *p=new LNode; p->data=x;
if(rear==0) {rear=p; rear->next=rear;}
else {p->next=rear->next; rear->next=p; reat=p; }
}
(3)出队列
void DeQueue(LinkQueue &rear, ElemType &x)
{ if(rear==0) return;
LNode *p=rear->next; x=p->data;
if(rear==p) rear=0;
else rear->next=p->next;
delete p;
}
(4)判别队列是否为空
in QuEmpty(LinkQueue &rear)
{ return rear==0; }
例题2_3_1:两个串相等的充分必要条件是什么?
答:两个串相等的充分必要条件是:两个串的长度相等且对应位置上的字符相等。

例题2_3_2:空串和空格串有何区别?
答:空串是指不含任何字符的串,其长度为0,空串是任意串的子串。

空格串是指仅仅含有空格字符的串,其长度是串中空格字符的个数。

例题2_3_3:设计在链串上实现判定两个串是否相等的算法。

解:存储结构如下:
typedef struct LNode
{ char data;
struct LNode *next;
}LNode, *LinkString;
算法如下:
int Equal(LinkString &S, LinkString &T)
{ LNode *ps=S, *pt=T;
int flag=1;
while(ps && pt && flag)
{ if(ps->data!=pt->data) flag=0;
pt=pt->next; ps=ps->next; }
if(ps || pt) return 0;
else return flag;
}
例题2_4_1:编写一个算法,计算一个三元组表表示的稀疏矩阵的对角线元素之和。

解:存储结构如下:
typedef struct
{ int i,j; //行下标和列下标。

ElemType elem;
}Triple;
typedef struct
{ Triple data[MaxSize+1]; //非0元三元组表,data[0]未用。

int mu, nu, tu; //矩阵的行数、列数、非0元素个数。

}TSMatrix;
算法如下:
int Diagonal(TSMatrix &A, ElemType &sum)
//用sum返回对角线元素之和。

{int k;
sum=0;
if(A.mu!=A.nu) { cout<<”不是正方阵,不能求对角线元素之和\n”; return 0;}
for(k=1; k<=A.tu; k++)
if(A.data[k].i==A.data[k].j) sum+=A.data[k].elem;
return 1;
}
例题2_4_2:判断题:判断以下叙述的正确性:
(1)广义表的长度与广义表中含有多少个原子元素有关。

(2)一个广义表的表尾总是一个广义表。

(3)在广义表中,每个原子的类型都是相同的。

(4)在广义表中,每个子表的结构都是相同的。

(5)空的广义表是指广义表中不包含原子元素。

(6)广义表的长度不小于其中任何一个子表的长度。

答:(1)错误。

(2)正确。

(3)正确。

(4)错误。

(5)错误。

(6)错误。

第三部分树形结构
(注:以下的算法设计题,均假定二叉树用二叉链表存储)
例题3_1:编写算法,求二叉树的结点个数、叶结点个数、单分支结点个数、双分支结点个数。

例题3_2:编写二叉树的先序遍历、中序遍历、后序遍历的递归算法。

例题3_3:编写二叉树的先序遍历、中序遍历的非递归算法。

例题3_4:编写二叉树的层次遍历的算法。

例题3_5:给出二叉树的先序序列EBADCFHGIKJ和中序序列ABCDEFGHIJK,画出二叉树并写出二叉树的后序序列和层次序列。

例题3_6:给出二叉树的后序序列DCEGBFHKJIA和中序序列DCBGEAHFIJK,画出二叉树并写出二叉树的后序序列和层次序列。

例题3_7:给出二叉树的层次序列ABCDEFGHIJ和中序序列DBGEHJACIF,画出二叉树并写出二叉树的先序序列和后序序列。

例题3_8:编写销毁二叉树的算法。

例题3_9(1):给定权集w={2,3,4,7,8,9},试构造关于w的一棵哈夫曼树,并求其加权路径的长度WPL,写出相应的哈夫曼编码。

例题3_9(2):给定权集w={7,19,2,6,32,3, 21, 10},试构造关于w的一棵哈夫曼树,并求其加权路径的长度WPL,写出相应的哈夫曼编码。

例题3_10:编写算法输出二叉树的所有叶子结点。

例题3_11:给出一棵二叉树,画出其中序线索二叉树。

例题3_12:设计一个算法,交换二叉树的左右子树。

例题3_13: 设计一个算法,复制一棵二叉树。

例题3_14:完全二叉树、二叉树的结点数、叶结点数等之间的关系。

(n0=n2+1,根据这个性质可以得到一些结论)
第四部分图结构
例题4_1:给定一个图(有向图或无向图),写出邻接矩阵,或画出其邻接表或逆邻接表。

例题4_2:给定一个带权的有向图(无向图),画出其按Prim算法或Kruskal算法生成最小生成树的过程及最后结果。

(不要求写出算法)。

例题4_3:给出一个有向的无环图,写出其一个或若干个拓扑序列。

(不要求定算法)。

例题4_4:给定一个图(有向图或无向图),写出其从某一点出发,按深度优先或按宽度优先遍历的序列。

(不要求写算法)。

例题4_5:给定一个带权的图(有向图或无向图),写出按照Dijkstra算法求某个源点到其余各点的最短路径的过程。

(不要求写完整的算法)。

第五部分查找与排序
例题5_1(1):给出一个有序表{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20},写出按照二分查找法查找某个结点(如3,又如16)的查找过程。

例题5_1(2): 有一个有序表R[1…13]={1,3,9,12,32,41,45,62,75,77,82,95,100},当用二分查找法查找关键字为82的结点时,经过多少次比较后查找成功,依次与哪些关键字进行比较?
例题5_2:给出一组数据,比如{7,2,9,8,13,1,4,3,6,5,11,14,10,12},画出相应的二叉排序树。

然后再画出在该二叉排序树中删除结点13后的二叉排序树。

例题5_3:分别设计在二叉排序树中查找指定关键字结点的递归算法和非递归算法。

解:存储结构如下:
typedef struct LNode
{ KeyType key;
//InfoType info;
struct LNode *left, *right;
}LNode, *BiSortTree;
递归的查找算法如下:
LNode *FindX(BiSortTree &T, KeyType x)
{ if(T==0) return 0;
if(T->key==x) return T;
if(x<T->key) return FindX(T->left, x);
else return FindX(T->right, x);
}
非递归算法如下:
LNode* FindY(BiSortTree &T, KeyType y)
{LNode *p=T;
if(p==0 || p->key==y) return p;
while(p)
{if(p->key==y) return p;
else if(x<p->key) p=p->left;
else p=p->right;
}
return p;
}
例题5_4:给出一组数据,要求用相应的方法构造哈希表。

(包括给定哈希函数及处理冲突的方法)。

在等概率下计算平均比较次数。

(1)关键字序列{7,4,1,14,100,30,5,9,20,134},设哈希函数为H(key)=key %13,试给出表长为13的哈希表(用线性探测开放地址法处理冲突),并给出在等概率情况下,查找成功和查找不成功时的平均查找长度。

例题5_5:给出一组数据,比如{503,87,512,61,908,170,897,275,653,426},分别写出按照直接插入排序、选择排序、起泡排序、Shell排序、快速排序、堆排序、归并排序、基数排序的排序过程(各趟排序结束时关键字序列的状态)。

写出以上各种排序算法的平均时间复杂度。

例题5_6: 判断一个给定的序列是否为堆(大根堆或者小根堆)。

(1){100,85,98,77,80,60,82,40,20,10,66}
(2){10,20,40,60,66,77,80,82,85,98,100}
(3){100,85,40,77,80,60,66,98,82,10,20}。

相关文档
最新文档