数据结构实验指导书(朱素英)

《 —	数据结构 —》
实验指导书


朱素英   编 写




适用专业:  计算机科学与技术       
  计算机网络工程         


	
湖南人文科技学院计算机科学技术系
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

  

转载于:https://www.cnblogs.com/wc1903036673/p/3413006.html

你可能感兴趣的:(数据结构实验指导书(朱素英))