目录
实验要求
一、生成数据
1.生成0-2048的奇数作为顺序序列,存放到Seq.txt
2.将这些奇数打乱顺序,存放到Random.txt
图 1 顺序序列
图 2:随机序列
二、构建二叉查找树
读取两个文件,分别常见二叉查找树T1,T2。其中,
T1是顺序序列得到的,是一棵斜树
T2是随机序列得到的,是正常的二叉查找树
三、删除操作
指定特定的元素进行删除
中序遍历删除后的二叉查找树进行展示
图 3:删除操作
四、插入操作
指定元素,插入到二叉查找树中,
中序遍历树,进行结果展示
图 4:插入操作
五、查找操作
图 5:查找成功
图 6:查找失败
六、折半查找
图 7:折半查找成功
图 8:折半查找失败
七、二叉查找树和折半查找的时间性能
图 9:二叉查找树和折半查找的时间性能
(1)斜树的时间性能
如果数组是按顺序存储段,那么斜二叉查找树实际就是退化为链式存储,和单链表的顺序查找的时间性能是一样的,为(n+1)/ 2。
本实验中n是1024,所以平均比较次数为512.5左右。
(2)正常二叉树的时间性能
如果数据的序列是随机的,则正常二叉查找树和折半查找的判定树类似,为O(log2n)。
本实验中的n是1024,所以平均比较次数为10左右。
(3)折半查找的时间性能
当n很大时,折半查找的查找成功的平均查找长度约为log2(n+1)-1
查找失败和查找最坏情况的关键字比较次数不超过判定树的高度log2[n+1]的上取整。
本实验中的n是1024,所以平均比较次数为10左右。
(4)总结
当数据是有序的时候,二叉查找树的时间性能差。
当数据是随机的时候,二叉查找树和折半查找性能类似的。
在平均情况下,二叉查找树的查找和折半查找差不多,都是O(log2n)级别的。
就维护表的有序性而言,二叉查找树更有效。
代码实现
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <time.h>
#define ArraySize 1024 //数组的大小
#define UpLimit 2048 //数组数字的上界
typedef int records;
typedef struct celltype //二叉查找树的节点,左右链存储
{
records data; //关键字
struct celltype* lchild; //左孩子
struct celltype* rchild; //右孩子
}BSTNode;
typedef BSTNode* BST;
typedef records* List;
int BSTcount = 0; //中序遍历二叉查找树时的数组下标
int count = 0; //查找次数
void Insert(BST *T, records k);
BSTNode* Search(BST T, records k);
records DeleteMin(BST *T);
void Delete(BST *T, records k);
void Inorder(BST T, List L);
int BinSearch(List L,records k,int up);
void GenerateData(void);
void PrintData(char* filename);
BST CreateBST(char* filename);
void ASL_BST(BST T);
void ASL_BinSearch(List number);
void Delete_packed(BST* T,records *number);
void Insert_packed(BST* T, records* number);
void Search_packed(BST T);
void BinSearch_packed(records* number);
int main(void)
{
printf("生成数据:\n");
GenerateData();
printf("0-2048的奇数顺序排列\n");
PrintData("Seq.txt");
printf("\n0-2048的奇数随机排列\n");
PrintData("Random.txt");
BST T1 = CreateBST("Seq.txt");
BST T2 = CreateBST("Random.txt");
printf("\n\n斜二叉查找树(T1)的平均查找长度:\n");
ASL_BST(T1);
printf("\n正常二叉查找树(T2)的平均查找长度:\n");
ASL_BST(T2);
printf("\n中序遍历二叉查找树进行排序,输出给需要折半查找的数组\n");
records number[ArraySize] = { 0 };
BSTcount = 0;
Inorder(T2, number);
printf("折半查找的平均查找长度:\n");
ASL_BinSearch(number);
Delete_packed(&T2,number);
Insert_packed(&T2, number);
Search_packed(T2);
BinSearch_packed(number);
return 0;
}
//向二叉查找树插入
//修改一级指针是需要使用二级指针
void Insert(BST *T, records k)
{
if (*T == NULL) //创建新节点
{
BSTNode *p = (BSTNode*)malloc(sizeof(BSTNode));
if (p == NULL) return;
p->data = k;
p->lchild = p->rchild = NULL;
*T = p;
}
else if ((*T)->data == k) return;
else if ((*T)->data > k) Insert(&((*T)->lchild), k);
else if ((*T)->data < k) Insert(&((*T)->rchild), k);
}
//二叉查找树的查找
BSTNode* Search(BST T, records k)
{
BSTNode* p = T;
if (p==NULL) //空树,递归中止
{
return p;
}
count++; //比较次数计数
if (p->data == k)
{
return p;
}
else if (k < p->data)
{
return Search(p->lchild, k); //查找左子树
}
else if (k > p->data)
{
return Search(p->rchild, k); //查找右子树
}
}
//删除T的最小节点
records DeleteMin(BST *T)
{
records temp;
BST p;
if ((*T)->lchild == NULL) //是最小元
{
p = *T;
temp = (*T)->data;
*T = (*T)->rchild; //右链继承
free(p);
return temp;
}
else //左子树不空,则最小元在左子树上
{
return DeleteMin(&((*T)->lchild));
}
}
//删除关键字为k的节点
void Delete(BST *T, records k)
{
if ((*T) != NULL)
{
if (k < (*T)->data) //递归地到左子树中去删除
{
Delete(&((*T)->lchild), k);
}
else if (k > (*T)->data) //递归地到右子树中去删除
{
Delete(&((*T)->rchild), k);
}
else
{
if ((*T)->lchild == NULL) *T = (*T)->rchild; //右链继承
else if ((*T)->rchild == NULL) *T = (*T)->lchild; //左链继承
else (* T)->data = DeleteMin(&((*T)->rchild)); //左右子树非空,则用右子树的最左节点代替
}
}
}
//对二叉查找树中序遍历,即可实现排序
//可以将排好序的数组输出给折半查找
void Inorder(BST T, List L)
{
if (T == NULL) return;
Inorder(T->lchild, L);
L[BSTcount] = T->data;
BSTcount++;
Inorder(T->rchild, L);
}
//折半查找(非递归)
int BinSearch(List L, records k,int up)
{
int low = 0;
int mid;
while (low <= up)
{
mid = (low + up) / 2;
count++; //比较次数加1
if (L[mid] == k) return mid;
else if (L[mid] > k) up = mid - 1;
else if (L[mid] < k) low = mid + 1;
}
return -1;
}
//生成顺序和随机序列,分别写入Seq.txt Random.txt
void GenerateData(void)
{
FILE* fp1,*fp2; //文件指针
int array[ArraySize], temp;
fp1 = fopen("Seq.txt", "w");
fp2 = fopen("Random.txt", "w");
if (!fp1 || !fp2)
{
printf("文件打开失败\n");
return;
}
for (int i = 0;i < ArraySize;++i) //顺序数组,写入Seq.txt
{
array[i] = 2 * i + 1;
fprintf(fp1, "%d ", array[i]);
}
fclose(fp1);
srand(time(0));
int randomidx; //随机下标,用来打乱数组的顺序
for (int i = 0;i < ArraySize;++i) //通过交换实现序列的随机化
{
randomidx = rand() % ArraySize;
temp = array[i];
array[i] = array[randomidx];
array[randomidx] = temp;
}
for (int i = 0;i < ArraySize;++i) //乱序数组,写入Random.txt
{
fprintf(fp2, "%d ", array[i]);
}
fclose(fp2);
}
//打印数据
void PrintData(char* filename)
{
FILE* fp = fopen(filename, "r");
if (fp == NULL) return;
records k;
for (int i = 0;i < ArraySize;++i)
{
fscanf(fp, "%d ", &k);
printf("%d ", k);
}
return;
}
//读取文件创建二叉树
BST CreateBST(char *filename)
{
//printf("\n二叉查找树如下:\n");
FILE* fp = fopen(filename, "r");
if (fp == NULL) return NULL;
BST p = NULL;
records k; //存储从文件中读出的数字
for (int i = 0;i < ArraySize;++i)
{
fscanf(fp, "%d ", &k);
Insert(&p, k);
}
fclose(fp);
return p;
}
//二叉查找树的查找成功和查找失败的平均查找长度
//对于本程序的数据集来说:
//查找奇数是查找成功;查找偶数是查找失败
void ASL_BST(BST T)
{
long sum1 = 0, sum2 = 0;
BSTNode* p;
for (int i = 0;i < ArraySize;++i)
{
count = 0;
p = Search(T, 2 * i + 1);
sum1 += count;
}
printf("查找成功的平均查找长度(查找奇数)为%f\n", sum1 * (1.0) / ArraySize);
for (int i = 0;i < ArraySize;++i)
{
count = 0;
p = Search(T, 2 * i);
sum2 += count;
}
count = 0;
p = Search(T, 2048);
sum2 += count;
printf("查找失败的平均查找长度(查找偶数)为%f\n", sum2 * (1.0) /(ArraySize+1));
}
//计算折半查找的平均查找长度
void ASL_BinSearch(List number)
{
long sum1 = 0, sum2 = 0;
for (int i = 0;i < ArraySize;++i)
{
count = 0;
BinSearch(number, 2 * i + 1, ArraySize - 1);
sum1 += count;
}
printf("查找成功的平均查找长度(查找奇数)为%f\n", sum1 * (1.0) / ArraySize);
for (int i = 0;i < ArraySize;++i)
{
count = 0;
BinSearch(number, 2 * i, ArraySize - 1);
sum2 += count;
}
printf("查找失败的平均查找长度(查找奇数)为%f\n", sum2 * (1.0) / ArraySize);
}
void Delete_packed(BST* T,records *number)
{
printf("\n\n从二叉查找树(T2)中删除一个数\n");
printf("请输入:\t");
int k;
scanf("%d", &k);
Delete(T, k);
printf("删除后二叉查找树的中序遍历如下\n");
BSTcount = 0;
Inorder(*T, number);
for (int i = 0;i < BSTcount;++i)
{
printf("%d ", number[i]);
}
}
void Insert_packed(BST* T, records* number)
{
printf("\n\n向二叉查找树中插入一个数\n");
printf("请输入:\t");
int k;
scanf("%d", &k);
Insert(T, k);
printf("插入后的二叉查找树的中序遍历如下\n");
BSTcount = 0;
Inorder(*T, number);
for (int i = 0;i < BSTcount;++i)
{
printf("%d ", number[i]);
}
}
void Search_packed(BST T)
{
printf("\n\n请输入想要在二叉查找树中查找的值\n");
int k;
scanf("%d", &k);
count = 0;
BSTNode* p = Search(T, k);
if (p)
{
printf("查找成功,查找次数为%d\n",count);
}
else printf("查找失败\n");
}
void BinSearch_packed(records* number)
{
printf("\n\n请输入需要折半查找的值\n");
int k;
scanf("%d", &k);
count = 0;
int result = BinSearch(number,k,BSTcount-1);
if (result == -1) printf("查找失败\n");
else
{
printf("查找成功,查找次数为%d\n",count);
}
}