k=(int)(A[i]*100);
B[k][index[k]]=A[i];
index[k]++;
}
//在数组B[0-9][]中进行快速排序
for(i=0;i<100;i++){
Quick_sort(B[i],0,index[i]);
}
//排好后再复制回数组A中
k=0;
for(i=0;i<100;i++){
for(j=1;j<=index[i];j++)
if(index[i]!
=0){
A[k]=B[i][j];
k++;
}
}
}
五、实验内容与结果(程序的输入,输出,结果,演示界面)
(1)程序输入:
程序输入包括,10,1000,10000,100000,0,-1。
1)如果输入10,则给出各种排序结果;2)如果输入1000,10000,100000,则选择某种算法,观察在该算法下排序所用时间;3)如果输入0,则给出每种排序在6种样本下的排序时间和平均时间;4)如果输入-1程序结束!
5)如果输入其它数据则给出错误信息,程序继续执行。
输入数据前界面显示:
(2)程序输出结果及界面演示
1)输入10,给出随机生成的10个数据,在各种排序下的结果:
2)输入1000,10000,100000,则选择某种算法,观察在该算法下排序所用时间;
输入1000,然后输入1、2、3、4、5、6选择各排序算法,观察在各排序算法下对1000个随机产生的数据进行排序所用时间:
输入10000,然后输入1、2、3、4、5、6选择各排序算法,观察在各排序算法下对10000个随机产生的数据进行排序所用时间:
输入100000,然后输入1、2、3、4、5、6选择各排序算法,观察在各排序算法下对100000个随机产生的数据进行排序所用时间:
3)输入0则给出每种排序在6种样本下的排序时间和平均时间:
(注:
桶排序时有问题,在对数量小时能成功,数据太大时就会出现问题。
原因是我用的静态二维数组,所以数据大时堆栈空间中存不下所有二维数组的数据,所以出现问题)
4)如果输入其它数据则给出错误信息,程序继续执行。
六、各种排序算法的性能分析
(1)合并排序算法的性能分析
合并排序是一个时间复杂度为O(n*lgn)的排序算法,而且是一种稳定的排序算法。
在最好和最坏情况下的时间复杂度都是O(n*lgn),而合并排序的空间复杂度为O(n);
对第五条中的输入0,则给出每种排序在6种样本下的排序时间和平均时间,进行分析可以看出。
在数据数量时,合并排序要优于插入排序、和冒泡排序,但是不如快速排序、桶排序和希尔排序性能好。
在数据数量较小时,合并排序不如其他5种排序性能好,这是因为合并排序在最好和最坏情况下的时间复杂度都是O(n*lgn)。
(2)插入排序算法的性能分析
插入排序是一个时间复杂度为O(n*n)的排序算法,而且是一种稳定的排序算法。
插入排序的空间复杂度为O
(1)。
在最好情况下,待排数据为升序排列好的数据,此时插入算法只要进行的比较操作需(n-1)次即可,最坏况下,待排数据为逆序,此时需要进行的比较共有n(n-1)/2次。
对第五条中的输入0,则给出每种排序在6种样本下的排序时间和平均时间,进行分析可以看出。
在数据数量较大时,插入排序只优于冒泡排序算法。
而在数据数量较少时,插入排序要优于合并排序和冒泡排序,但是不如桶排序、快速排序和希尔排序性能好。
(3)希尔排序算法的性能分析
希尔排序算法是一个时间复杂度为O(n*n)的排序算法,但是一种不稳定的排序算法。
希尔排序的空间复杂度为O
(1)。
希尔排序的执行时间依赖于增量序列。
对第五条中的输入0,则给出每种排序在6种样本下的排序时间和平均时间,进行分析可以看出。
在数剧数量较大和较小情况下,另外五种算法相比较,希尔排序要比合并排序、插入排序和冒泡排序算法性能好,但是不如桶排序和快速排序性能好。
(4)快速排序的性能分析
快速排序算是一个时间复杂度为O(n*lgn)的排序算法。
空间复杂度为O(n*lgn),而且是一个不稳定的排序算法。
快速排序最好时间复杂度为O(n*lgn),最坏时间复杂度为O(n*n)。
对第五条中的输入0,则给出每种排序在6种样本下的排序时间和平均时间,进行分析可以看出。
在数剧数量较大和较小情况下,快速排序要比插入排序、冒泡排序、希尔排序和合并排序都要好,只比桶排序的时间复杂度差而已。
(5)冒泡排序算法的性能分析
冒泡排序是一个时间复杂度为O(n*n)的排序算法。
在最好情况下时间复杂度为O(n),在最坏情况下时间复杂度为O(n*n)。
冒泡排序的空间复杂度为O
(1),而且冒泡排序是一种稳定的排序算法。
对第五条中的输入0,则给出每种排序在6种样本下的排序时间和平均时间,进行分析可以看出。
当数据量比较少时,冒泡排序只比合并排序算法性能好,在数据量较大时冒泡排序比其他性能都差。
当数据量特别大时,冒泡排序性能非常差,无法忍受!
(6)桶排序算法的性能分析
桶排序的平均时间复杂度为O(n+c),最好情况下时间复杂度为O(n)。
桶排序的空间复杂度为O(m+n),其中m为桶的数量,c=n*(lgn-lgm)。
桶排序是一种稳定的排序算法。
对第五条中的输入0,则给出每种排序在6种样本下的排序时间和平均时间,进行分析可以看出。
桶排序是六种排序算法中性能最好的排序算法。
总结:
通过上面的分析可以知道,时间复杂度为O(n*n)的排序算法为:
插入排、冒泡排序和希尔排序算法,时间复杂度为O(n*lgn)的排序算法为:
合并排序和快速排序算法,时间复杂度为O(n+c)的排序算法为桶排序。
稳定的排序算法为:
合并排序、插入排序、冒泡排序和桶排序算法。
不稳定的排序算法为:
希尔排序和快速排序算法。
空间复杂度为O
(1)的排序算法为:
插入排序、希尔排序和冒泡排序。
空间复杂度为O(n)的为合并排序。
空间复杂度为O(n*lgn)的是快速排序。
空间复杂度为O(m+n)的是桶排序。
在数据量较少时,六种排序算法的性能从高到低依次为:
桶排序>快速排序>希尔排序>插入排序>冒泡排序>合并排序。
在数据量较大时,六种排序算法的性能从高到低依次为:
桶排序>快速排序>希尔排序>合并排序>插入排序>冒泡排序
实验二红黑树、二叉搜索树的实现和性能比较
一、实验环境
操作系统:
WindowsXP操作系统
编程语言:
C语言
开发工具:
MicrosoftVisualC++6.0
二、问题描述
实现红黑树、二叉搜索树相关算法:
插入(红黑树涉及树的调整:
左旋、右旋等),删除,搜索(指定Key值节点)。
另外,红黑树实现计算树黑高的算法。
三、实验要求
(1)插入测试,输入8,11,17,15,6,1,22,25,27,建立红黑树,按照红黑树信息输出方式输出整棵红黑树以及黑高。
(2)删除测试,删除1)中红黑树中Key=15的节点,按照红黑树信息输出方式输出调整后的整棵红黑树以及黑高。
(3)随机产生300,000个不同自然数Key值(1-300,000,每个数出现一次,出现顺序随机),建立红黑树,查找Key=15000的节点,输出查找花费时间。
用上面的数据,建立二叉搜索树,查找Key=15000的节点,输出查找花费时间。
(4)重复3-5次3)中操作,求各自平均时间。
(5)在
(1)-(4)的红黑树算法基础上修改完成P30714.1-4算法OS_Key_Rank(T,k).输入1,2,3,4,5,6,7,8建树,k=6,输出OS_Key_Rank的返回值。
四、红黑树与二叉树算法原理与算法语言描述
(1)二叉树
1)二叉树的结构定义:
二叉树的结构包括二叉树节点和二叉树两部分。
分别定义为结构体search_tree_node和search_tree。
其中定义二叉树节点包括关键字、左孩子、右孩子、和父节点。
二叉树定义为包含一个节点结构的root跟节点。
定义如下:
structsearch_tree_node{//二叉树的节点结构
intkey;
structsearch_tree_node*left;
structsearch_tree_node*right;
structsearch_tree_node*p;
};
structsearch_tree{//二叉树结构
structsearch_tree_node*root;
};
2)二叉树插入函数:
从二叉树根节点开始依次查找待插入节点在二叉树中的位置,若该节点的关键字大于待插入关键字则继续向左子树查找,若小于待插入关键字则向右子树查找。
直至查找到叶子节点,将待插入节点作为该叶子节点的孩子,插入完毕。
算法描述如下:
voidtree_insert(structsearch_tree*T,structsearch_tree_node*z){//将新值V插入到二叉树T中
structsearch_tree_node*x;
structsearch_tree_node*y;
y=NULL;
x=T->root;
while(x!
=NULL){
y=x;
if(z->keykey)
x=x->left;
else
x=x->right;
}
z->p=y;
if(y==NULL){
T->root=z;
}
elseif(z->keykey)
y->left=z;
else
y->right=z;
}
3)二叉树查找函数:
采用递归算法实现。
实现代码如下:
//二叉树查找
structsearch_tree_node*tree_search(structsearch_tree_node*x,intk){//给定指向树根的指针x和关键字k,返回指向k的节点的指针
if(x==NULL||k==x->key)
returnx;
if(kkey)
returntree_search(x->left,k);
else
returntree_search(x->right,k);
}
(2)红黑树
1)红黑树的结构定义:
定义红黑树结构为结构体,包括红黑树节点rb_tree_node和红黑树rb_tree。
其中红黑树节点结构包括:
关键字、左孩子、右孩子、父节点和颜色。
红黑树结构体包括:
根节点root和空节点nil。
具体定义如下:
structrb_tree_node{//红黑树节点
intkey;
charcolor;
structrb_tree_node*left;
structrb_tree_node*right;
structrb_tree_node*p;
};
structrb_tree{//红黑树
structrb_tree_node*root;
structrb_tree_node*nil;
};
2)红黑树插入后修复函数:
红黑树的的插入函数算法与二茶树插入算法类似,这里就不重复讨论了,下面主要关注插入成功后,为保证红黑树的性质而进行的修复工作。
红黑树插入节点后可能会破坏红黑树的第4条性质,为了回复红黑树的性质而对红黑树进行调整:
根据Z的父节点是Z的祖节点的左子节点还是右子节点,分为两组对称的情况,每组有3种情况。
下面我们只对其中一组进行说明,以Z的父节点是Z祖节点的左子节点为例。
第一种情况:
z的叔父节点是红色的。
在这种情况下,将父、叔节点都着为黑色,再将子树根节点着为红色,那么子树的黑高度没有发生改变,而且红黑性质得得到了调整。
此时,再将Z指向子树的根节点,向上递归恢复红黑特性。
第二种情况:
Z的“叔父”节点是黑色的,Z的父节点的左子节点。
将Z的父节点与祖节点进行一次右旋,并把父节点着黑色,原来的祖节点着红色。
这些子树的红黑特性得到了恢复,而且子树的黑高度没有变化。
另外,由于子树根节点已经是黑色了(这个节点不会出现父子同为红色的问题了),所以不必再向上递归了,此时整个树的红黑特性都已经是正确的了。
第三种情况:
Z的“叔父”节点是黑色的,Z的父节点的右子节点。
将Z本身与其父节点进行一次左旋,让Z指向原来的父节点,就可以调整为情况二,而情况二已经得到解决。
红黑树插入后修复函数算法语言描述为:
voidrb_insert_fixup(structrb_tree*T,structrb_tree_node*z){//插入修正红黑树
structrb_tree_node*y;
while(z->p->color=='R'){
if(z->p==z->p->p->left){
y=z->p->p->right;
if(y->color=='R'){
z->p->color='B';
y->color='B';
z->p->p->color='R';
z=z->p->p;
}
else{
if(z==z->p->right){
z=z->p;
left_rotate(T,z);
}
z->p->color='B';
z->p->p->color='R';
right_rotate(T,z->p->p);
}
}
else{
//与上述代码类似,将right与left交换即可
}
T->root->color='B';
}
3)红黑树删除后修复函数:
红黑树的删除操作比较简单,就不论述了,下面对较为复杂的删除后调整操作进行讨论。
如果Y指向的节点是个红色节点,那么直接删除掉Y以后,红黑性质不会被破坏。
操作结束。
下面就具体地分析如何恢复第1、2、4三个可能被破坏的红黑特性:
我们知道,如果X指向的节点是有红黑两色,或是X是根节点时,只需要简单的对X进行一些改变就行了。
要对除X节点外的其它节点进行操作时,必定是这样的情况:
X节点是双层黑色,且X有父节点P。
由知可知,X必然有兄弟节点W,而且这个W节点必定有两个子节点。
(因为这是原树满足红黑条件要求而自然具备的。
X为双黑色,那么P的另一个子节点以下一定要有至少两层的节点,否则高黑度不可能和X路径一致)。
所以我们就分析这些节点之间如何变形,把问题限制在比较小的范围内解决。
另一个前提是:
X在一开始,肯定是树底的叶节点或是NIL节点,所以在递归向上的过程中,每一步都保证下一步进行时,至少 X的子树是满足红黑特性的。
因此子树的情况就可以认为是已经正确的了,这样,分析就只限制在X节点,X的父节点P,X的兄弟节点W,以及W的两个子节点。
这些个节点中。
W以及W的两个子节点C1和C1的一共有五种组合,便有两种情况的处理是一致的,因此调整的过程可以分以下四个情况:
第一种情况:
W是红色节点。
如果W是红色的,那么B和D节点进行一次左旋,并把D(也就是原来的W)着为黑色,B节点(X的父节点)着为红色。
然后让W指向X的新兄弟。
这样,就把这种情况转化为了W为黑色的情况来解决。
在这个变形中,这五个节点之间保持了红黑性质不变,而X指向的双黑色节点的位置和颜色特性都没有变化。
变形后的情况如何解决呢?
下面的都是W为黑色的问题,因此下面三种中总有一种会合适。
第二种情况:
W以及W的两个子节点都是黑色的。
这种情况下,把D节点着成红色。
然后把X的一个黑色推到父节点B中去,这时X就指向B节点了。
变形前后,这五个节点间的黑高是没有变化的。
唯一可能产生问题的就是如果B原来是红色,那么B和D两个红色相邻就破坏了第4个性质,这样新X的子树就有问题了。
这本来不符合我们向上递归的假设。
但正好在这种情况下递归就可以结束了。
因为B节点原来是红色,现在双加一层黑色,那么X现在指