静态查找与动态查找
查找表的方式分为静态查找和动态查找
静态查找
只做查找操作的查找表,它主要是
- 查询某个特定数据元素是否在查找表中
- 检索某个特定数据元素和各种属性
动态查找
在查找的过程中同时插入表中不存在的数据元素,或者从表中删除已经存在的数据元素。它的特性有
- 查找时插入数据
- 查找时删除数据
静态查找
顺序查找
- 又称线性查找,它从表中的第一个或最后一个开始,逐个将关键字跟特定元素数据比对,直到找到为止
- 核心代码实现
#pragma mark - 顺序查找 int OrderSearch(int target[],int length, int keyWord){ for (int i = 0; i<length; i++) { if (target[i] == keyWord) {//找到则返回位置 return i; } } //找不到则返回-1 return -1; } #pragma mark - 顺序查找优化 /** 思路:数组中第一个元素位置是哨兵 */ int OrderSearchBetter(int target[], int length, int keyWord){ int i = length; //设置哨兵位置存储关键字 target[0] = keyWord; while (target[i] != keyWord) { i--; } //注意返回i == 0时表示没有查找到 return i; }
折半查找(二分查找)
- 折半查找:折半查找又叫二分查找,它需要查找的表是有序的(通常是从小到大),且表的存储方式也是顺序存储
- 核心代码实现
#pragma mark - 折半查找 /** 思路:条件:查找的表是从小到大排序的 1、从中间位置开始查找,如果等于关键字则返回 2、如果中间位置大于关键字,则从左半部查找 3、如果中间位置小于关键字,则从右半部查找 4、重复上述过程,直到找到或查找结束位置 */ int BinanrySearch(int target[], int lenght, int keyWord){ int low,height,mid; //地位low为1,0位置是哨兵 low = 1; height = lenght; while (low <= height) {//low不大于height就表示还没有找到,继续循环 mid = (low + height)/2; if (keyWord < target[mid]) {//关键字在中间位置的左侧,更新最高位下标 //将最高位下标设置成中位减一 height = mid - 1; }else if (keyWord > target[mid]){ //关键字在中间位置的右侧,更新最低位下标 //将最低位下标设置成中位加一 low = mid + 1; }else{//中间位置就是keyWord return mid; } } return 0; } - 插值查找优化:由于要查找的表是顺序排序的,因此我们只要找到mid在表中的分布区域就能缩小查找范围,所以mid的计算公式改为:
mid = low + ((keyWord - target[low])/(target[height] - target[low]))*(height - low),其 中target为要查找的表,keyWord是要查找的关键字
- 插值查找代码实现
#pragma mark - 插值查找
/**
思路:同折半查找相同,唯一的区别在于计算mid的同,mid计算公式为:mid = low + ((keyWord - target[low])/(target[height] - target[low]))*(height - low)
*/
int InsertBinanrySearch(int target[], int length, int keyWord){
int low,height,mid;
//地位low为1,0位置是哨兵
low = 1;
height = length;
while (low <= height) {//low不大于height就表示还没有找到,继续循环
mid = low + ((keyWord - target[low])/(target[height] - target[low]))*(height - low);
if (keyWord < target[mid]) {//关键字在中间位置的左侧,更新最高位下标
//将最高位下标设置成中位减一
height = mid - 1;
}else if (keyWord > target[mid]){ //关键字在中间位置的右侧,更新最低位下标
//将最低位下标设置成中位加一
low = mid + 1;
}else{//中间位置就是keyWord
return mid;
}
}
return 0;
}斐波拉契查找
- 斐波拉契:一个有序递增数组中,从第三个数据开始,F[n] = F[n - 1] + F[n - 2],其中n>=3,
斐波拉契查找:首先找到顺序表的长度n在斐波拉契中的位置,
斐波拉契查找的mid算法:mid = low + F[k - 1] - 1;
斐波拉契查找核心代码实现
//定义斐波拉契序列 int F[100]; //斐波拉契查找,这个有哨兵 int FibonacciSearch(int target[], int length, int key){ int low,height,mid; //地位low为1,0位置是哨兵 low = 1; height = length; //先生成斐波拉契序列 F[0] = 0; F[1] = 1; for (int i = 2; i<100; i++) { F[i] = F[i - 1] + F[i - 2]; } //找到length在斐波拉契序列中的位置 int k = 0; while (length > F[k] - 1) { k++; } //当F[k]大于n时补足数组中缺失的数据 for (int i = length; i<F[k] - 1; i++) { //用最后一位数据补足 target[i] = target[length]; } while (low <= height) { mid = low + F[k - 1] - 1; if (key > target[mid]) { low = mid + 1; //斐波拉契下标-2 k = k - 2; }else if (key < target[mid]){ height = mid - 1; //斐波拉契下标-1 k = k - 1; }else{//找到了 if (mid <= length) {//没有超过原来的length长度找到了 return mid; }else{//超过了原来的长度找到了,则位置一定是最后一个 return length; } } } return 0; }
动态查找
什么是二叉排序树
二叉排序树又叫二叉查找树,它是一棵空树或是具有以下特定的二叉树
- 二叉排序树性质:
- 若它的左子树不为空,则左子树上的所有节点的值小于双亲节点
- 若它的右子树不为空,则右子树上的所有节点的值大于双亲节点
- 它的左右子树也是二叉排序树
二叉排序树的查找
- 算法思路:根据二叉排序树的性质递归查找
- 核心代码实现
/** 思路:T为二叉排序树,key为节点的值,fater为查找结点的双亲,p指向查找到的节点 1、递归查找 2、当key大于T->data,表明查找的节点在右孩子这边 3、当key小于T->data,表明查找的节点在左孩子这边 */ Status SearchBiTree(BiTree T, int key, BiTree fater, BiTree *p){ if (!T) {//查找失败 *p = fater; return ERROR; } if (key < T->data) {//在左孩子这边查找 return SearchBiTree(T->leftChild, key, T, p); }else if (key > T->data){//在右孩子这边查找 return SearchBiTree(T->rightChild, key, T, p); }else {//找到了 *p = T; return TRUE; } }
二叉排序树的插入
核心代码实现
/**
思路:
1、先查找节点是否存在,如果不存在则插入
2、当插入的节点值小于双亲节点则插入左孩子,否则插入右孩子
*/
Status InsertBiTree(BiTree *T, int data){
BiTree p;
if (!SearchBiTree(*T, data, NULL, &p)) {//当没有找到该节点时插入数据
BiTree node = (BiTree)malloc(sizeof(BiTNode));
node->data = data;
node->leftChild = NULL;
node->rightChild = NULL;
if (!p) {//如果p不存在,即第一次插入,则s为跟节点
*T = node;
}else if (p->data > data){//插入左孩子
p->leftChild = node;
}else{//插入右孩子
p->rightChild = node;
}
return TRUE;
}
return ERROR;
}
二叉排序树的删除
- 需要注意:根据查找到的位置删除节点,当该节点非叶子节点的时候需要连接二叉树,此时需要注意的是:
- 删除节点是否有左孩子
- 删除节点是否有右孩子
- 删除节点是否左右孩子都有
- 核心代码实现
/** 思路: 1、当删除的是叶子节点时则直接删除 2、当删除的是非叶子节点 ①删除的节点只有左孩子,则删除节点的父节点连接左孩子 ②删除的节点只有右孩子,则删除节点的父节点连接右孩子 ③删除的节点左右孩子均有时,如果连接左孩子,那么需要遍历左孩子的右孩子,如果右孩子不存在则直接连接删除节点的左孩子;如果连接右孩子则遍历右孩子的左孩子,如果左孩子不存在,则直接连接删除节点的右孩子 */ Status Delete(BiTree *T){ BiTree temp,p; //记录要删除的节点 temp = *T; if (!(*T)->leftChild) {//如果删除的节点左孩子不存在,则直接连接右孩子即可(不用管右孩子是否存在) p = temp->rightChild; *T = p; free(temp); }else if (!(*T)->rightChild){//如果删除的节点右孩子不存在,则直接连接左孩子即可(不用管左孩子是否存在) p = temp->rightChild; *T = p; free(temp); }else{//左右孩子都存在 /* 连接左孩子代码开始 */ // p = temp->leftChild; // //查找左孩子的右孩子的节点 // while (p->rightChild) { // //temp记录p // temp = p; // p = p->rightChild; // } // //寻找右孩子结束 // //更改删除节点的data值 // (*T)->data = p->data; // if (temp != *T) {//找到了删除节点左孩子的右孩子 // temp->rightChild = p->leftChild; // }else{ // temp->leftChild = p->leftChild; // } /* 连接左孩子代码结束 */ //连接右孩子代码开始 p = temp->rightChild; while (p->leftChild) { temp = p; p = p->leftChild; } //更改删除节点的data值 (*T)->data = p->data; if (temp != *T) {//找到了删除节点左孩子的右孩子 temp->rightChild = p->leftChild; }else{ temp->rightChild = p->rightChild; } //连接右孩子代码结束 free(p); } return OK; } Status DeleteBiTree(BiTree *T, int data){ if (!*T) { return ERROR; } if ((*T)->data == data) { return Delete(&(*T)); }else if ((*T)->data > data){//左孩子查找删除 return DeleteBiTree(&(*T)->leftChild, data); }else {//右孩子查找删除 return DeleteBiTree(&(*T)->rightChild, data); } } void VisitBiTree(BiTree T){ if (T) { if (T->leftChild) { VisitBiTree(T->leftChild); } printf("%4d",T->data); if (T->rightChild) { VisitBiTree(T->rightChild); } } }
删除添加了删除节点连接左孩子、右孩子的代码,感兴趣的话可以试试