大二下小学期《数据结构》课程设计报告1

题目一: 基本查找算法比较
1) 对以下 3 种基本的查找算法的性能进行比较:顺序查找,二叉查找树,哈希。算法
包含两步:第一步:从文件中读取数据建查找表;第二步从文件中读取查找关键字
静态查找。
2) 待查找记录的个数不小于 n ( n<=1000000),关键字key 互不相同,取值范围0-109;
查询操作的次数为m(m=100000),每次查询的关键字随机生成,取值范围0-109.
3) 比较的指标为关键字的比较次数(包含建立查找表和查找操作)、内存空间和实际
运行时间。至少用5 组不同的数据作比较,至少包含完全正序(n=10000)、完全逆
序(n=10000)及随机(n=10000、100000,1000000)情况。
4) 为提高比较的可信度,待查找数据和查询关键字数据的生成及排序等预处理要设计
成一个独立功能,并将结果数据保留在data.in 文件中,格式如下:第1 行,用空
格分隔的两个非负整数,分别表示n 和m;接下来的n 行,每行一个非负整数,表
示待查询数据;接下来的m 行,每行一个非负整数,表示查询的关键字。
5) 每次查询都输出到结果文件 data.out 中,一共m 行。每行包含对应的查询关键字的
查询结果。查询结果:成功输出“Yes”,不成功输出“No”。
6) 哈希的散列函数建议使用 key mod P,P 是大于n 的质数,注意内存限制;冲突
消解建议使用简单的线性探查法。当然,有能力的同学可以自己设计哈希函数,使
用统计结果较好的冲突消解方法,如平方探测法。

7) 对结果作简单分析,包括对各组数据得出结果波动大小的解释。



我的解答:

由题目分析得,设计2个程序,其中一个来产生随机数据和key,另外一个程序用来进行对数据的分析。由于rand()函数只能产生0~32767之间的随机数,但是题目要求随机数的范围会很大,怎么才能生成很大的随机数,又不破坏均匀性?我采用的方法是按位随机,每次产生0~9之间的随机数,做为9位数的其中一位,然后生成9次,组合起来就是一个9位的随机数。

对于线性分析,在全局定义一个很大的数组,每次程序将数据读入到数组中,用一个2重循环进行分析。对于二叉树查找,首先构造二叉排序树,然后在对每个key进行查找。对于哈希算法,我才用拉链法,取质数10007进行哈希。

如何得到每种方法的比较次数?我才用设置全局量(longlong),在3种方法的查询函数中,在有比较的时候,全局量自加一,然后在屏幕上输出程序运行完成后的各个次数,对其进行分析。

//============================================================================
// Name        : creat_rand.cpp
// Author      : menglei
// Version     : 2013.8.13  20:19
// Copyright   : Your copyright notice
// Description : 此程序产生5组随机的数据
//============================================================================

#include <iostream>
#include <fstream>
using namespace std;


int min_rand(){
	//返回一个0到9的随机数

	//srand((unsigned)time(NULL));
	return rand()%10;
}
int myRand(int bit){
	//hit表示多少位,返回一个在int范围内bit位的一个随机数
	int randdata = 0;
	while(bit--){
		randdata = 10*randdata +min_rand();
	}
	return randdata;
}

int main() {
	/*如何产生一个很大范围的随机数?
	 *采用按位结合法和丢弃法结合
	 *每位随机0到9,---按位结合
	 超过范围的数字就丢弃掉----丢弃法
	*/
	ofstream fout1("data1_in.txt");
	fout1<<"10000"<<" "<<"100000"<<endl;
	//有序的10000个数字,前面的表示n和m
	for(int i=0;i<10000;i++){
		fout1<<i<<endl;
	}
	//产生100000个key
	for(int i=0;i<100000;i++){
		fout1<<myRand(9)<<endl;
	}
	fout1.close();
	cout<<"finish_data1_in.txt"<<endl;

	ofstream fout2("data2_in.txt");//逆序
	fout2<<"10000"<<" "<<"100000"<<endl;
	for(int i=10000;i>0;i--){
		fout2<<i<<endl;
	}
	for(int i=0;i<100000;i++){
		fout2<<myRand(9)<<endl;
	}
	fout2.close();
	cout<<"finish_data2_in.txt"<<endl;

	ofstream fout3("data3_in.txt");//随机10000三种
	fout3<<"10000"<<" "<<"100000"<<endl;
	for(int i=0;i<10000;i++){
		fout3<<myRand(9)<<endl;
	}
	for(int i=0;i<100000;i++){
		fout3<<myRand(9)<<endl;
	}
	fout3.close();
	cout<<"finish_data3_in.txt"<<endl;

	ofstream fout4("data4_in.txt");//随机100000三种
	fout4<<"100000"<<" "<<"100000"<<endl;
	for(int i=0;i<100000;i++){
		fout4<<myRand(9)<<endl;
	}
	for(int i=0;i<100000;i++){
		fout4<<myRand(9)<<endl;
	}
	fout4.close();
	cout<<"finish_data4_in.txt"<<endl;

	ofstream fout5("data5_in.txt");//随机1000000三种
	fout5<<"1000000"<<" "<<"100000"<<endl;
	for(int i=0;i<1000000;i++){
		fout5<<myRand(9)<<endl;
	}
	for(int i=0;i<100000;i++){
		fout5<<myRand(9)<<endl;
	}
	fout5.close();
	cout<<"finish_data5_in.txt"<<endl;

	return 0;
}


//============================================================================
// Name        : temp1.cpp
// Author      : menglei
// Version     :
// Copyright   : Your copyright notice
// Description : 数据的分析
//============================================================================
#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;

class node{
public:
	node(int value):value(value),left(NULL),right(NULL){};//构造函数
	node(){right = NULL;left =NULL;value = -1;}
	node*getleft(){return left;}
	node*getright(){return right;}
	int getvalue(){return value;}
	void setvalue(int t){value = t;}
	void setleft(node * l){left = l;}
	void setright(node * r){right = r;}
	node *right;
	node *left;
	int value;
};

class searchTree{
public:
	searchTree(int i){init(i);}
	void init(int);
	void deleteTree(node*);
	void insert (node *,int);
	bool find(node *,int,long long & );
	node getRoot(){return root;}
	void midOder(node *);

private:
	node root;
};

void searchTree::init(int a){
	root.left = NULL;
	root.right = NULL;
	root.value = a;
}
void searchTree::deleteTree(node * a){
	if(a != NULL){
		deleteTree(a->getleft());
		deleteTree(a->getright());
		delete a;
	}
}
void searchTree::insert(node *n,int i){
	node * pointer = NULL;
	if(n == NULL){init(i);return ;}
	else pointer = n;
	while(1){
		if(pointer->getvalue() == i) return ;//要插入的节点value和根节点的value相同则返回
		else if(pointer->getvalue() >i){
			if(pointer->getleft() == NULL){ //左侧无节点了,插入
				pointer->left = new node(i);
				return ;
			}else
				pointer = pointer->left;
		}
		else
		{
			if(pointer->getright() == NULL){
				pointer->right = new node(i);
				return;
			}else
				pointer = pointer->right;
		}
	}
}
void searchTree::midOder(node *t){
	if(t != NULL){

		midOder(t->getleft());
		cout<<t->getvalue()<<" ";
		midOder(t->getright());
	}
}
bool searchTree::find(node *t,int v,long long &y){
	//find node value equal to 'v' in tree whose root named 't'

	if(t == NULL){y++;return false; }
	if(t->getvalue() == v){y++; return true;}
	if(t->getvalue() > v){
		y++;
		return find(t->left,v,y);}
	if(t->getvalue() < v){
		y++;
		return find(t->right,y);}
}

int data_array[1000001];   //all scorp
long long x,y,z;//x表示顺序查找次数,y表示二叉树,z表示哈希法
void shunxu(){
		int n,m;   int t;
		ifstream fin3("data_in.txt");
		ofstream fout3("shunxu_out.txt");
		fin3>>n>>m;
		cout<<"n和m的值分别为:"<<n<<" "<<m<<endl;//
		for(int i=0;i<n;i++){
			fin3>>data_array[i];
		}
		//顺序查找,m*n   all "NO" ?
		bool flag = false;
		for(int i=0;i<m;i++){
			fin3>>t;
			for(int j=0;j<n;j++){
				x++;
				if(t == data_array[j]){
					flag = true;
					//找到了就跳出循环
					break;}
			}
			if(flag)
				fout3<<"YES"<<endl;
			else
				fout3<<"No"<<endl;
			flag = false;
		}
		fin3.close();
		fout3.close();
		cout<<"shunxu_finished"<<endl;
}

void tree(){
		int n,m;   int t;
		ifstream fin3("data_in.txt");
		ofstream fout3("tree_out.txt");
		fin3>>n>>m;
		cout<<"n和m的值分别为:"<<n<<" "<<m<<endl;//

		//二叉树查找
		searchTree tree(t);
		node * tempNode = &tree.getRoot();
		for(int i=1;i<n;i++)
		{
			fin3>>t;
			tree.insert(tempNode,t);
		}
		bool flag;
		for(int i=0;i<m;i++){
			fin3>>t;
			flag = tree.find(tempNode,t,y);
			if(flag)
				fout3<<"YES"<<endl;
			else
				fout3<<"NO"<<endl;
		}
		fin3.close();
		fout3.close();
		//tree.midOder(tempNode);
		cout<<"tree_finished"<<endl;
}
struct cnode{
	cnode(){}
	cnode(int n){value = n;next = NULL;}
	int value;
	struct cnode *next;
};
struct cnode nodearray[10007];

void haxi(){
	int n,m,haxi;
	ifstream fin3("data_in.txt");
	ofstream fout3("haxi_out.txt");
	fin3>>n>>m;
	cout<<"n和m的值分别为:"<<n<<" "<<m<<endl;//

	struct cnode *temp_cnode,*t_cnode;
	for(int i=0;i<n;i++){
		//构建哈希表(拉链法)
		fin3>>t;
		haxi = t % 10007;     //10007,为比10000大的质数
		temp_cnode = new cnode(t);
		t_cnode = nodearray[haxi].next;
		nodearray[haxi].next = temp_cnode;
		temp_cnode->next = t_cnode;
	}
	for(int i=0;i<m;i++){
		//查找哈希表
		bool flag = false;
		fin3>>t;
		haxi = t % 10007;
		temp_cnode = nodearray[haxi].next;
		while(temp_cnode != NULL){
			z = z + 1;
			if(temp_cnode->value == t){
				flag = true;

				break;}
			temp_cnode = temp_cnode->next;
		}
		if(flag)
			fout3<<"YES"<<endl;
		else
			fout3<<"NO"<<endl;
	}
	fin3.close();
	fout3.close();
	cout<<"haxi_finished"<<endl;
}
int main() {
	//每次分析一组数据,每组数据的文件标为data_in.txt 。输出文件为3种
	//分别为shunxu_out.txt  tree_out.txt   haxi_out.txt
	shunxu();
	tree();
	haxi();
	cout<<"3种方法的比较次数分别为:"<<endl;
	cout<<x<<"  "<<y<<"  "<<z;
	return 0;
}



测试结果:

第一组:(正序)

顺序查找所需时间为:10

二叉排序树查找时间为:4

哈希表法所用查找时间为:1

3种方法的比较次数分别为:

99999888284762413199917

第二组:(逆序)

顺序查找所需时间为:9

二叉排序树查找时间为:3

哈希表法所用查找时间为:1

3种方法的比较次数分别为:

99999546326485899926

第三组:(随机10000)

顺序查找所需时间为:9

二叉排序树查找时间为:2

哈希表法所用查找时间为:1

3种方法的比较次数分别为:

9999856681741928100014

第四组:(随机100000)

顺序查找所需时间为:82

二叉排序树查找时间为:2

哈希表法所用查找时间为:1

3种方法的比较次数分别为:

99996023492324639998328

第五组:(随机1000000)

顺序查找所需时间为:871

二叉排序树查找时间为:5

哈希表法所用查找时间为:3

3种方法的比较次数分别为:

9994500525528387819985419


ps,第一个交的

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


【啊哈!算法】算法3:最常用的排序——快速排序       上一节的冒泡排序可以说是我们学习第一个真正的排序算法,并且解决了桶排序浪费空间的问题,但在算法的执行效率上却牺牲了很多,它的时间复杂度达到了O(N2)。假如我们的计算机每秒钟可以运行10亿次,那么对1亿个数进行排序,桶排序则只需要0.1秒,而冒泡排序则需要1千万秒,达到115天之久,是不是很吓人。那有没有既不浪费空间又可以快一点的排序算法
匿名组 这里可能用到几个不同的分组构造。通过括号内围绕的正则表达式就可以组成第一个构造。正如稍后要介绍的一样,既然也可以命名组,大家就有考虑把这个构造作为匿名组。作为一个实例,请看看下列字符串: “08/14/57 46 02/25/59 45 06/05/85 18 03/12/88 16 09/09/90 13“ 这个字符串就是由生日和年龄组成的。如果需要匹配年两而不要生日,就可以把正则
选择排序:从数组的起始位置处开始,把第一个元素与数组中其他元素进行比较。然后,将最小的元素方式在第0个位置上,接着再从第1个位置开始再次进行排序操作。这种操作一直到除最后一个元素外的每一个元素都作为新循环的起始点操作过后才终止。 public void SelectionSort() { int min, temp;
public struct Pqitem { public int priority; public string name; } class CQueue { private ArrayList pqueue; public CQueue() { pqueue
在编写正则表达式的时候,经常会向要向正则表达式添加数量型数据,诸如”精确匹配两次”或者”匹配一次或多次”。利用数量词就可以把这些数据添加到正则表达式里面了。 数量词(+):这个数量词说明正则表达式应该匹配一个或多个紧紧接其前的字符。 string[] words = new string[] { "bad", "boy", "baad", "baaad" ,"bear", "b
来自:http://blog.csdn.net/morewindows/article/details/6678165/归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列
插入排序算法有两层循环。外层循环会啄个遍历数组元素,而内存循环则会把外层循环所选择的元素与该元素在数组内的下一个元素进行比较。如果外层循环选择的元素小于内存循环选择的元素,那么瘦元素都想右移动以便为内存循环元素留出位置。 public void InsertionSort() { int inner, temp;
public int binSearch(int value) { int upperBround, lowerBound, mid; upperBround = arr.Length - 1; lowerBound = 0; while (lowerBound <= upper
虽然从表内第一个节点到最后一个节点的遍历操作是非常简单的,但是反向遍历链表却不是一件容易的事情。如果为Node类添加一个字段来存储指向前一个节点的连接,那么久会使得这个反向操作过程变得容易许多。当向链表插入节点的时候,为了吧数据复制给新的字段会需要执行更多的操作,但是当腰吧节点从表移除的时候就能看到他的改进效果了。 首先需要修改Node类来为累增加一个额外的链接。为了区别两个连接,这个把指
八、树(Tree)树,顾名思义,长得像一棵树,不过通常我们画成一棵倒过来的树,根在上,叶在下。不说那么多了,图一看就懂:当然了,引入了树之后,就不得不引入树的一些概念,这些概念我照样尽量用图,谁会记那么多文字?树这种结构还可以表示成下面这种方式,可见树用来描述包含关系是很不错的,但这种包含关系不得出现交叉重叠区域,否则就不能用树描述了,看图:面试的时候我们经常被考到的是一种叫“二叉树”的结构,二叉
Queue的实现: 就像Stack类的实现所做的一样,Queue类的实现用ArrayList简直是毋庸置疑的。对于这些数据结构类型而言,由于他们都是动态内置的结构,所以ArrayList是极好的实现选择。当需要往队列中插入数据项时,ArrayList会在表中把每一个保留的数据项向前移动一个元素。 class CQueue { private ArrayLis
来自:http://yingyingol.iteye.com/blog/13348911 快速排序介绍:快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地
Stack的实现必须采用一种基本结构来保存数据。因为再新数据项进栈的时候不需要担心调整表的大小,所以选择用arrayList.using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Collecti
数组类测试环境与排序算法using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace Data_structure_and_algorithm{ class CArray { pr
一、构造二叉树 二叉树查找树由节点组成,所以需要有个Node类,这个类类似于链表实现中用到的Node类。首先一起来看看Node类的代码。 public class Node { public int Data; public Node Left; public Node Right; public v
二叉树是一种特殊的树。二叉树的特点是每个结点最多有两个儿子,左边的叫做左儿子,右边的叫做右儿子,或者说每个结点最多有两棵子树。更加严格的递归定义是:二叉树要么为空,要么由根结点、左子树和右子树组成,而左子树和右子树分别是一棵二叉树。 下面这棵树就是一棵二叉树。         二叉树的使用范围最广,一棵多叉树也可以转化为二叉树,因此我们将着重讲解二叉树。二叉树中还有连两种特殊的二叉树叫做满二叉树和
上一节中我们学习了队列,它是一种先进先出的数据结构。还有一种是后进先出的数据结构它叫做栈。栈限定只能在一端进行插入和删除操作。比如说有一个小桶,小桶的直径只能放一个小球,我们现在向小桶内依次放入2号、1号、3号小球。假如你现在需要拿出2号小球,那就必须先将3号小球拿出,再拿出1号小球,最后才能将2号小球拿出来。在刚才取小球的过程中,我们最先放进去的小球最后才能拿出来,而最后放进去的小球却可以最先拿
msdn中的描述如下:(?= 子表达式)(零宽度正预测先行断言。) 仅当子表达式在此位置的右侧匹配时才继续匹配。例如,w+(?=d) 与后跟数字的单词匹配,而不与该数字匹配。此构造不会回溯。(?(零宽度正回顾后发断言。) 仅当子表达式在此位置的左侧匹配时才继续匹配。例如,(?此构造不会回溯。msdn描述的比较清楚,如:w+(?=ing) 可以匹配以ing结尾的单词(匹配结果不包括ing),(
1.引入线索二叉树 二叉树的遍历实质上是对一个非线性结构实现线性化的过程,使每一个节点(除第一个和最后一个外)在这些线性序列中有且仅有一个直接前驱和直接后继。但在二叉链表存储结构中,只能找到一个节点的左、右孩子信息,而不能直接得到节点在任一遍历序列中的前驱和后继信息。这些信息只有在遍历的动态过程中才能得到,因此,引入线索二叉树来保存这些从动态过程中得到的信息。 2.建立线索二叉树 为了保
排序与我们日常生活中息息相关,比如,我们要从电话簿中找到某个联系人首先会按照姓氏排序、买火车票会按照出发时间或者时长排序、买东西会按照销量或者好评度排序、查找文件会按照修改时间排序等等。在计算机程序设计中,排序和查找也是最基本的算法,很多其他的算法都是以排序算法为基础,在一般的数据处理或分析中,通常第一步就是进行排序,比如说二分查找,首先要对数据进行排序。在Donald Knuth 的计算机程序设