数据结构实验报告

数据结构课程实验报告题 目:姓 名:学 院:班 级:学 号: 互联网域名查询 信息科学与工程学院 ,实验三树和图应用类实验一、 问题定义及i 需求分析1. 问题描述互

数据结构课程实验报告

题 目:姓 名:

学 院:班 级:

学 号: 互联网域名查询 信息科学与工程学院

,

实验三树和图应用类实验

一、 问题定义及i 需求分析

1. 问题描述

互联网域名系统是一个典型的树形层次结构。从根节点往下的第一层是顶层域,如cn 、com 等,最底层(第四层)是叶子结点,如www 等。因此,域名搜索可以看成是树的遍历问题。

2. 实验要求

设计搜索互联网域名的程序。

(1)采用树的孩子兄弟链表等存储结构。

(2)创建树形结构。

(3)通过深度优先遍历搜索。

(4)通过层次优先遍历搜索。

3. 数据输入的形式

输入域名地址。如:www.sohu.com

4. 数据输出的形式

输出对应的ip 地址或者出错时输出“找不到服务器或发生DNS 错误”

二、概要设计

(1)为了实现上述程序的功能,应该以孩子兄弟链表存储树,所以需要树的抽象数据类型。

(2)存入磁盘文件时还需要另一种文件的抽象数据类型。

(3)所有的域名和IP 都是以串的形式存的,所以还需要串的抽象数据类型。

(4)当用户输入站点的域名时,需要将域名分段存储,在搜索时从最根部依次提出来与树的节点域比较,由于用户输入是从最小级到最根级,弹出时与输入顺序相反,比如输入“www.163.com ”,其中:www 是第三级,163是第二级,com 是第一级,所以再与树节点比较时,首先比较com ,然后比较163,最后才比较www 。根据这种“后进先出”的关系,需要栈作为辅助工具,所以需要栈的抽象数据类型。

具体内容:

1)树的抽象数据类型

ADT Tree {

数据对象D :站点域名的集合。

数据关系R :{H}

(1)D 中存在惟一称为根的数据元素root ,它在关系{H}下没有前

驱。

(2)D-{root}之后可划分为D 1, D 2, „, Dn (n >0)对任意的划分都两

两不相交。对任意的(i 1≤i ≤n ),惟一的存在数据元素Xi ∈Di ,

有〈root , Xi 〉∈H ;

(3)对应于D-{root}的划分,H-{〈root ,Xi 〉}(i=1,2,„, n )有惟

一的一个划分H 1, H 2, „, Hn (n >0),对任意的j ≠k (i≤j , k ≤n )

1

,

有H j H k =Φ,并且对任意的i (1≤i ≤n ), H j 是Di 上的二元关系,(Di ,{Hi })是一颗符合本定义的树,成为根root 的子树。

基本操作P :

CreateChild (&T)

初始条件:树根T 已经存在。

操作结果:根据用户输入的域名建立此域名的树链,并以T 为

树根如用户输入www.cau.edu.cn ,则在建立起以T 为

根,cn 为T 的第一个孩子,www 为叶子的树链,所

有的节点都链在孩子域。

InitialTree (&T)

操作结果:通过用户输入的数据,构造域名树。其实是将建好的

域名树链有序地插到树中相应的位置。

Insert (&T)

初始条件:树T 已经存在。

操作结果:插入新的域名及IP 。

CreateTree (&T, *fp)

初始条件:包含树的信息文件已经存在,fp 为指向文件的指

针。

操作结果:通过从文件中读取数据,构造域名树。

DeleteTree (&T)

初始条件:T 已经存在。

操作结果:销毁树,释放空间。

} ADT Tree

2)文件的抽象数据类型

ADT File {

数据对象:D ={a i │a i ∈ElemSet, i =1,2, „, n , n ≥0}

数据关系:R ={〈a i -1, a i 〉│a i -1, a i ∈D , i =1,2, „, n }

基本操作:│

Save (&T)

初始条件:树T 存在。

操作结果:先序遍历树,给叶子节点数据域赋IP 值,同时把相

关信息存入文件。

} ADT File

3)串的抽象数据类型

ADT String {

数据对象:D ={a i │a i ∈ElemSet, i =1,2, „, n , n ≥0}

数据关系:R ={〈a i -1, a i 〉│a i -1, a i ∈D , i =1,2, „, n }

基本操作:

CopyChar (&s, *a)

初始条件:a 是字符串常量。

操作结果:生成一个其值等于a 的串s 。

CopyStr (&s, a)

2

,

初始条件:a 是一个串。

操作结果:生成一个值等于a 的串s 。

CompareStr (a, b)

初始条件:a 和b 是两个串。

操作结果:若a>b返回值>0;若a=b,返回值=0;若a

DeleteString (&s)

初始条件:串s 存在。

操作结果:销毁串。

} ADT String

4)栈的抽象数据类型

ADT Stack {

数据对象:D ={a i │a i ∈ElemSet, i =1,2, „, n , n ≥0}

数据关系:R ={〈a i -1, a i 〉│a i -1, a i ∈D , i =1,2, „, n }约定a 1为栈底,a n 为

栈顶

基本操作:

InitialStack (&s)

操作结果:构造一个空栈。

GetTop (s ,&e)

初始条件:栈s 已经存在且非空。

操作结果:用e 返回栈s 的栈顶元素。

Push (&s, e)

初始条件:栈s 已经存在且非空。

操作结果:插入元素e 为新的栈顶元素。

Pop (&s, &e)

初始条件:栈s 已经存在且非空。

操作结果:删除栈s 的栈顶元素,并用e 返回其值。

DeleteStack (&s)

初始条件:s 已经存在。

操作结果:销毁栈,释放空间。

} ADT Stack

5)本程序5个模块

本程序有5个模块:主程序模块,树模块(实现树抽象数据类型),文件模块(实现文件抽象数据类型),串模块(实现串抽象数据类型),栈模块(实现栈抽象数据类型)

3

,

三、详细设计

typedef unsigned char String[30]; // 存放域名和IP 的串结构

typedef struct TreeNode { // 树的节点

ElemType data,IP; // 数据域(用来放域名)和IP 域(用来放IP )

struct TreeNode *firstchild,*nextsibling; // 指向孩子和兄弟的指针

// 兄弟表示同一层,孩子表示下一层

}TreeNode,*Tree;

typedef struct FileNode { // 存入文件的节点

ElemType data,IP; // 数据域(用来放域名)和IP 域(用来放IP )

int ltag,rtag; // 左右标志域:0表示无孩子,1表示有孩子

}FileNode;

typedef struct SNode {

SElemType data;

分开的一部分

struct SNode *next;

}SNode,*Stack;

(1)基本操作

Stack CreateWeb(){

// 用户输入域名时按 '.' 将域名分开分别放入链式栈中

// 最后将 "root" 入栈,为查找做准备,返回栈顶指针

CopyChar (root,"root" ); // 把 "root" 的值赋给串root InitialStack (webside ); // 初始化链式栈

do{

a =getchar(); // 输入域名

4

// 用户输入的域名存在栈节点 // 数据域(用来存放域名被 '.' // 指针域,指向域名下一部分

,

for (int i=0;a!='.'&&a!='n';i ){

s[i]=a; a=getchar(); // 得到两个 '.' 间的域名,将其值放

入一个串中

}

s[i]='0';

Push (webside,s ); // 将串的值入栈

}while(a!='n');

Push (webside,root ); // 将root 入栈

return webside; // 返回栈顶的指针

} // CreateWeb

void Search(Tree T,Stack &webside,Tree &p){

// 用递归算法通过域名查找相应的IP ,返回相同层的指针

// 若存在则输出IP ,若不存在则输出相应的出错信息

if (T&&webside->next!=NULL){

GetTop (webside,s ); // 取得域名栈顶元素

int t=CompareStr(s,T->data); // 比较域名和树的数据域

if (t>0) Search (T->nextsibling,webside,p);

// 若域名比树节点值大则递归搜索

树节点兄弟

else if(t==0){

Pop (webside,s ); p=T; // 若域名和树节点相同则

域名链式栈节点出栈

Search (T->firstchild,webside,p); // 继续搜索树节点的孩子,

即下一层

} // end else

} // end if

} // Search

(2)关于树的操作

void CreateChild(Tree &T,Stack &webside){

// 用户输入一个网址域名,域名已经被输入到栈中,为域名建立相应的域名树链

if (webside->next!=NULL){

t=new TreeNode; // 新开一个节点

Pop (webside,s ); // 从栈中取出域名第一部分

CopyStr (t->data,s); // 把第一部分赋值给树节点的数据域

t->firstchild=t->nextsibling=NULL; // 把树节点的孩子和兄弟链域暂置空

t->IP[0]='0'; // 把树节点的IP 域置0

T->firstchild=t; // 让根节点的孩子域指向新建的树节点

CreateChild (t,webside ); // 再以此树节点为根建立

5

,

新的树节点,

// 直到把域名全部赋值到树节点中

if (t->firstchild==NULL){

cout<<"请输入域名IP"<

户输入IP

cin>>t->IP;

} // end if

} // end if

} // CreateChild

void Insert(Tree &T){

// 将域名树链插入到树T 中

do{

cout<<"请输入站点域名:"<

webside=CreateWeb(); // 为用户输入的域名建立链表栈

Search (T,webside,p ); // 在树中查找与域名节点相同的节点,

// 每一部分查找相匹配的树节点,返回

指向

// 最后一个与域名节点相同的树节点的

指针

if (p->firstchild==NULL){

if (p->IP[0]!='0') // 若发现输入了重复的域名则给出提示

cout<<"你输入了重复的域名"<

else CreateChild(p,webside ); // 若这个树节点没有孩子,则直接把新的

// 域名建立成新的树,并让这个树节点孩

// 子域指向次新开的域名树。

// 即将域名节点作为此树节点的第一个孩

} // end if

else {

if (webside->next==NULL) // 若发现输入了错误的域名,则给出相应

// 的提示

cout<<"你输入了错误的域名!"<

else{

GetTop (webside,s ); // 若这个树节点有孩子,则在他的孩子的

// 兄弟中找到新开域名树应该插入的位置,

将其插入

6

// 若是叶子节点则要求用

,

t=p->firstchild;

a=CompareStr(t->data,s); // 比较域名与已经存在的

兄弟节点

if (a>0){

CreateChild (p,webside ); // 若域名比兄弟节点小,则

前插

p->firstchild->nextsibling=t;

} // end if

else{

while (t->nextsibling!=NULL&&(a=CompareStr

(t->nextsibling->data,s))<0)

t=t->nextsibling; // 若域名比兄弟节点大,则后插

q=new TreeNode;

Pop (webside,s );

CopyStr (q->data,s);

q->firstchild=NULL;

q->IP[0]='0'; // 给新开节点赋值

q->nextsibling=t->nextsibling;

// 新开节点的兄弟指针指向插入处节//

点的兄弟

t->nextsibling=q; // 插入处的节点的兄弟指针指

向新开节点

CreateChild (q,webside ); // 把整个域名树链插入

} // end else

} // end else

} // end else

DeleteStack (webside );

cout<<"是否要继续输入新域名? (y/n)"<

ch=getchar(); // 获得用户输入的字符

getchar (); // 此为用户输入的回车

}while(ch=='y');

} // Insert

void InitialTree(Tree &T){

// 第一次由用户输入网站域名和IP ,建立节点有序的树

T=new TreeNode; // 新开一个树节点

CopyChar (T->data,"root"); // 建立树的根节点

T->firstchild=T->nextsibling=NULL;

T->IP[0]='0';

Insert (T ); // 把新的域名树链插入树中

} // InitialTree

void CreateTree(Tree &T,FILE *fp){

// 文件已经存在,从文件中读出数据,用递归算法先序遍历构建树的孩子,兄弟二叉链表结构

7

,

if (!feof (fp )){

T=new TreeNode; // 新建树节点

f=new FileNode; // 新建文件节点用于读取磁盘文件中的数据

if ((fread (f,sizeof (struct FileNode),1,fp ))!=1)

cout<<"无法打开文件!"<

CopyStr (T->data,f->data);

CopyStr (T->IP,f->IP); // 将文件节点的IP 的值赋给树节点IP

if (f->ltag)

CreateTree (T->firstchild,fp); // 若树节点有孩子则递归建立其孩子

else T->firstchild=NULL; // 若无孩子则孩子链域置为空

if (f->rtag)

CreateTree (T->nextsibling,fp); // 若树节点有兄弟则递归建立其兄弟

else T->nextsibling=NULL; // 若无兄弟则兄弟链域置为空 delete f;

} // end if

} // CreateTree

void DeleteTree(Tree &T){

// 用递归算法销毁树,释放空间

if (T ) {

DeleteTree (T->firstchild);

DeleteTree (T->nextsibling);

delete T;

} // end if

} // DeleteTree

// 销毁树的左子树 // 销毁树的右子树 // 销毁树的根节点

8

,

(3)串的操作

voidCopyStr (String &a,String b){

// 将串b 的值赋给串a

for (int i=0;b[i]!='0';i )a[i]=b[i];

赋给a

a[i]='0';

} // CopyStr

voidCopyChar (String &a,char* b){

// 将字符串b 的值赋给串a

for (int i=0;*b!='0';b ,i )a[i]=*b;

a[i]='0';

} // CopyStr

// 将b 数组中的值一个一个

int CompareStr(String a,String b){

// 串a 和b 从第一个字符开始比较,直到出现第一个不相同的字符为止

for (int i=0;a[i]!='0'&&b[i]!='0';i ) // 如果a>b,return >0;

if (a[i]!=b[i]) return a[i]-b[i]; // 如果a

if (a[i]!='0') return 1; // 如果a=b,return =0;

else if(b[i]!='0')return -1;

else return 0;

} // CompareStr

(4)栈的基本操作

void InitialStack(Stack &s){

// 初始化空栈

s=new SNode;

s->next=NULL;

} // InitialStack

voidPush (Stack &s,SElemType e){

// 将元素e 插入链式栈中,成为新的栈顶元素

p=new SNode; // 为新的栈顶元素分配空间 CopyStr (p->data,e); // 将e 的值赋给新节点数据域 p->next=s->next; // 插入新的栈顶元素

s->next=p; // 修改栈顶指针

} // Push

voidPop (Stack &s,SElemType &e){

// 若栈为空则给出相应的信息,若不空则删除栈顶元素并将其值赋给e

if (!s->next) cout<<"空栈!"<

CopyStr (e,s->next->data); // 取出栈顶元素的值赋给e p=s->next; // 删除栈顶元素

s->next=p->next;

9

// 新建一个栈头节点 // 头节点指向空

标签: