【例】顺序查找
顺序查找又称线性查找,它对于顺序表和链表都是适用的。对于顺序表,可通过数组下标递增来顺序扫描每个元素;对于链表,则通过指针 next 来依次扫描每个元素
于前面咱们已经实战了顺序表用数组实现,现在我们实战顺序表用指针,也就是申请一个堆空间,使用方式和数组还是一致的
#include <cstdio>
#include <cstdlib>
#include <ctime>
typedef int ElemType;
typedef struct{
ElemType* elem;//整型指针
int TableLen;//存储动态数组里面元素的个数
}SSTable;
int Search_Seq(SSTable ST,ElemType key)
{
ST.elem[0]=key;//让零号元素作为哨兵
int i;
for(i=ST.TableLen-1;ST.elem[i]!=key;--i);
return i;
}
//init进行了随机数生成,随机数生成考研不会考
void ST_Init(SSTable& ST,int len)
{
//多申请了一个位置,为了存哨兵,不使用哨兵也可以,为了和王道书保持一致
ST.TableLen=len+1;
ST.elem=(ElemType*) malloc(sizeof(ElemType)*ST.TableLen);
int i;
srand(time(NULL));//随机数生成
for(i=1;i<ST.TableLen;i++)//因为第0个是哨兵,所以从1随机
{
ST.elem[i]=rand()%100;
}
}
void ST_print(SSTable ST)
{
for(int i=0;i<ST.TableLen;i++)
{
printf("%3d",ST.elem[i]);
}
printf("/n");
}
int main()
{
SSTable ST;
ElemType key;
int pos;//存储查询元素的位置
ST_Init(ST,10);
ST_print(ST);
printf("请输入要搜索的key值:\n");
scanf("%d",&key);
pos= Search_Seq(ST,key);
if(pos)
{
printf("查找成功 位置为%d\n",pos);
} else{
printf("查找失败\n");
}
return 0;
}
【例】折半查找
折半查找又称二分查找,它仅适用于有序的顺序表。
针对顺序表有序,使用 qsort 来排序,
qsort 的使用方法如下:
#include <stdlib.h>
void qsort( void *buf, size_t num, size_t size, int (*compare)(const void *, const void *) );
buf:要排序数组的起始地址,也可以是指针,申请了一块连续的堆空间
num:数组中元素的个数
size:数组中每个元素所占用的空间大小
compare:比较规则,需要我们传递一个函数名,这个函数由我们自己编写,
返回值必须是 int 类型,形参是两个 void 类型指针,这个函数我们编写,但是是qsort 内部调用的,相当于我们传递一种行为给 qsort。
代码流程:
我们初始化顺序表,随机 10 个元素
使用 qsort 进行排序,排序完毕后,打印
输入要查找的元素值,存入变量 key 中
通过二分查找查找对应 key 值,找到则输出在顺序表中的位置,没找到输出未找到
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef int ElemType;
typedef struct {
ElemType* elem;//整型指针
int TableLen;//存储动态数组里边元素的个数
}SSTable;
//init 进行了随机数生成,折半查找我们没有使用哨兵
void ST_Init(SSTable& ST, int len)
{
ST.TableLen = len;
ST.elem = (ElemType*)malloc(sizeof(ElemType) * ST.TableLen);
int i;
srand(time(NULL));//随机数生成
for (i = 0; i < ST.TableLen; i++)
{
ST.elem[i] = rand() % 100;
}
}
void ST_print(SSTable ST)
{
for (int i = 0; i < ST.TableLen; i++)
{
printf("%3d", ST.elem[i]);
}
printf("\n");
}
//时间复杂度 logn
int BinarySearch(SSTable L, ElemType key)
{
int low = 0, high = L.TableLen - 1, mid;
while (low <= high)
{
mid = (low + high) / 2;
if (L.elem[mid] == key)
return mid;//等于就找到了
else if (L.elem[mid] > key)
high = mid - 1;
else
low = mid + 1;
}
return -1;
}
int compare(const void* left, const void* right)//left,right 是任意两个元素的地址值
{
return *(ElemType*)left - *(ElemType*)right;
//return *(ElemType*)right - *(ElemType*)left;//从大到小
}
//顺序查找 与 折半查找
int main()
{
SSTable ST;
ElemType key;
int pos;//存储查询元素的位置
ST_Init(ST, 10);
qsort(ST.elem, ST.TableLen, sizeof(ElemType), compare);//qsort 实现的是快排
ST_print(ST);
printf("二分查找,请输入要搜索的 key 值:\n");
scanf("%d", &key);
//有序数组
pos = BinarySearch(ST, key);//二分查找,也叫折半查找
if (pos != -1)
{
printf("查找成功 位置为 %d\n", pos);
}
else {
printf("查找失败\n");
}
return 0;
}
【例】二叉排序树
二叉排序树(也称二叉查找树)或者是一棵空树,或者是具有下列特性的二叉树:
1)若左子树非空,则左子树上所有结点的值均小于根结点的值。
2)若右子树非空,则右子树上所有结点的值均大于根结点的值。
3)左、右子树也分别是一棵二叉排序树。
步骤:首先我们新建了一颗二叉排序树,使用的是非递归的方法新建,当然递归的代码也给出了,不过注释了。然后针对建好的二叉排序树进行了中序遍历输出,接着对二叉排序树进行查找,我们可以看到二叉排序树的最大查找次数是树的高度。
#include <stdio.h>
#include <stdlib.h>
typedef int KeyType;
typedef struct BSTNode{
KeyType key;
struct BSTNode *lchild,*rchild;
}BSTNode,*BiTree;
//54,20,66,40,28,79,58
// 王道书上的递归写法,代码简单,但是理解有难度
//int BST_Insert(BiTree &T,KeyType k)
//{
// if(NULL==T)
// { //为新节点申请空间,第一个结点作为树根,后面递归再进入的不是树根,是为叶子结点
// T=(BiTree)malloc(sizeof(BSTNode));
// T->key=k;
// T->lchild=T->rchild=NULL;
// return 1;//代表插入成功
// }
// else if(k==T->key)
// return 0;//发现相同元素,就不插入
// else if(k<T->key)//如果要插入的结点,小于当前结点
// //函数调用结束后,左孩子和原来的父亲会关联起来,巧妙利用了引用机制
// return BST_Insert(T->lchild,k);
// else
// return BST_Insert(T->rchild,k);
//}
//54,20,66,40,28,79,58
int BST_Insert(BiTree& T, KeyType k)
{
if (NULL == T)
{ //为新节点申请空间,第一个结点作为树根,T 是树根
T = (BiTree)malloc(sizeof(BSTNode));
T->key = k;
T->lchild = T->rchild = NULL;
return 1;//代表插入成功
}
BiTree p = T,parent;//p 用来遍历,parent 用来存 p 的父亲
while (p)
{
parent = p;
if (k == p->key)
{
return 0;
}
else if (k < p->key)
{
p = p->lchild;
}
else {
p = p->rchild;
}
}
//开始申请空间并插入
BiTree pnew = (BiTree)malloc(sizeof(BSTNode));
pnew->key = k;
pnew->lchild = pnew->rchild = NULL;
if (k > parent->key)
{
parent->rchild = pnew;//新结点在父亲的右边
}
else {
parent->lchild = pnew;//新结点在父亲的左边
}
}
//创建二叉排序树
void Creat_BST(BiTree &T,KeyType str[],int n)
{
T=NULL;//T 是树根
int i=0;
while(i<n)
{
BST_Insert(T,str[i]);//把某一个结点放入二叉查找树
i++;
}
}
//也可以用递归实现,只不过循环查找已经非常简单清晰,用递归必要性不强
BSTNode *BST_Search(BiTree T,KeyType key,BiTree &p)
{
p=NULL;//存储要找的结点的父亲
while(T!=NULL&&key!=T->key)
{
p=T;
if(key<T->key) T=T->lchild;//比当前节点小,就左边找
else T=T->rchild;//比当前节点大,右边去
}
return T;
}
void InOrder(BiTree T)
{
if(T!=NULL)
{
InOrder(T->lchild);
printf("%3d",T->key);
InOrder(T->rchild);
}
}
//二叉排序树的创建,中序遍历,查找,删除
int main()
{
BiTree T=NULL;//树根
BiTree parent;//存储父亲结点的地址值
BiTree search;
KeyType str[7]={54,20,66,40,28,79,58};//将要进入二叉排序树的元素值
Creat_BST(T,str,7);
InOrder(T);
printf("\n");
search=BST_Search(T,40,parent);
if(search)
{
printf("找到对应结点,值=%d\n",search->key);
}else{
printf("未找到对应结点\n");//没找到 search 返回的是 NULL
}
return 0;
}
【例】二叉排序树删除
#include <cstdio>
#include <cstdlib>
typedef int KeyType;
typedef struct BSTNode{
KeyType key;
struct BSTNode *lchild,*rchild;
}BSTNode,*BiTree;
//54,20,66,40,28,79,58
// 王道书上的递归写法,代码简单,但是理解有难度
//int BST_Insert(BiTree &T,KeyType k)
//{
// if(NULL==T)
// { //为新节点申请空间,第一个结点作为树根,后面递归再进入的不是树根,是为叶子结点
// T=(BiTree)malloc(sizeof(BSTNode));
// T->key=k;
// T->lchild=T->rchild=NULL;
// return 1;//代表插入成功
// }
// else if(k==T->key)
// return 0;//发现相同元素,就不插入
// else if(k<T->key)//如果要插入的结点,小于当前结点
// //函数调用结束后,左孩子和原来的父亲会关联起来,巧妙利用了引用机制
// return BST_Insert(T->lchild,k);
// else
// return BST_Insert(T->rchild,k);
//}
//54,20,66,40,28,79,58
int BST_Insert(BiTree& T, KeyType k)
{
if (NULL == T)
{ //为新节点申请空间,第一个结点作为树根,T 是树根
T = (BiTree)malloc(sizeof(BSTNode));
T->key = k;
T->lchild = T->rchild = NULL;
return 1;//代表插入成功
}
BiTree p = T,parent;//p 用来遍历,parent 用来存 p 的父亲
while (p)
{
parent = p;
if (k == p->key)
{
return 0;
}
else if (k < p->key)
{
p = p->lchild;
}
else {
p = p->rchild;
}
}
//开始申请空间并插入
BiTree pnew = (BiTree)malloc(sizeof(BSTNode));
pnew->key = k;
pnew->lchild = pnew->rchild = NULL;
if (k > parent->key)
{
parent->rchild = pnew;//新结点在父亲的右边
}
else {
parent->lchild = pnew;//新结点在父亲的左边
}
}
//创建二叉排序树
void Creat_BST(BiTree &T,KeyType str[],int n)
{
T=NULL;//T 是树根
int i=0;
while(i<n)
{
BST_Insert(T,str[i]);//把某一个结点放入二叉查找树
i++;
}
}
//也可以用递归实现,只不过循环查找已经非常简单清晰,用递归必要性不强
BSTNode *BST_Search(BiTree T,KeyType key,BiTree &p)
{
p=NULL;//存储要找的结点的父亲
while(T!=NULL&&key!=T->key)
{
p=T;
if(key<T->key) T=T->lchild;//比当前节点小,就左边找
else T=T->rchild;//比当前节点大,右边去
}
return T;
}
//这个书上没有二叉排序树删除代码--考大题没那么高
void DeleteNode(BiTree &root,KeyType x){
if(root == NULL){
return;
}
if(root->key>x){
DeleteNode(root->lchild,x);//往左子树找要删除的结点
}else if(root->key<x){
DeleteNode(root->rchild,x);//往右子树找要删除的结点
}else{ //查找到了删除节点
if(root->lchild == NULL){ //左子树为空,右子树直接顶上去
BiTree tempNode = root;//用临时的存着的目的是一会要 free
root = root->rchild;
free(tempNode);
}else if(root->rchild == NULL){ //右子树为空,左子树直接顶上去
BiTree tempNode = root;//临时指针
root = root->lchild;
free(tempNode);
}else{ //左右子树都不为空
//一般的删除策略是左子树的最大数据 或 右子树的最小数据 代替要删除的节点(这里采用查找左子树最大数据来代替)
BiTree tempNode = root->lchild;
while(tempNode->rchild!=NULL){//向右找到最大的
tempNode = tempNode->rchild;
}
root->key = tempNode->key;//把 tempNode 对应的值替换到要删除的值的位置上
DeleteNode(root->lchild,tempNode->key);//删除 tempNode
}
}
}
void InOrder(BiTree T)
{
if(T!=NULL)
{
InOrder(T->lchild);
printf("%3d",T->key);
InOrder(T->rchild);
}
}
//二叉排序树的创建,中序遍历,查找,删除
int main()
{
BiTree T=NULL;//树根
BiTree parent;//存储父亲结点的地址值
BiTree search;
KeyType str[7]={54,20,66,40,28,79,58};//将要进入二叉排序树的元素值
Creat_BST(T,str,7);
InOrder(T);
printf("\n");
search=BST_Search(T,40,parent);
if(search)
{
printf("找到对应结点,值=%d\n",search->key);
}else{
printf("未找到对应结点\n");//没找到 search 返回的是 NULL
}
DeleteNode(T,40);//删除某个结点
InOrder(T);
printf("\n");
return 0;
}