查找方法--从静态表到动态表
概要:
数据结构学习的笔记:查找的一些方法。各种方法的具体描述和特点,烦请参考数据结构的课本,或直接google
针对数据存储结构的不一样,查表方法分为:
静态表:顺序,折半,分块查找
动态表:二叉排序树、平衡二叉树
一、具体方法介绍
1、针对静态表
(法1)顺序查找:适合于存储结构为顺序存储或链接存储的线性表。
顺序查找
/* 顺序查找 适合于存储结构为顺序存储或链接存储的线性表。 查找关键字为key 的记录 输入:待查数组 s[0]----s[n-1],共n个数 输出:成功,返回序号 失败,返回-1 */ int Search_Seq(int *s,int key, int n ) { int i=0; while( i<n && s[i] != key) //不如书上的优化 i++; if (s[i] == key) { return i; } else return -1; }
(法2)折半查找:适用于有序表,且只能是顺序存储结构
折半查找
/* 折半查找 适用于有序表,且只能是顺序存储结构 实现: 递归:Search_Bin_Rec 非递归:Search_Bin_NoRec */ int Search_Bin_NoRec(int *s,int key, int n) { int low = 0; int high = n-1; int mid = 0; while ( low<= high) { mid = (low+high)/2; if (s[mid] == key) //查找到了,直接返回序号 { return mid; } else if (s[mid]>key) { high = mid -1; } else low = mid +1; } return -1;//查找失败 } //递归实现,这里跟4个参数 int Search_Bin_Rec(int *s,int key,int low,int high) { if (low>high) return -1; int mid = (low+high)/2; if (s[mid] == key) { return mid; } else if (s[mid]>key) { return(Search_Bin_Rec(s,key,low,mid-1)); } else return(Search_Bin_Rec(s,key,mid+1,high)); }
(法3)分块查找:
分块查找
/* 分块查找 */ typedef struct { int key; int link; }IntexType; //索引表元素的结构 int Search_Blk(IntexType *idx,int *s,int key,int nIdx) { //先用折半查找法,在有序的索引表中找到块 int low=0,high=nIdx-1,mid=0; bool findBlock_flag = false; int blockStart=0,blockEnd=0;//记录块的起始位置和终止位置 if (key > idx[high].key)//比最大的还要大,查找失败 { return -1; } while (low<=high && !findBlock_flag) { mid = (low+high)/2; if (idx[mid].key == key)//找到分块 { blockStart = idx[mid].link; blockEnd = idx[mid+1].link - 1; findBlock_flag = true; } else if (idx[mid].key > key) { high = mid -1; } else low = mid +1; if (low >high) { findBlock_flag = true; //找到分块 blockStart = idx[mid].link; blockEnd = idx[mid+1].link - 1; } } //找到块了(blockStart, blockEnd),再进行顺序查找 cout<<"位于: "<<blockStart<<" "<<blockEnd<<endl; int i=blockStart; while(i<=blockEnd && s[i] != key) i++; if (s[i] == key) { return i; //查找成功 } else return -1;//查找失败 }
2、针对动态表
动态表: 二叉排序树
二叉排序树
/* 动态表: 二叉排序树 */ typedef struct node { int data; struct node *lchild; struct node *rchild; }BSTnode; //二叉排序树的结点 //中序遍历 二叉树,得到的是递增的序列。 可用于检验构造二叉树是否成功?! void InOrderTraverse_Recur(BSTnode *T) { if ( T != NULL) { InOrderTraverse_Recur(T->lchild); printf("%d \t",T->data); InOrderTraverse_Recur(T->rchild); } } //二叉排序树插入操作, 注意:涉及到修改时,参数一定要用引用或者是二级指针。 //当二叉排序树中不存在关键字时,将其插入到二叉排序树 bool InsertBST(BSTnode *&T, int key) { if (T == NULL) { T = (BSTnode*)malloc(sizeof(BSTnode)); T->lchild = T->rchild = NULL; T->data = key; return true; //二叉树中无关键字相同的点,插入成功 } else { if ( key == T->data) //二叉树中已经有了关键字相同的点,不再插入 return false; else if ( key < T->data) //小于,则插入到左子树 return(InsertBST(T->lchild,key)); else return(InsertBST(T->rchild,key));//大于,则插入到左子树 } } /* 二叉排序树的生成, 是从空的二叉排序树开始,每输入一个结点数据,就调用一次插入算法将它插入到当前已生成的二叉排序树中 */ //输入序列,构造二叉排序树 BSTnode * CreatBST(int *s,int n) { BSTnode * T= NULL; int i=0,key; while (i<n) { key = s[i]; InsertBST(T,key); i++; } printf("\n构造二叉排序树成功!!\n"); return T; } //二叉树的查找 //已经建立好了二叉树,查找关键字key是否成功 //参数:二叉排序树,待查找关键字,查找结束后指向关键字的指针 //不成功,则插入,返回false;否则,返回true bool SearchBST(BSTnode *&T, int key,BSTnode *&pKey ) { if (T == NULL) //为空,则查找失败,进行插入操作 { InsertBST(T,key); //这里进行了插入,相当于修改了二叉树,故SearchBST 参数使用引用 pKey = T; return false; } else { if ( key == T->data) { pKey = T; return true; } else if ( key < T->data) return(SearchBST(T->lchild,key,pKey)); else return(SearchBST(T->rchild,key,pKey)); } } //删除二叉排序树中关键字为key的节点 /* 删除关键字为key的结点,删除成功,返回true。否则,返回false(即没有关键字为key的结点) */ //已找到结点,直接删除结点的具体实现。注意:分为2种情况:有子树为空 2个子树均为非空 void DeleteNode(BSTnode*&T) { BSTnode *temp=NULL; if (!T->lchild) //左子树为空 { temp = T;//记录要删除的结点 T = T->rchild;//重接右子树 free(temp); temp = NULL; } else if (!T->rchild)//右子树为空 { temp = T; T = T->lchild;//重接左子树 free(temp); temp = NULL; } else //左右子树均非空 { BSTnode *s=NULL,*parent=NULL; //s为直接前驱,parent为直接前驱的双亲 parent = T; s = T->lchild;//转左 while (s->rchild) //向右走到底,找中序遍历的直接前驱s { parent = s; s = s->rchild; } T->data = s->data; //把直接前驱的数据 给要删除的结点 //判断是否转左,进行重接直接前驱s的左子树 if (parent == T)//没有转左 { parent->lchild = s->lchild;//或者T->lchild = s->lchild free(s); s = NULL; } else { parent->rchild = s->lchild; free(s); s = NULL; } } } //删除,包含查找关键字结点。 bool DeleteBST(BSTnode*&T,int key) { if (!T) { return false; } else { if (key == T->data) { DeleteNode(T); return true; } else if (key < T->data) return(DeleteBST(T->lchild,key)); else return(DeleteBST(T->rchild,key)); } }
平衡二叉树:为了保证二叉排序树的性能,应该减少二叉排序树的深度。因此要构建平衡二叉树
参考:!!(好!) http://webtrados.llh4.com/post/522.html
http://hxraid.iteye.com/blog/609949
动画演示:http://webtrados.llh4.com/post/522.html
平衡二叉树
/* 为了保证二叉排序树的性能,应该减少二叉排序树的深度。因此要构建平衡二叉树 参考:!!(好!) http://webtrados.llh4.com/post/522.html http://hxraid.iteye.com/blog/609949 动画演示:http://webtrados.llh4.com/post/522.html 平衡二叉树的操作 */ //平衡二叉树,结点的结构 typedef struct AVLNODE { int data; int bf; //结点的平衡因子 struct AVLNODE *lchild,*rchild; }AVLNode; //中序遍历 平衡二叉树,得到的是递增的序列。 可用于检验构造二叉树是否成功?! void InOrderTraverseAVLTree_Recur(AVLNode*T) { if ( T != NULL) { InOrderTraverseAVLTree_Recur(T->lchild); printf("%d \t",T->data); InOrderTraverseAVLTree_Recur(T->rchild); } } //单向右旋处理 void R_Rotate(AVLNode* &T) { AVLNode* tempLc = T->lchild;//tempLc 指向根的左孩子 T->lchild = tempLc->rchild; tempLc->rchild = T; T = tempLc; //T继续指向根结点(新) } //单向左旋处理 void L_Rotate(AVLNode *&T) { AVLNode* tempRc = T->rchild; //tempRc 指向根的右孩子 T->rchild = tempRc->lchild; tempRc->lchild = T; T = tempRc; } #define LH 1 //左高 #define EH 0 //等高 #define RH -1 //右高 void LeftBalance(AVLNode*&T)//先左后右处理(或者直接右旋处理) { AVLNode* lc = T->lchild;//当前根(T)的左孩子lc AVLNode *rd = NULL; //调整后新的根节点 switch (lc->bf) { case LH: //直接右旋处理 T->bf = lc->bf = EH;//平衡因子都修改为0 R_Rotate(T); break; case RH: //先左旋后右旋 { rd = lc->rchild; // switch (rd->bf) //为了修改旋转后的平衡因子 { case LH: T->bf = RH; lc->bf = EH; break; case EH: T->bf = lc->bf = EH; break; case RH: T->bf = EH; lc->bf = LH; break; }//switch(rd->bf) rd->bf = EH; L_Rotate(T->lchild); R_Rotate(T); break; } }//switch(lc->bf) } void RightBalance(AVLNode* &T)//先右后左处理(或者直接左旋转处理) { AVLNode *rc,*ld; rc = T->rchild; switch(rc->bf) { case RH: T->bf = rc->bf = EH; L_Rotate(T); break; case LH: { ld = rc->lchild; switch (ld->bf) { case LH: T->bf = EH; rc->bf = RH; break; case EH: T->bf = rc->bf = EH; break; case RH: T->bf = LH; rc->bf = EH; break; }// switch (ld->bf) ld->bf = EH; R_Rotate(T->rchild); L_Rotate(T); break; } }//switch(rc->bf) } //在二叉平衡树中插入关键字为e的结点。若插入操作引起二叉树失衡,则进行平衡旋转处理,taller反映T长高与否 //不存在关键字为e的结点,则插入,返回1 否则,返回0 int InsertAVL(AVLNode*&T,int e,bool &taller) { if (!T) { T = (AVLNode*)malloc(sizeof(AVLNode)); T->bf = EH; T->data = e; T->lchild = T->rchild = NULL; taller = true; return 1; } if (e == T->data) { taller = false; return 0; } else if ( e < T->data) //插入到左子树 { if(!InsertAVL(T->lchild,e,taller))// 未插入结点,直接返回。 return 0; //否则,要进行平衡旋转二叉树处理 if (taller) { switch (T->bf) { case LH: LeftBalance(T); taller = false; break; case RH: taller = false; T->bf = EH; break; case EH: T->bf = LH; taller = true; break; } } return 1; } else //插入到右子树 { if (!InsertAVL(T->rchild,e,taller)) //未插入 { return 0; } if (taller) //插入成功 { switch (T->bf) { case LH: T->bf = EH; taller = false; break; case EH: T->bf = RH; taller = true; break; case RH: RightBalance(T); taller = false; break; } } return 1; } }
主函数测试:
主函数main
void main() { const n = 10; int key=58; int a[n] = {32,5,10,8,200,50,36,20,150,96}; // cout<<Search_Seq(a,key,n)<<endl; // cout<<Search_Bin_NoRec(a,key,n)<<endl; // cout<<Search_Bin_Rec(a,key,0,n-1)<<endl; //分块查找测试 int aBlack[16] = {9,22,12,14, 35,42,44,38, 48,60,58,47, 78,80,77,82}; IntexType idx[4] = {{22,0},{44,4},{60,8},{82,12}}; //索引表 // cout<<Search_Blk(idx,aBlack,key,4)<<endl; //二叉排序树 查找 BSTnode * BST_main = NULL,*pTemp=NULL; BST_main = CreatBST(a,n); InOrderTraverse_Recur(BST_main); if(SearchBST(BST_main,80,pTemp)) cout<<"查找成功!!"<<endl<<pTemp->data<<endl; else cout<<"查找失败!!"<<endl<<pTemp->data<<endl; InOrderTraverse_Recur(BST_main); //删除结点 测试 if (DeleteBST(BST_main,5)) { cout<<"删除结点成功"<<endl; InOrderTraverse_Recur(BST_main); } else cout<<"删除失败!!"<<endl; //平衡二叉树的测试 AVLNode * AVLTree = NULL; bool taller = false; cout<<endl<<"构造平衡二叉树实验: "<<endl; for (int i=0;i<n;i++) { InsertAVL(AVLTree,a[i],taller); InOrderTraverseAVLTree_Recur(AVLTree); printf("\n"); } int xa = sizeof(int); }
(完)