《 — 数据结构 —》
实验指导书
朱素英 编 写
适用专业: 计算机科学与技术
计算机网络工程
湖南人文科技学院计算机科学技术系
2008 年 8 月
前 言
《数据结构》课程是计算机科学与技术专业的一门必修的专业基础课。这门课程的主要特点是实践性很强,不仅要学习基本理论知识,更要注重上机实践,通过上机实践验证算法的正确性,掌握和巩固所学理论知识。通过对本课程中算法设计和上机实践的训练,培养学生的数据抽象能力和程序设计的能力,为后续课程,特别是软件课程打下坚实的知识基础。要求学生掌握各种常用数据结构的逻辑结构,存储结构及有关操作的算法。
通过本课程的学习,要求学生了解数据结构及其分类、数据结构与算法的密切关系; 熟悉各种基本数据结构及其操作,学会根据实际问题要求来选择数据结构; 掌握设计算法的步骤和算法分析方法; 掌握数据结构在排序和查找等常用算法中的应用。
为了使学生更好地理解和深刻地把握这些知识,并在此基础上,训练和培养了解数据结构及其分类、数据结构与算法的密切关系; 熟悉各种基本数据结构及其操作,学会根据实际问题要求来选择数据结构; 掌握设计算法的步骤和算法分析方法; 掌握数据结构在排序和查找等常用算法中的应用等方面的技能,设置的以下十二个的具体实验项目,这些实验项目中有验证性实验、综合性实验与设计性实验,在每一个具体实验中都有标明。各项实验主要了解、掌握的具体知识,在每个实验中都有说明。
本实验指导书对计算机科学与技术专业与网络工程专业均适应。
目录
实验一:顺序表的基本操作 1
实验二:单链表的基本操作 9
实验三:栈的基本操作 14
实验四:队列的基本操作 16
实验五:串的模式匹配 19
实验六:矩阵的基本运算 22
实验七:二叉树的基本操作 29
实验八:哈夫曼树与哈夫曼编码 34
实验九:图的最短路径算法 38
实验十:哈希表的基本操作 40
实验十一:各种排序算法 44
实验十二:银行模拟 48
实验一:顺序表的基本操作
实验学时:2
实验类型:验证
实验要求:选修
一、实验目的
1.掌握使用VC++进行控制台应用程序编写的基本方法
2.掌握顺序表的初始化、销毁、数据元素的插入和删除以及顺序表的输出等基本操作。
二、实验内容
顺序表的初始化、插入和删除
三、 程序清单
1、运行环境
Visual c++ 6.0的微机一台
2、程序清单
//线性表的顺序结构算法实现
#include "stdio.h"
#include "stdlib.h"
# define OVERFLOW -2
# define OK 1
#define ERROR 0
//数据类型说明
typedef int ElemType;
typedef int Status;
# define List_init_size 100
//线性表存储空间初始分配量
# define Listincrement 10
//线性表存储空间分配增量
typedef struct{
ElemType *elem; //存储空间基址
int length; //当前长度
int listsize;//当前分配的存储容量
// (以sizeof(ElemType)为单位)
}sqlist;
//初始化线性表:
Status InitList(sqlist &L)
{//构造一个空线性表L
L.elem=(ElemType *) malloc(List_init_size*sizeof(ElemType));
if (!L.elem) exit(OVERFLOW);
L.length=0;
L.listsize= List_init_size;
return OK;
}
//在一个空的线性表中输入N个数据:
Status sqlistinput(sqlist &L,int n)
{int i=0;
if(n>L.listsize)return ERROR;
for(;il.length+1) return ERROR;
e=l.elem[i-1];
return OK;
}
//定义两个数据元素相等的比较函数
Status equal(ElemType e1,ElemType e2)
{if (e1==e2)
return OK;
else
return ERROR;
}
//根据compare()函数在线性表中定位元素e的位置
int LocateElem_sq(sqlist L,ElemType e,Status (*compare)(ElemType,ElemType)) //成功返回位序,否则返回0
{ int i=1;
ElemType *p;
p=L.elem;
while(i<=L.length &&!(*compare)(*p++,e)) ++i;
if (i<=L.length) return i;
else return 0;
}// locateElem_sq
//在线性表中求某元素的前趋结点,如存在则返回其前趋结点pre_e的值,否则返回出错信息
Status PriorElem(sqlist L,ElemType cur_e,ElemType &pre_e)
{ int pos;
pos=LocateElem_sq(L,cur_e,equal);
if(pos==0) return ERROR;
else if(pos==1) return OVERFLOW; //overflow 表示位于第一个
else{
GetElem(L,pos-1,pre_e);
return OK;
}
}
//在线性表中求某元素的后继结点
Status NextElem(sqlist L,ElemType cur_e,ElemType &next_e)
{ int pos;
pos=LocateElem_sq(L,cur_e,equal);
if(pos==0) return ERROR;
else if(pos==L.length) return OVERFLOW; //overflow 表示最后一个
else{
GetElem(L,pos+1,next_e);
return OK;
}
}
//在线性表中插入一个元素
Status Listinsert_sq(sqlist &L,int i,ElemType e) {
ElemType *p,*q,*newbase;
if (i<1 || i>L.length+1) return ERROR;
if (L.length>=L.listsize) {
newbase=(ElemType *) realloc(L.elem, (L.listsize+Listincrement) *sizeof(ElemType));
if (!newbase) exit(OVERFLOW);
L.elem=newbase; L.listsize+=Listincrement ;}
q=&(L.elem[i-1]);
for(p=&(L.elem[L.length-1]);p>=q;--p) *(p+1)=*p;
*q=e; ++L.length;
return OK;
}// listinsert_sq;
//在线性表中删除第i个元素,其结果保留在e中
Status Listdelete_sq(sqlist &l,int i,ElemType &e)
{
ElemType *p,*q;
if (i<1||i>l.length+1) return ERROR;
p=&(l.elem[i-1]);
e=*p;
q=l.elem+l.length-1;
for(++p;p<=q;++p) *(p-1)=*p;
--l.length;
return OK;
}// listdelete_sq;
//将la和lb线性表归并到lc
void mergelist_sq(sqlist la,sqlist lb,sqlist &lc)
{ ElemType *pa,*pb,*pc,*pa_last,*pb_last;
pa=la.elem;
pb=lb.elem;
lc.listsize=lc.length=la.length+lb.length;
pc=lc.elem=(ElemType*)malloc(lc.listsize*sizeof(ElemType));
if (!lc.elem) exit(OVERFLOW);
pa_last=la.elem+la.length-1; pb_last=lb.elem+lb.length-1;
while ((pa<=pa_last)&& (pb<=pb_last))
if (*pa<=*pb) *pc++=*pa++; else *pc++=*pb++ ;
while(pa<=pa_last) *pc++=*pa++;
while(pb<=pb_last) *pc++=*pb++;
} //mergelist_sq;
//对线性表的元素进行排序
Status sortsqlist(sqlist &L)
{//冒泡排序
int i,j,len;
ElemType t;
len=ListLength(L);
for(i=len-1;i>=1;i--)
for(j=0;jL.elem[j+1])
{t=L.elem[j];
L.elem[j]=L.elem[j+1] ;
L.elem[j+1]=t;
}
}
return OK;
}
void main()
{sqlist la,lb,lc;
int n,m,i,e,k,cur_e,pre_e,next_e;
//建立线性表,并输入输出线性表的数据
InitList(la);InitList(lb);
printf("please input la's numbers:n(请输入线性表la的元素个数n)\n");
scanf("%d",&n);
printf("please input la n numbers:( 请输入线性表la的n个元素)\n");
sqlistinput(la,n);
sqlistoutput(la);
printf("\n");
//调用插入函数,对线性表进行插入操作
printf("请输入要插入的元素的位置和插入的值 \n");
scanf("%d%d",&i,&e);
Listinsert_sq(la,i,e);
sqlistoutput(la);
//调用删除函数,对线性表进除删操作
printf("请输入要删除的元素的位置\n");
scanf("%d",&i);
Listdelete_sq(la,i,e);
printf("the dele data is %d\n",e);
sqlistoutput(la);
printf("please input the get data's locate\n");
scanf("%d",&i);
//调用GetElem()函数,取第I个位置的元素的值。
GetElem(la,i,e);
printf("the get data is %d\n",e);
printf("please input the locateelem's data :cur_e\n");
//调用LocateElem_sq()函数,求元素cur_e的位置。
scanf("%d",&cur_e);
k=LocateElem_sq(la,cur_e,equal);
printf("the locate is %d\n",k);
//调用PriorElem()函数,求元素cur_e的前驱。
printf("please input the cur_e data\n");
scanf("%d",&cur_e);
PriorElem(la,cur_e,pre_e);
printf("the pre_e is %d\n",pre_e);
//调用NextElem()函数,求元素cur_e的后继。
printf("please input the cur_e data\n");
scanf("%d",&cur_e);
NextElem(la,cur_e,next_e);
printf("the next_e is %d\n",next_e);
//建立两个线性表并排序然后归并
printf("please input lb's numbers:m\n");
scanf("%d",&m);
printf("please input lb m numbers:\n");
sqlistinput(lb,m);
printf("\n");
sqlistoutput(lb);
sortsqlist(la);
printf("the sort list la is:\n");
sqlistoutput(la);
sortsqlist(lb);
printf("the sort list lb is:\n");
sqlistoutput(lb);
mergelist_sq(la,lb,lc);
printf("la and lb's mergelist is:\n");
sqlistoutput(lc);
}
四、思考题
1.如何实现顺序表的逆置
2.每次删除操作时,都会使得大量的数据元素移动,删除多个数据元素时,就需多次移动数据元素。能否一次进行删除多个数据元素的操作,使得数据元素的移动只进行一次。
实验二:单链表的基本操作
实验学时:2
实验类型:验证
实验要求:必修
一、 实验目的
1.定义单链表的结点类型。
2.熟悉对单链表的一些基本操作和具体的函数定义。
3.通过单链表的定义掌握线性表的链式存储结构的特点。
4.掌握循环链表和双链表的定义和构造方法
二、实验内容
链表的创建、插入与删除操作
三、程序清单
1、运行环境
装有Visual c++6.0的微机一台
2、程序清单
//链表的创建及插入、删除操作
#include "stdio.h"
#include"stdlib.h"
#define NULL 0
#define error 0
#define ok 1
#define overflow -2
#define infeasible -1
//类型定义
typedef int Status;
typedef int ElemType;
//定义链表的存储结构
typedef struct LNode
{int data; //数据域
struct LNode *next; //指针域
}LNode,*LinkList; //链表的类型
Status GetElem_l(LinkList L, int i , ElemType &e)
//L为带头结点的单链表,当第i 个元素存在时,其值赋给e.
{int j;
LinkList p ;
p=L->next; j=1;
while(p&&jnext; ++j;}
if(!p||j>i) return error;
e=p->data;
return ok;
}
//逆序创建链表
void CreatList_L1(LinkList &L,int n) //n为元素个数,L为头结点
{int i;
LinkList p;
L=(LinkList)malloc(sizeof(LNode)); //生成头结点
L->next=NULL;
for(i=n;i>0;i--) //链头插入法
{ p=(LinkList)malloc(sizeof(LNode));
scanf("%d",&p->data);
p->next=L->next;
L->next=p;
}
}
//正序创建单链表
void CreatList_L2(LinkList &L,int n ) ///n为元素个数,L为头结点
{int i ;
LinkList p,q;
L=(LinkList )malloc(sizeof(LNode));
q=L;
for(i=0;idata);
q->next=p;
q=p;
}
q->next=NULL;
}
//输出链表
void print(LinkList L)
{LinkList p;
p=L->next;
while(p)
{printf("%d ",p->data);
p=p->next;
}
}
//链表的插入操作
int ListInsert(LinkList &L,int i,int e)
{LinkList p,s;
int j;
p=L;j=0;
while(p&&jnext; ++j;}
if(!p||j>i-1) return error;
s=(LinkList)malloc(sizeof(LNode));
s->data=e; s->next=p->next;
p->next=s;
return ok;
}
//链表的删除操作
int ListDelete(LinkList &L,int i,int &e)
{LinkList p,q;
int j;
p=L; j=0;
while(p->next&&jnext; ++j;}
if(!(p->next)||j>i-1) return error;
q=p->next;p->next=q->next;
e=q->data;free(q);
return ok;
}
//对链表的元素进行排序
Status sortlinklist(LinkList &L)
{LinkList p,q,r;
ElemType t;
p=L->next; //p指向链表第一个元素结点
while(p->next!=NULL)
{q=L->next; //q指向链表第一个元素结点
while(q->next!=NULL)
{r=q->next;
if(q->data>r->data) //相邻两个元素比较、交换
{t=q->data; q->data=r->data; r->data=t; }
q=q->next;
}
p=p->next;
}
return ok ;
}
void mergelist_l(LinkList la, LinkList &lb, LinkList &lc)
{LinkList pa,pb,pc;
pa=la->next; pb=lb->next ;
lc=pc=la;
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);
}
//主函数通过调用创建、插入、删除用输出函数完成链表的基本操作
void main()
{LinkList L1,L2,L3;
int n,ins,del,i;
//创建一个先进先出单链表
printf("please input fifo linklist's node number n:\n");
scanf("%d",&n);
printf("please input the linklist %d nodes data \n",n);
CreatList_L2(L2,n);
print(L2);
printf("\n");
//创建一个后进先出单链表
printf("please input lifo linklist's node number n:\n");
scanf("%d",&n);
printf("please input the linklist %d nodes data \n",n);
CreatList_L1(L1,n);
print(L1);
printf("\n");
//对链表进行插入操作
printf("please input the insert node's locate i and value e\n");
scanf("%d%d",&i,&ins);
ListInsert(L1,i,ins);
print(L1);
printf("\n");
//对链表进行删除操作
printf("please input the delete node's locate i\n");
scanf("%d",&i);
ListDelete(L1,i,del);
print(L1);
printf("\n%d\n",del);
//对链表进行排序
sortlinklist(L1);
printf("\n the L1 list's sort is:\n");
print(L1);
printf("\n the L2 list's sort is:\n");
sortlinklist(L2);
print(L2);
//对链表进行合并
printf("\n the merge result is : \n" );
mergelist_l(L1,L2,L3);
print(L3);
}
四、思考题
l.如果需要将新结点插入到第i个数据元素之后,算法将如何改动?
2. 双向链表和循环链表的定义和构造方法。
实验三:栈的基本操作
实验学时:2
实验类型:验证
实验要求:选修
一、实验目的
1.会定义顺序栈和链栈的结点类型。
2.掌握顺序栈的插入和删除结点在操作上的特点。
3.熟悉对顺序栈的一些基本操作和具体的函数定义。
二、实验内容
栈的初始化、进栈与出栈等基本操作
三、程序清单
1、运行环境
装有Visual 6.0的微机一台
2、程序清单
#define stackinitsize 20
#define stackincrement 8
#include
#include
typedef struct{
int *base;
int *top;
int stacksize;
}sqstack;
int initstack(sqstack &s)
{s.base=(int * ) malloc(stackinitsize*sizeof(int));
s.top=s.base;
s.stacksize=stackinitsize;
return 1;
}
int push(sqstack &s,int e)
{
s.top)=e;
s.top++;
return 1;
}
int gettop(sqstack s)
{
return *(s.top-1);
}
int emptystack(sqstack s)
{if (s.top==s.base) return 1;
else return 0;
}
int pop(sqstack &s,int &e)
{ if (emptystack(s)) return 0;
--s.top;
*e=*s.top;
return 1;
}
Void main()
{
Sqstack s;
Int n,I,e;
initstack(s);
scanf(“%d”,&n);
for(i=1;i<=n;i++)
{scanf(“%d”,&e);
Push(s,e);
}
While(!emptystack(s))
{pop(s,e);
Printf(“%d ”,e);
}
}
四、思考题
如果两个栈共用一个存储空间,该如何解决?
实验四:队列的基本操作
实验学时:2
实验类型:验证
实验要求:选修
一、实验目的
1.会定义循环队列的结点类型。
2.循环队列的插入和删除结点在操作上的特点。
3.熟悉对循环队列的一些基本操作和具体的函数定义
二、实验内容
循环队列的插入与删除
三、程序清单
1、运行环境
装有Visual 6.0的微机一台
2、程序清单
//--------------对列的顺序存储结构------------------
# include "stdlib.h"
# include "stdio.h"
# include "time.h"
//函数结果状态代码
# define TURE 1
# define FALSE 0
# define OK 1
# define ERROR 0
# define OVERFLOW -2
# define maxqsize 100
typedef int status;
typedef int qelemtype;
typedef struct{
qelemtype *base;
int front;
int rear;
}sqqueue;
//----------循环队列的基本操作的算法描述--------
status initqueue(sqqueue &Q){
//构造一个空队列Q
Q.base=(qelemtype*)malloc(maxqsize*sizeof(qelemtype));
if(!Q.base) return ERROR;
Q.front=Q.rear=0;
return OK;
}
int queuelength(sqqueue Q){
//返回Q的元素个数,即对列的长度
return (Q.rear-Q.front+maxqsize)%maxqsize;
}
status enqueue(sqqueue &Q,qelemtype e){
//插入元素e为Q的新的队尾元素
if((Q.rear+1)%maxqsize==Q.front) return ERROR; //队列满
Q.base[Q.rear]=e;
Q.rear=(Q.rear+1)%maxqsize;
return OK;
}
status dequeue(sqqueue &Q,qelemtype &e){
//若队列不空,则删除Q的队头元素,用e返回其值,并返回OK
//否则返回ERROR
if(Q.front==Q.rear) return ERROR;
e=Q.base[Q.front];
Q.front=(Q.front+1)%maxqsize;
return OK;
}
void main()
{
//测试基本操作
int i,e;
sqqueue Q;
initqueue(Q);
printf("\n");
for(i=1;i<=10;i++)
{
e=i;
enqueue(Q,e);
}
printf("the length of queue is :%d\n",queuelength(Q));
for(i=1;i<=10;i++)
{
dequeue(Q,e);
printf(" %d",e);
}
}
四、思考题
1. 如果循环队列的下标不是从0开始,而是是从1开始,那么头指针加l的操作应如何修改?
2. 在循环队列中判断队空和队满的条件能否一样,为什么?
3. 用另一种不同与上面算法的方法解决“假上溢”问题。
实验五:串的模式匹配
实验学时:2
实验类型:验证
实验要求:选修
一、实验目的
1.会定义定长顺序串的存储结构。
2.掌握定长顺序串的基本运算。
3.了解KMP算法。
二、实验内容
串的模式匹配算法
三、程序清单
1、运行环境
2、程序清单
#include
#include
#include
#include
#define maxstrlen 255 //用可以在255以内定义最大串长
typedef unsigned char SString[maxstrlen+1]; //0好单元可以存放串的长度
int index(SString S,SString T,int pos) {
//利用模式串T的next函数求T在主串S中第pos个字符之后的位置
//其中T非空,1<=pos<=strlength(s).
int i,j;
i=pos;j=1;
while(i<=S[0]&&j<=T[0]) {
if(S[i]==T[j]) {++i;++j;}
else
{i=i-j+2;j=1;}
}
if(j>T[0]) return i-T[0];
else
return 0;
}//index_kmp
void main()
{
SString S={17,'a','c','a','b','a','a','b','a','a','b','c','a','c','a','a','b','c'};
SString T={6,'a','b','a','a','b','c'};
printf("\nit is :%d ",index(S,T,1));
}
//-----------改进的模式匹配算法-------------
//-----------本算法在BC++3.1下调试通过------
//-----------调试时间2002年4月14日----------
#include
#include
#define maxstrlen 255 //用可以在255以内定义最大串长
int next[7];
typedef unsigned char SString[maxstrlen+1]; //0好单元可以存放串的长度
/********************************************
*********************************************/
void get_next(SString T) {
//求模式串T的next函数值并存入数组next。
int i,j;
i=1; next[1]=0; j=0;//
while(iT[0]) return i-T[0];
else
return 0;
}//index_kmp
void main()
{
SString S={17,'a','c','a','b','a','a','b','a','a','b','c','a','c','a','a','b','c'};
SString T={6,'a','b','a','a','b','c'};
//S[0]=17; T[0]=6;
get_next(T);
printf("\nit is :%d ",index_kmp(S,T,1));
}
四、思考题
1.如何使得在串插入和联接时不会上溢。
2.串的其它存储结构的实现。
实验六:矩阵的基本运算
实验学时:2
实验类型:设计
实验要求:选修
一、实验目的
1.了解多维数组的顺序存储结构及其地址计算方式
2.了解特殊矩阵和稀疏矩阵的概念。
3.掌握疏矩阵的压缩存储方式——三元组表。
4.掌握稀疏矩阵的两种转置运算算法。
二、实验内容
数组的稀疏矩阵的三元组存储表示与实现
三、程序清单
1、运行环境
2、程序清单
//—————稀疏矩阵的三元组顺序表存储表示一———
#define MAXSIZE 100 //假设非零元个数的最大值为100
#define ROW 7
#define COL 7
#define OK 1
#define FALSE 0
#include "stdlib.h"
#include "stdio.h"
#include "conio.h"
int a[ROW][COL]={{0,12,9,0,0,0,0},
{0,0,0,0,0,0,0},
{-3,0,0,0,0,14,0},
{0,0,24,0,0,0,0},
{0,18,0,0,0,0,0},
{15,0,0,-7,0,0,0},
{0,0,0,0,0,0,-5}};
typedef int ElemType;
typedef int Status;
typedef struct{
int i,j; //该非零元的行下标和列下标
ElemType e;
}Triple;
typedef union{
Triple data[MAXSIZE+1]; //非零元三元组表, data[0]未用
int mu,nu,tu; //矩阵的行数、列数和非零元个数
}TSMatrix;
void InitTSMatrix(TSMatrix &T)
{ T.tu=0;
}
Status ChangeArrayToMS(int a[][COL],TSMatrix &T)
{ int i,j,k=0;
for(i=0;i0)
{ printf("\n row col val\n");
printf("------------------\n");
for(k=1;k<=T.tu;k++)
printf("(%4d%5d%5d)\n",T.data[k].i,T.data[k].j,T.data[k].e);
}
}
Status TransposeSMatrix(TSMatrix M, TSMatrix &T) {
//采用三元组表存储表示,稀疏矩阵M的转置T
int q,p,col;
T.mu=M.nu;T.nu=M.mu;T.tu =M.tu;
if(T.tu) {
q = 1;
for (col=1 ;col<=M.nu; ++col)
for (p=1; p<=M.tu; ++p)
if (M.data[p].j==col){
T.data[q].i=M.data[p].j ; T.data[q].j=M.data[p].i;
T.data[q].e=M.data[p].e; ++q;}
}
return OK;
} // TrazlsposeSMatrix
Status FastTransposeSMatrix(TSMatrix M, TSMatrix &T){
//采用三元组表存储表示,稀疏矩阵M的转置T(快速转换)
int t,col,p,q,num[COL+1],cpot[COL+1];
T.mu=M.nu;T.nu=M.mu;T.tu= M.tu;
if(T.tu) {
for (col = 1; col<= M.mu;++col) num[col] = 0;
for (t=1;t<=M.tu;++t) ++num[M.data[t].j]; //求M中每一列含非0元素的个数
cpot[ 1 ] = 1 ;
// 求第 col列中第一个非0元素在 b. data 中的序号
for (col = 2;col<= M.nu; ++col) cpot[col]=cpot[col-1] + num[col-1];
for (p=1; p<=M.tu;++p)
{ col=M.data[p].j;q=cpot[col];
T. data[q].i=M.data[p].j; T.data[q].j=M.data[p].i;
T. data[q].e=M.data[p].e; ++cpot[col];
}
}
return OK ;
} // FastTransposeSMatrix
Status AddTSMatrix(TSMatrix A,TSMatrix B,TSMatrix &C)
{ //A+B==>C 两个稀疏矩阵相加结果存于C中
//此算法类似于顺序表的归并算法
int p,q,k;
p=q=k=1;
if (A.mu!=B.mu||A.nu!=B.nu)
return FALSE;
if(A.tu==0&&B.tu==0) {C.tu=0;return OK;}
C.mu=A.mu;C.nu=A.nu;
while(p<=A.tu&&q<=B.tu)
{if ((A.data[p].i==B.data[q].i)&&(A.data[p].j==B.data[q].j))
{ if (A.data[p].e+B.data[q].e)
{ C.data[k].i=B.data[q].i;C.data[k].j=B.data[q].j;
C.data[k].e=A.data[q].e+B.data[q].e;
}
p++;q++;k++;
}
else if((A.data[p].i>B.data[q].i)||
((A.data[p].i==B.data[q].i)&&(A.data[p].j>B.data[q].j)))
{C.data[k].i=B.data[q].i;C.data[k].j=B.data[q].j;
C.data[k].e=B.data[q].e;q++;k++;}
else
{C.data[k].i=A.data[p].i;C.data[k].j=A.data[p].j;
C.data[k].e=A.data[p].e;p++;k++;}
}
while (p<=A.tu)
{C.data[k].i=A.data[p].i;C.data[k].j=A.data[p].j;
C.data[k].e=A.data[p].e;p++;k++;}
while (q<=B.tu)
{C.data[k].i=B.data[q].i;C.data[k].j=B.data[q].j;
C.data[k].e=B.data[q].e;q++;k++;}
C.tu=k-1;
return OK;
}
void main()
{ TSMatrix T,T1,T2;
InitTSMatrix(T);getch();
ChangeArrayToMS(a,T);
PrintTSmatrix(T);getch();
TransposeSMatrix(T,T1);
PrintTSmatrix(T1);getch();
FastTransposeSMatrix(T,T1);
PrintTSmatrix(T1);getch();
AddTSMatrix(T,T1,T2);
PrintTSmatrix(T2);
}
四、思考题
1.稀疏矩阵的十字链表又是如何实现的?
实验七:二叉树的基本操作
实验学时:2
实验类型:设计
实验要求:必修
一、实验目的
1.熟悉二叉树结点的结构和对二叉树的基本操作。
2.掌握对二叉树每一种操作的具体实现。
3.学会利用递归方法编写对二叉树这种递归数据结构进行处理的算法。
二、实验内容
二叉树的创建及各种周游
三、程序清单
1、运行环境
2、程序清单
/* ****************二叉树有关算法*************** */
#include "alloc.h"
#include "stdio.h"
#include "conio.h"
#include "stdlib.h"
#define stackinitsize 100
#define OK 1
#define ERROR 0
#define OVERFLOW -1
typedef int TElemType ;
typedef int Status;
//一一一一一二叉树的二叉链表存储表示一一一一一
typedef struct BiTNode{
TElemType data;
struct BiTNode *lchild,*rchild; //左右孩子指针
}BiTnode,*SElemType,*BiTree;
typedef struct{
//该堆栈的元素是指针类型的
//base和top均是指向指针的指针
SElemType *base;
SElemType *top;
int stacksize;
}sqstack;//堆栈结构
//一一一一一基本操作的函数原型说明(部分)一一一一一
Status CreateBitree(BiTree &T);
//按先序次序输入二叉树中结点的值(一个字符),空格字符表示空树。
//构造二叉链表表示的二叉树T.
Status PreOrderTraverse(BiTree T,Status(*Visit)(TElemType e));
//采用二叉链表存储结构,visit是对结点操作的应用函数。
//先序遍历二叉树T,对每个结点调用函数Visit一次且仅一次。
//一旦visit()失败,则操作失败。
Status InorderTraverse(BiTree T,Status(*Visit)(TElemType e));
//采用二叉链表存储结构,Visit是对结点操作的应用函数。
//中序遍历二叉树T,对每个结点调用函数Visit一次且仅一次。
//一旦visit()失败,则操作失败。
Status PostorderTraverse(BiTree T,Status(*Visit)(TElemType e));
//采用二叉链表存储结构,visit是对结点操作的应用函数。
//后序遍历二叉树T,对每个结点调用函数Visit一次且仅一次。
//一旦visit()失败,则操作失败。
Status LevelIOrderTraverse(BiTree T,Status(*Visit)(TElemType e));
//采用二又链表存储结构,Visit是对结点操作的应用函数。
//层序遍历二叉树T,对每个结点调用函数Visit一次且仅一次。
//一旦visit()失败,则操作失败
sqstack *InitStack()//初始化堆栈
{sqstack *s;
s->base=(SElemType*)malloc(100*sizeof(SElemType));
if(!s->base) return NULL;
s->top=s->base;
s->stacksize=100;
return s;
}
int StackEmpty(sqstack *s) //栈空判别
{return(s->top==s->base);
}
void Pop(sqstack *s,SElemType &e)//弹栈
{
e=*--s->top;
}
Status GetTop(sqstack *s,SElemType &e)
{
if(s->top==s->base) return ERROR;
e=*(s->top-1);
return OK;
}
void Push(sqstack *s,SElemType e)//将元素压入堆栈
{SElemType t;
*s->top++=e;
}
Status CreateBiTree(BiTree &T){
//按先序次序输入二叉树中结点的值(一个字符),空格字符表示空树。
//构造二叉链表表示的二叉树T.
char ch;
ch=getche();
if(ch==' ') T=NULL;
else{
if(!(T=(BiTNode *)malloc(sizeof(BiTNode)))) return(OVERFLOW);
T->data=ch; //生成根结点
CreateBiTree(T->lchild);//构造左子树
CreateBiTree(T->rchild);//构造右子树
}
return OK;
}//CreateBiTree
Status PreOrderTraverse(BiTree T,Status(*Visit)(TElemType e))
//采用二叉链表存储结构,visit是对数据元素操作的应用函数,
//先序遍历二叉树T的递归算法,对每个数据元素调用函数visit。
//调用实例: PreOrderTraverse(T,printElement);
{
if(T){
if (Visit(T->data))
if (PreOrderTraverse(T->lchild,Visit))
if (PreOrderTraverse(T->rchild,Visit)) return OK;
return ERROR;
}else return OK;
}//preOrderTraVerse
Status InOrderTraverse(BiTree T,Status(*Visit)(TElemType e))
//采用二叉链表存储结构,visit是对数据元素操作的应用函数,
//中序遍历二叉树T的递归算法,对每个数据元素调用函数visit。
{
if(T){
if (InOrderTraverse(T->lchild,Visit))
if (Visit(T->data))
if (InOrderTraverse(T->rchild,Visit)) return OK;
return ERROR;
}else return OK;
}//preOrderTraVerse
Status PostOrderTraverse(BiTree T,Status(*Visit)(TElemType e))
//采用二叉链表存储结构,visit是对数据元素操作的应用函数,
//后序遍历二叉树T的递归算法,对每个数据元素调用函数visit。
{
if(T){
if (PostOrderTraverse(T->lchild,Visit))
if (PostOrderTraverse(T->rchild,Visit))
if (Visit(T->data)) return OK;
return ERROR;
}else return OK;
}//preOrderTraVerse
Status PrintElement(TElemType e)
{ //输出元素e的值
printf("%c",e);
return OK;
}
Status InorderTraverseNoRecursion1(BiTree T,Status(*Visit)(TElemType e))
//采用二叉链表存储结构,visit是对数据元素操作的应用函数。
//中序遍历二叉树T的非递归算法,对每个数据元素调用函数visit。
{sqstack *s;
BiTree p;
s=InitStack();p=T;
while(p||!StackEmpty(s))
{
if(p){ Push(s,p);p=p->lchild;}//根指针进栈,遍历左子树
else{ //根指针退栈,访问根结点,遍历右子树
Pop(s,p);if(!Visit(p->data)) return ERROR;
p=p->rchild;
}//else
}//while
return OK;
}//InorderTraVerse1
Status InorderTraverseNoRecursion2(BiTree T,Status(*Visit)(TElemType e))
//采用二叉链表存储结构,visit是对数据元素操作的应用函数。
//中序遍历二叉树T的非递归算法,对每个数据元素调用函数visit。
{sqstack *s;
BiTree p;
s=InitStack();Push(s,T);
while(!StackEmpty(s))
{
while(GetTop(s,p)&&p) Push(s,p->lchild);//向左走到尽头
Pop(s,p); //空指针退栈
if(!StackEmpty(s)) { //访问结点,向右一步
Pop(s,p);if(!Visit(p->data)) return ERROR;
Push(s,p->rchild);
}//if
}//while
return OK;
}//InorderTraVerse1
void main()
{
BiTree t;
printf("\n请按先序遍历输入二叉树(当左右子树为空时用空格输入)\n");
CreateBiTree(t);
printf("\n该二叉树的先序遍历为:\n");
PreOrderTraverse(t,PrintElement);
printf("\n该二叉树的中序遍历为:\n");
InOrderTraverse(t,PrintElement);
printf("\n该二叉树的后序遍历为:\n");
PostOrderTraverse(t,PrintElement);
printf("\n该二叉树的中序遍历为:(用非递归调用1)\n");
InorderTraverseNoRecursion1(t,PrintElement);
printf("\n该二叉树的中序遍历为:(用非递归调用2)\n");
InorderTraverseNoRecursion2(t,PrintElement);
}
四、思考题
1.如何实现二叉树的层次遍历?
2. 如何实现二叉树的先序遍历、中序遍历和后序遍历的非递归算法。
实验八:哈夫曼树与哈夫曼编码
实验学时:2
实验类型:设计
实验要求:选修
一、实验目的
1.了解创建哈夫曼树的基本方法
2.掌握通过赫夫曼树进行赫夫曼编码的基本原理和方法。
二、实验内容
哈夫曼树与哈夫曼编码
三、程序清单
1、运行环境
2、程序清单
#include "stdlib.h"
#include "stdio.h"
#include "conio.h"
#include "string.h"
#define ERROR 0;
#define OK 1;
typedef int Status ;
//哈夫曼树的存储和哈夫曼编码的存储
typedef struct{
unsigned int weight;
unsigned int parent,lchild,rchild;
}HTNode,*HuffmanTree; //动态分配数组存储哈夫曼树
typedef char **HuffmanCode; //动态分配数组存储哈夫曼编码表
Status Select(HuffmanTree HT,int n,int &s1,int &s2) {
//在哈夫曼树HT[1..n] 搜索最大权值和最小权值并用s1,s2 返回它们的下标
unsigned int temp=9999;
int i;
s1=0;s2=0;
for(i=1;i<=n;i++)
if((HT[i].weight0),构造hufmantree HT,并求N个字符有huffman编码HC
HuffmanTree p;
char *cd;
int s1,s2,i,c,m,start,f;
if(n<1) return ;
m=2*n-1; //哈夫曼树的结点数
HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode)); //0号单元未用
p=HT;p++;
for(i=1;i<=n;++i)
{
p->weight=w[i-1];
p->parent=0;
p->lchild=0;
p->rchild=0;
p++;
}//将各结点赋初值
for(;i<=m;++i,++p)
{
p->weight=0;
p->parent=0;
p->lchild=0;
p->rchild=0;
}//后续结点赋初值
for(i=n+1;i<=m;++i)
{ Select(HT,i-1,s1,s2);
//在HT[1..i-1]选择parent为0且weight最小的两个结点,其序号为S1,S2
//每次创建的树放在i的位置其中(i>n)
HT[s1].parent=i;HT[s2].parent=i;
HT[i].lchild=s1;HT[i].rchild=s2;
HT[i].weight=HT[s1].weight+HT[s2].weight;
}
printf("\nthe huffmantree is:\n");
printf(" NO [weight parent lchild rchild]\n");
printf(" --------------------------------\n");
for(i=1;i<=m;i++)
printf("%6d [%6d,%6d,%6d, %6d ]\n",i,HT[i].weight,HT[i].parent,HT[i].lchild,HT[i].rchild);
//从叶子到根逆向求每个字符的Huffman 编码
HC=(HuffmanCode)malloc((n+1)*sizeof(char*));
cd=(char *)malloc(n*sizeof(char));
cd[n-1]='\0';
for(i=1;i<=n;++i)
{ start=n-1;
for(c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent)
if(HT[f].lchild==c) cd[--start]='0';
else cd[--start]='1';
HC[i]=(char*)malloc((n-start)*sizeof(char));
strcpy(HC[i],&cd[start]);
}
free(cd);
}//end huffmancoding
void main()
{
HuffmanTree HT;
int n,i,w[20];
HuffmanCode HC;
printf("please intput n=\n");
scanf("%d",&n);
printf("please input w[%d]:\n",n);
for(i=0;iv[%d] is:%d\n",v,i,dist[i]);
}
实验十:哈希表的基本操作
实验学时:2
实验类型:设计
实验要求:选修
一、实验目的
1.熟悉有关哈希表的基本概念。
2.熟悉构造哈希表的方法。
3.掌握处理哈希冲突的开放定址法。
二、实验内容
哈希表的动态生成与查找
三、程序清单
//定义开放定址哈希表的存储结构
int hashsize[]={13,19,29,37};/*哈希表容量递增表,一个合适的素数序列*/
int m=0;/*哈希表表长,全局变量*/
typedef int ElemType;
typedef int Status;
typedef struct
{
ElemType *elem;/*数据元素存储基址,动态分配数组*/
int count;/*当前数据元素个数*/
int sizeindex;/*hashsize[sizeindex]为当前容量*/
}HashTable;
#define SUCCESS 1
#define UNSUCCESS 0
#define DUPLICATE -1
#define EQ(a,b) ((a)一(b))
#define LT(a,b) ((a)<(b))
#define LQ(a,b) ((a)<=(b))
//哈希表的初始化
Status InitHashTable(HashTable * H)
{/*操作结果:构造一个空的哈希表*/
int i;
(*H).count=0;/*当前元素个数为0*/
(*H).sizeindex=0;/*初始存储容量为hashsize[0]*/
m=hashsize[0];
(*H).elem=(ElemType*)malloc(m*sizeof(ElemType));
If(!(*H).elem)
exit(OVERFLOW);/*存储分配失败*/
for(i-0;ikey!=NULLKEY)/*该单元有数据*/
*p++=*((*H).elem+i);
(*H).count=0;
(*H).sizeindex++;/*增大存储容量*/
m=hashsize[(*H).sizeindex];
p=(ElemType*)realloc((*H).elem,m*sizeof(ElemType));
if(!p)
exit(OVERFLOW);/*存储分配失败*/
(*H).elem=p;
for(i=0;i0)
{
for(i=gap+1;i<=n;i++)
{
j=i-gap;
while(j>0)
if(r[j]>r[j+gap])
{
x=r[j];
r[j]=r[j+gap];
r[j+gap]=x;
j=j-gap;
}
else j=0;
}
gap=gap/2;
}
}
void bubblesort(sqlist r,int n)
{
int i,j,w;
for(i=1;i<=n-1;i++)
for(j=n;j>=i+1;j--)
if(r[j]=p) --high;
a[low]=a[high];
while(low=(b.occurtime)) return 1;
else return 0;
}
double random0_1() /****产生0 到 1 之间的随机数**/
{
static long seed=3;
long a=16807;
long m=2147483647;
long q=127773;
long r=2836;
seed=a*(seed%q)-r*(seed/q) ;
if(seed<0) seed+=m;
return (double)seed/(double)m;
}
void random2(int *durtime,int *intertime) /**产生1 到MAX_DURTIME 之间的随机数*/
{ /**产生1 到MAX_INTERTIME 之间的随机数*/
*durtime=(int)(1+MAX_DURTIME*random0_1()) ;
*intertime=(int)(MAX_INTERTIME*random0_1()) ;
}
eventlist * initlist () /*产生一个空链表,并返回其地址******/
{
eventlist *evp;
evp=(eventlist*)malloc(sizeof(eventlist));
if(!evp)exit(OVERFLOW);
evp->head=NULL;
evp->len=0;
return (evp);
}
int emptyeventlist (eventlist * ev) /******* 检查链表是否为空************/
{
return(ev->len);
}
linklist * delfirst(eventlist * ev) /*** 删除链表中的第一个结点,并返回其地址*/
{
linklist *p;
p=ev->head;
ev->head=p->next;
ev->len--;
return (p) ;
}
event getcurelem(linklist *p) /***返回p 指针所指向结点的元素**********/
{
return(p->data);
}
void orderinsert(eventlist* ev,event en) /***按事件发生的先后将事件en插入有序链表中*/
{
event en_temp;
linklist* s ,*p,*q;
s=(linklist*)malloc(sizeof(linklist));
if(!s)exit (OVERFLOW);
else {
s->data=en;
s->next=NULL;
}
if(ev->head==NULL||(en.occurtimehead->data.occurtime))
{ s->next=ev->head;
ev->head=s;
}
else
{
q=ev->head;
p=q->next ;
en_temp=getcurelem(p);
while((p!=NULL)&& en.occurtime>en_temp.occurtime)
{
q=p;
p=p->next;
en_temp=getcurelem(p);
}
s->next =p;
q->next=s;
}
ev->len++;
}
linkqueue * initqueue() /***创建空队列******/
{
linkqueue * q;
q=(linkqueue*)malloc(sizeof(linkqueue));
if(!q)exit(OVERFLOW);
q->front=q->rear=(qnode*)malloc(sizeof(qnode));
if(!q->front)exit(OVERFLOW);
q->front->next=NULL;
q->len=0;
return(q) ;
}
void enqueue(linkqueue*q,qelemtype e) /***将e结点元素插入队列q中**/
{
qnode *p;
p=(qnode*)malloc(sizeof(qnode));
if(!p)exit(OVERFLOW);
else p->data=e;
p->next=NULL;
q->rear->next=p;
q->rear=p;
q->len++;
}
int queueempty(linkqueue*q) /****检查队列是否为空******/
{
return(q->len);
}
int queuelength(linkqueue*q) /****检查队列的长度*******/
{
return(q->len);
}
qelemtype delqueue(linkqueue *q)
{
qelemtype customer;
qnode *p;
if(q->front==q->rear)exit(ERROR);
p=q->front->next;
customer=p->data;
q->front->next=p->next;
if(q->rear==p)q->rear=q->front;
free(p);
q->len--;
return(customer);
}
qelemtype gethead_q(linkqueue*q) /**返回第一个结点数据域的值***/
{
qelemtype customer;
qnode *p;
if(!queueempty(q))exit(ERROR);
p=q->front->next;
customer=p->data;
return(customer);
}
int minnum(linkqueue*q[]) /***返回几个队列中结点最少的队列的序号**/
{
int min1,min2;
min1=queuelength(q[1])<=queuelength(q[2])? 1 : 2 ;
min2=queuelength(q[3])<=queuelength(q[4])? 3 : 4 ;
return queuelength(q[min1])<=queuelength(q[min2]) ? min1 :min2 ;
}
void openforday() /***初始化环境*****/
{
int i;
totaltime=0; customernum=0;
ev=initlist();
en.occurtime=0; en.ntype=0;
orderinsert(ev,en) ;
for(i=1;i<5;i++)
q[i]=initqueue();
}
void customerarrived() /** 客户到达事件函数**/
{
event en_temp;
qelemtype e_temp;
int i,t,durtime,intertime;
customernum++;
random2(&durtime,&intertime);
t=en.occurtime+intertime;
en_temp.occurtime=t;
en_temp.ntype=0;
if(t