数据结构课程实验报告.docx

上传人:b****6 文档编号:3692370 上传时间:2022-11-24 格式:DOCX 页数:33 大小:238.46KB
下载 相关 举报
数据结构课程实验报告.docx_第1页
第1页 / 共33页
数据结构课程实验报告.docx_第2页
第2页 / 共33页
数据结构课程实验报告.docx_第3页
第3页 / 共33页
数据结构课程实验报告.docx_第4页
第4页 / 共33页
数据结构课程实验报告.docx_第5页
第5页 / 共33页
点击查看更多>>
下载资源
资源描述

数据结构课程实验报告.docx

《数据结构课程实验报告.docx》由会员分享,可在线阅读,更多相关《数据结构课程实验报告.docx(33页珍藏版)》请在冰豆网上搜索。

数据结构课程实验报告.docx

数据结构课程实验报告

 

数据结构课程实验报告

 

编译平台:

VSTS2008StandardEdition

西安交通大学

 

【实验目的】

熟悉数据结构在软件写作中的应用,提高程序写作能力,思维能力。

培养不怕困难勇于“调试”的精神。

了解堆,优先队列,二叉树,二叉搜索树,字符串匹配算法。

最重要的是深入理会递归思想。

【实验题目】

字符串匹配、二叉树、搜索的训练

【实验内容】

1、利用哈希函数设计一种字符串匹配算法:

问题描述如下:

已知目标串target[0..n-1],模式串pattern[0..m-1],现在给出如下的算法伪代码描述:

functionsearch(stringtarget[0..n-1],stringpattern[0..m-1]){

hash_pattern=hash(pattern[0..m-1])//计算模式串的哈希值

hash_target=hash(target[0..m-1])//计算目标串的子串(子串的长度和模式串的长度是一样的)的哈希值

forifrom0ton-m-1

{

ifhash_pattern==hash_target//判断模式串的哈希值和目标子串的哈希值是否一致

iftarget[i..i+m-1]==pattern[0..m-1]//模式串的哈希值和目标子串的哈希值即使一致了,也要检查模式串和目标子串是否真的相等,这主要因为哈希函数中的同义词现象

returni

hash_target=hash(s[i+1..i+m])//将目标子串向后移动一位,并且计算新目标子串的哈希值

}

returnnotfound

}

你需要做如下工作:

1)设计一种哈希函数,用来方便的计算给定目标串的哈希值;

2)实现该算法,并验证其有效性;

3)分析该算法的时间性能

2、编写一个程序判断两棵二叉树是否是相似的,关于相似性的定义如下:

这两棵二叉树或者都是空树,或者非空且有相似的左子树和右子树

3、重新编写一个栈数据结构,该数据结构采用的物理实现是堆。

每个栈元素在压栈的时候可以附加一个时间标签(时间标签是一个从0到max的值,下一个元素在压栈的时候,则这个元素的时间标签值要大于上一个压栈的元素时间标签值)。

1)重写push、pop、getTop等栈数据类型所需要的操作;

2)以大O的形式分析这些操作的时间复杂度。

4、重新编写一个优先队列的数据结构,该数据结构采用的物理实现是BST。

【实验步骤】

第一题

【需求分析】

根据数学狭义函数的定义,一个函数的函数值随参数的确定而确定。

推广到泛函(参数不一定是数的称泛函)也一样,所以如果两个参数的函数值不相等,则两个参数绝对不相等。

但是反之不成立。

因为存在多重解(同义词)。

根据这个性质,我们定义一个

Hash函数从“字符串域”到“非负整数域”,对应法则是

H(x)=x各个字符的ASCII编码算术和。

比如H(“AB”)=65+66=131;这种方法的好处是很容易从前一个Hash值计算出nextHash值,且计算速度很快。

坏处是重码比较多。

先对模式串求Hash函数值,保存在hash_pattern中,这是个定值。

然后对目标传的各个子串(长度同模式串)求Hash函数值。

从Hash的定义上来看,目标子串后移一位,很容易就能从前一个Hash函数值中计算出当前字串的Hash值。

然后比较两个的hash值,如果不等,那么肯定不可能匹配。

如果相等,进一步检查。

检查方法是逐字比较,BruteForce穷举法。

我们让目标串当前字符指针和模式串指针如果指的值相等,那么一直向后移。

如果最后发现模式串指针到达“尽头”,那么说明逐字相等。

【源代码】

/*--------------------------------------------------------------------------

StringMatching.CPP--whethertargetstringandpatternstringmatch

Versionalpha

(c)XianfuPan,2010

---------------------------------------------------------------------------*/

#include"stdio.h"

#include

#include"string.h"

#defineTest

constintTAR=30;

constintPAT=10;

 

intHash(constchar*aKeyStr,constintnLen)//getHashvalue

{

char*p=(char*)aKeyStr;

inti=0;

inth=0;

while(i++

{

h=(h+*p++);//addeveryASCIIcode

}

returnh;

}

intsearch(constchar*TargetStr,constchar*PatternStr)//search

{

char*p1=(char*)(TargetStr);

constintlen=strlen(PatternStr);

inthash_target=Hash(p1,len);

constinthash_pattern=Hash(PatternStr,len);

while('\0'!

=*p1)

{

if(hash_target==hash_pattern)

{

char*temp=p1;

char*p2=(char*)PatternStr;

while(*temp!

='\0'&&*p2!

='\0'&&*temp++==*p2++)

//onlyifeverycharacterisequalcan*preachtheend(*p2==0);

if(*(p2)=='\0')

returnp1-TargetStr;//p1-TargetStristhepositionfirstmatch

}

//ifHashvauleisnotequal,getnextsubString'sHashvalue;

hash_target=(hash_target-*p1)+*(p1+len);

p1++;

}

return-1;//ifcan'tmatch,thenreturn-1

}

intmain(intarg,char*argv[])

{

std:

:

cout<

:

endl;

std:

:

cout<

:

endl;

std:

:

cout<

:

endl;

return0;

}

 

【运行结果】

经验证,16,-1,14都是预期的值,该算法是有效的。

 

【复杂度分析】

假设目标串的长度是m,模式串的是n,那么计算两个串Hash值耗费的时间是

n+n=2n,而从前一个字串hash值计算后一个基本上不耗时,耗费时间为1(常数)。

通过比较,如果任意和模式串等长度的子串hash值都不等于模式串Hash值,那么就是匹配失败。

AMLunsucc=m–n+1;

如果成功的话,平均比较(m–n+1)/2次。

而每次计算hash值的时间又是1,平均一共计算了(m–n+1)/2次Hash值,若是两个Hash值相等,那么继续逐字比较,消耗的时间也不过是n/2所以时间复杂度应该是O(mn),但是值得注意的是;两个Hash值相等的概率比较小,虽然复杂度仍然是O(mn)但是运算次数肯定较原版bruteforce少。

【小结】

关键是如何从前一个Hash值很快得到下一个Hash值。

本题加深了同义词的概念的理解。

Hash值相等,并不一定关键码相同。

第二题

【需求分析】

判断两棵二叉树是否相似,首先得得到两棵二叉树,笔者采用输入字符串的方式建立二叉树。

假定”RefValue”=‘#’表示一个结点无孩子,应为二叉树不是线性结构,采用前序遍历顺序穿插RefValue的字符串(线性结构)来构建一个非线性的二叉树

比如:

字符串“AB#D##C##”就构建出如下的树

关于相似性的定义如下:

这两棵二叉树或者都是空树,或者非空且有相似的左子树和右子树

由定义:

很容易得出关键代码为

Similar(a,b)

If(a空&&b空)Return相似//递归终止条件

If(a不空&&b不空&&similar(a的左子树,b的左子树)&&similar(a的右子树,b的右子树)

Return相似

Else

Return不相似

完全是运用定义。

现在提供版本二,也是定义,先假定相似,给flag赋予true;然后把flag的引用传递到各个递归层,(级数越高引用越多,但是flag的值始终是引用传递,任意一个递归层改变了参数值就会影响到Flag的真假)运用相似的定义去尝试否定flag。

一旦有一个某级子树不相似,就给flag赋假。

如果没有找到一个子树不是对应相似的,那么flag保持默认值为真。

【源代码】

/*--------------------------------------------------------------------------

SIMILARBinaryTree.CPP--judgementtotwobinarytreeswhethersimilar

alpha

(c)XianfuPan,2010

---------------------------------------------------------------------------*/

#include"stdio.h"

#include

//#defineTest

template

classBinTreeNode

{

public:

BinTreeNode(Tx,BinTreeNode*left=NULL,BinTreeNode*right=NULL)

:

m_Data(x),m_LeftChild(left),m_RightChild(right)

{}

~BinTreeNode()

{}

public:

//becauseSimilarfunctionisaglobalfunctionratherthanamemberfunction,sopublic:

Tm_Data;

BinTreeNode*m_LeftChild,*m_RightChild;

};

template

classBinTree

{

public:

BinTree()

{

m_Root=NULL;

}

BinTree(constchar*strTree)//constructortobuildatree

{

char*p=(char*)strTree;

CreateTree(p,m_Root);//PreOrdertocreateatree

}

//'&'isveryimportant.Itplayarolepassingdownaddress

voidCreateTree(char*&p,BinTreeNode*&subTree)

{

if('\0'!

=*p)

{//define'#'amarktodeclarethatthisnodehasnoleft/rightchildnode

if('#'!

=*p)

{//allocatememoryandinitialthenodewithdata*p,andNULL,NULL

subTree=newBinTreeNode(*p);

p++;

//CreateSubTreeforbothchildrenofthisTree

CreateTree(p,subTree->m_LeftChild);

CreateTree(p,subTree->m_RightChild);}

else

{

p++;

subTree=NULL;//if'#'==*pthen,nochild,soassignNULL

}

}

}

~BinTree()//definedestructorincaseofmemoryleak

{

Destroy(m_Root);

}

voidPreOrder()

{

std:

:

cout<<"PreOrderTraverseareasfollows"<

:

endl;

PreView(this->m_Root);

}

voidPreView(BinTreeNode*subTree)

{

if(NULL!

=subTree)

{

std:

:

cout<m_Data<

:

endl;

PreView(subTree->m_LeftChild);

PreView(subTree->m_RightChild);

}

}

voidDestroy(BinTreeNode*aTree)

{

if(NULL!

=aTree)

{

Destroy(aTree->m_LeftChild);

Destroy(aTree->m_RightChild);

#ifdefTest

std:

:

cout<<"deleting"<

:

endl;

#endif

deleteaTree;//ONLYpostOrderDeletingisavailabletoDestroy;

}

}

 

public:

BinTreeNode*m_Root;

};

//Version1,Definationbased;

template

boolSimilar(BinTreeNode*aTree,BinTreeNode*bTree)

{//iftheyarebothempty,accordingtodefination,similar

if(NULL==aTree&&NULL==bTree)

returntrue;

if(NULL!

=aTree&&NULL!

=bTree

//ifbotharenot,andtheirchildrenaresimilarrespectly

&&Similar(aTree->m_LeftChild,bTree->m_LeftChild)

&&Similar(aTree->m_RightChild,bTree->m_RightChild))//

returntrue;

else//else,notsimilar;

returnfalse;

}

//Version2,Greedalgorithm

template

voidSimilar(BinTreeNode*aTree,BinTreeNode*bTree,bool&flag)

{

if((NULL==aTree)&&(NULL==bTree))//onlyiftheyareempty

return;

if((NULL!

=aTree)==(NULL!

=bTree))

{//onlyiftheyareemptyornotsimultaneously,theyarelikelytobesimilar

Similar(aTree->m_LeftChild,bTree->m_LeftChild,flag);

Similar(aTree->m_RightChild,bTree->m_RightChild,flag);

return;

}

else//oneisemptybutonenot,whichdrawaconclusionthatnotsimilar

{

flag=false;

return;

}

}

 

intmain(intargc,char*argv[])

{

charbuffer[50];

std:

:

cout<<"inputfirsttreelike\"ABC##DE#G##F###\""<

:

endl;

std:

:

cin>>buffer;

BinTreefTree(buffer);

std:

:

cout<<"inputsecondtreelike\"ABC##DE#G##F###\""<

:

endl;

std:

:

cin>>buffer;

BinTreesTree(buffer);

fTree.PreOrder();

sTree.PreOrder();

std:

:

cout<<"------------------------------------------"<

:

endl;

boolflag=Similar(fTree.m_Root,sTree.m_Root);//Similarfunctionversion1

if(flag)

std:

:

cout<<"Theyaresimilar"<

:

endl;

else

std:

:

cout<<"Theyarenotsimilar"<

:

endl;

flag=true;//assumptheyaresimilar,thenmakeeveryattemptodenyit,

//followingfunctiondoesthejob

Similar(sTree.m_Root,fTree.m_Root,flag);//Similarfunctionversion2

std:

:

cout<

:

endl;

return0;

}

【运行结果】

相似的情况不相似的情况

【复杂度分析】

递归算法,在similar(n-1)返回前,similar(n)被挂起。

因为是遍历(变相遍历)二叉树,所以时间复杂度是:

O(n)n为二叉树的结点数。

但是注意到&&运算的短路性质,故真正用的时间是O(logn);

 

【小结】

理解递归思想很重要,递归的思想来自于事物的定义。

把大的问题分解成小的问题,但是小问题和大问题“相似”,那么就可以考虑用递归。

每一层递归只要考虑当前层就行,另外递归需要一个终止条件,那就是找到n=1时,强行定义f

(1)=某个值。

 

第三题

【需求分析】

依据题意,编写一个基于堆的栈,就是说:

对外部来说,栈还是栈结构,即FILO或者LIFO先进后出或后进先出。

但是,这个栈是基于堆的,对于内部来说,数据是按照堆序排列的。

所以说如果从栈的操作PushPop转化到堆的操作Insert,Remove就是一个最大的问题。

由于栈表示的是一种时间上的先后,基于数组或者链表的栈时间上的先后性其实就反映在空间上的先后性。

因为后(后表示时间概念)进来的元素总是比先进来的元素存放的地址(地址表示空间概念)靠后。

两者的关系是一次线性对应。

而堆序只是一种数值大小的先后,空间的先后无法反映到进栈时间的先后,当栈Push的时候,只要按堆序存入,调整位置就行。

当然没有问题,而当Pop的时候,存在堆中的数据因为缺乏一种空间上的顺序性,无法确定是哪个元素出栈。

解决这个问题的方法就是给进入的元素增加时间标签这个信息,使得进了堆的元素在Pop的时候能够知道是哪个该出堆了。

时间标签的值就是栈大小的值,或者说是栈顶元素的位置。

 

【源代码】

/*--------------------------------------------------------------------------

HeapBasedStack.CPP--aStackBasedOnHeap(minHeap)

alpha

(c)XianfuPan,2010

---------------------------------------------------------------------------*/

#include"stdio.h"

#include

#defineTest

constintMAX=40;

template

structNode

{

Tm_Data;

intm_iTimeTag;

//whenfunctionargumentpassdown,ituseoperator=indirectly;

//you'dbetteroverloadoperator=,thoughhereisnotnessesary

Nodeoperator=(Node&other)

{

m_Data=other.m_Data;

m_iTimeTag=other.m_iTimeTa

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高中教育 > 语文

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1