数据结构中的顺序查找,二叉排序树,二叉平衡树
一、查找的几个概念
查找表:是由一组具有相同数据类型的数据元素组成的集合,这些元素通常保存在一张结构化的数据表中,用于支持对其中元素的查找操作
关键字:数据元素中唯一标识该元素的属性值,查找时就是通过关键字来定位对方的数据元素
查找:在查找中根据给定的关键字找出对于数据元素的过程
平均查找长度:是指在一种查找方法下,查找一个元素所需要比较的关键字的平均次数,是衡量查找算法效率的重要指标,它通常分为两种情况来讨论:查找成功和查找失败
ASLsuccess = P1C1+P2C2+……+PiCi
如果表中有n个元素,第i个元素被查到的概率为Pi,查找它需要比较Ci次关键字
二、线性表的查找——顺序查找
顺序查找就是在数组中进行查找操作
代码实现
int search(int* data, int len, int value)
{
for (int i = 0;i < len;i++)
{
if (data[i] == value)
{
return i;
}
}
return -1;//返回-1,避免与数组下标冲突
}
int main()
{
int arr[] = { 12,13,23,45,67,98,55,66 };
int len = sizeof(arr) / sizeof(arr[0]);
int pos = search(arr, len, 45);
printf("%d\n", pos);
return 0;
}
输出结果
三、线性表的查找——折半查找(二分查找)
条件:查找表必须是有序的,无序的数据使用折半查找是无意义的
步骤:
1.设查找表的起始下标为low,结束下标为high
2.计算中间下标,mid=(low+high)/ 2 (向下取整)
3.进行判断,如果关键字等于a【mid】,成功,返回下标;如果关键字大于a【mid】,右半部分查找,令low=mid+1;如果关键字小于a【mid】,左半部分查找,令high=mid-1
代码实现
int binary_search(int* data, int len, int value)
{
int low = 0;
int high = len - 1;
int mid;
while (low <= high)
{
mid = (low + high) / 2;
if (value == data[mid])
{
return mid;
}
else if (value > data[mid])
{
low = mid + 1;
}
else
{
high = mid - 1;
}
}
}
int main()
{
int arr[] = { 12,13,23,45,67,98,55,66 };
int len = sizeof(arr) / sizeof(arr[0]);
int pos = binary_search(arr, len, 45);
printf("%d\n", pos);
return 0;
}
运行结果
四、二叉排序树
顺序查找效率比较低,而折半查找又依赖于有序的顺序表结构,且插入与删除操作较麻烦,而二叉排序树就可以兼顾二者的优点
二叉排序树是一种特殊的二叉树,它满足以下特性:
1.对于树中任意节点,其左子树上所有的节点的值都小于该节点的值
2.对于树中任意节点,其右子树上所有的节点的值都大于该节点的值,并且左右子树本身也是一个二叉排序树
以下图为例
二叉排序树的代码实现:
查找
typedef int ElemType;
typedef struct TreeNode
{
ElemType data;
struct TreeNode* lchild;
struct TreeNode* rchild;
}TreeNode;
typedef TreeNode* BiTree;
int treeArr[] = { 70,55,49,30,-1,39,-1,-1,53,-1,-1,-1,80,75,-1,-1,98,95,-1,-1,-1 };
int index = 0;
//创建树
void creaetTree(BiTree* T)
{
ElemType num;
num = treeArr[index++];
if (num == -1)
{
*T = NULL;
}
else
{
*T = (BiTree)malloc(sizeof(TreeNode));
(*T)->data = num;
creaetTree(&(*T)->lchild);
creaetTree(&(*T)->rchild);
}
}
//前序遍历
void preOrder(BiTree T)
{
if (T == NULL)
{
return;
}
printf("%d ", T->data);
preOrder(T->lchild);
preOrder(T->rchild);
}
//二叉排序树的查找
int search_bst(BiTree T, int value, BiTree parent, BiTree* pos)
{
if (T == NULL)
{
*pos = parent;
return 0;
}
if (T->data == value)
{
*pos = T;
return 1;
}
else if (T->data > value)
{
return search_bst(T->lchild, value, T, pos);
}
else
{
return search_bst(T->rchild, value, T, pos);
}
}
int main()
{
BiTree T;
creaetTree(&T);
preOrder(T);
printf("\n");
BiTree search_T;
search_bst(T, 75, NULL, &search_T);
printf("%d\n", search_T->data);
return 0;
}
运行结果
插入
//二叉排序树的插入
int insert_bst(BiTree* T, int value)//必须是二级指针,因为要在函数内对参数进行改变
{
BiTree curr, pos, parent;
int search = search_bst(T, value, NULL, &pos);
if (search == 0)
{
curr = (BiTree)malloc(sizeof(TreeNode));
curr->data = value;
curr->lchild = NULL;
curr->rchild = NULL;
if (pos == NULL)
{
*T = curr;
}
else if (pos->data > value)
{
pos->lchild = curr;
}
else
{
pos->rchild = curr;
}
return 1;
}
else
{
return 0;
}
}
int main()
{
BiTree T = NULL;
int arrTree[] = { 70,55,49,30,39,53,80,75,98,95 };//前序遍历的结果
int len = sizeof(arrTree) / sizeof(arrTree[0]);
for (int i = 0;i < len;i++)
{
insert_bst(&T, arrTree[i]);
}
preOrder(T);
printf("\n");
insert_bst(&T, 100);
preOrder(T);
printf("\n");
return 0;
}
利用插入就可以在主函数内直接进行造树操作
删除
删除的判断:
1.叶子节点直接删,不影响结构
2.有一个孩子节点,删除后“子承父业”,直接替换
3.有两个孩子节点,用被删节点的左子树的最右子树来进行代替
//比较删除
int delete(BiTree* D)
{
BiTree temp, record;
if ((*D)->rchild == NULL)
{
temp = *D;
*D = (*D)->lchild;
free(temp);
}
else if ((*D)->lchild == NULL)
{
temp = *D;
*D = (*D)->rchild;
free(temp);
}
else
{
temp = *D;
record = (*D)->lchild;
while (record->rchild != NULL)
{
temp = record;
record = record->rchild;
}
(*D)->data = record->data;
if (temp != *D)
{
temp->rchild = record->lchild;
}
else
{
temp->lchild = record->lchild;
}
free(record);
}
return 1;
}
//二叉树的删除
int delete_bst(BiTree* T, int value)
{
if (*T == NULL)
{
printf("not found\n");
}
else if ((*T)->data == value)
{
return delete(T);
}
else if ((*T)->data > value)
{
return delete_bst(&(*T)->lchild,value);
}
else
{
return delete_bst(&(*T)->rchild,value);
}
}
int main()
{
BiTree T = NULL;
int arrTree[] = { 70,55,49,30,39,53,80,75,98,95 };
int len = sizeof(arrTree) / sizeof(arrTree[0]);
for (int i = 0;i < len;i++)
{
insert_bst(&T, arrTree[i]);
}
preOrder(T);
printf("\n");
delete_bst(&T, 80);
preOrder(T);
printf("\n");
return 0;
}
五、折半查找判定树
折半查找判定树是一个中序遍历结果与数组一致的二叉排序树
折半查找判定树可以大致理解为一颗完全二叉树,当节点为n时,二叉树的深度h为
(log2 ^n)+1,所以折半查找的时间复杂度为,O(log2 ^n)
而二叉排序树的时间复杂度为,O(n)。(考虑最坏情况,一颗n个节点的斜树)
六、平衡二叉树
如果一颗树偏得厉害,查找的过程就像沿着一条细长的枝条不断向下爬,效率低,如果这颗树是匀称的,查找路径就短,每次都能接近目标,效率高,平衡二叉树就是这样的一颗树
平衡二叉树,又称AVL树,是一种二叉排序树,任意一个节点的左子树与右子树的高度之差的绝对值不超过1
平衡因子:等于左子树高度-右子树高度,是衡量节点平衡程度的指标
平衡二叉树的平衡调整:
对平衡二叉树进行平衡调整时,需要先找到最小不平衡子树
最小不平衡子树:从插入点向上回溯,第一个失衡的点
平衡二叉树失衡大概分为4种情况:
LL失衡;RR失衡;LR失衡;RL失衡
1.LL失衡
插在左子树的左子树向右旋转,以最小不平衡子树的下一个节点为轴
2.RR失衡
插在右子树的右子树向左旋转
3.LR失衡
插在左子树的右子树,先左转后右转
4.RL失衡
插在右子树的左子树,先右转后左转
在调整时要注意两点:
1.保证平衡因子≤1
2.保证这棵树是二叉排序树