查找算法
概述:
查找算法:就是在是数据元素集合中查看是否存在于指定的关键字相等的元素。
查找分为两种:静态查找和动态查找。
1) 静态查找:是指在数据元素集合中查找与给定的关键字相等的元素
2) 动态查找:就是指在查找过程中,如果数据元素集合中不存在与给定的关键字相等的元素,则将该元素插入到数据元素集合中。
静态查找主要包括顺序表、有序顺序表和索引顺序表的查找。
1) 顺序表的查找,就是指从表的第一个元素与给定关键字比较,直到表的最后。
2) 有序顺序表的查找,在查找的过程中如果给定的关键字大于表的元素,就可以停止查找,说明表中不存在该元素(假设表中的元素按照关键字从小到大排列,并且查找从第一个元素开始比较)
3) 索引顺序表的查找是为主表建立一个索引,根据所以确定元素所在的范围,这样可以有效地提高查找的效率。
动态查找主要包括二叉排序树、平衡二叉树、B_树和B+树。
这些都是利用二叉树和树的特点对数据元素集合进行排序,通过将元素插入到二叉树或树中建立二叉树或树,然后通过对二叉树或树德遍历按照从小到大输出元素的序列。
散列表是利用散列函数的映射关系直接确定要查找元素的位置,大大减少了与元素的关键字的比较次数。
建立散列表的方法主要有直接定址法、平方取中法、折叠法和除留余数法(最常用)等。
但是会存在冲突,解决冲突的最为常用的方法主要有两个:开放定址法和链地址法。
一.静态查找
1.顺序表的查找
从表的一端开始,向另一端逐个按给定值kx 与关键码进行比较,若找到,查找成功,并给出数据元素在表中的位置;若整个表检测完,仍未找到与kx 相同的关键码,则查找失败,给出失败信息。
说白了就是,从头到尾,一个一个地比,找着相同的就成功,找不到就失败。很明显的缺点就是查找效率低。
【适用性】:适用于线性表的顺序存储结构和链式存储结构。
平均查找长度=(n+1)/2.
【顺序查找优缺点】:
缺点:是当n 很大时,平均查找长度较大,效率低;
优点:是对表中数据元素的存储没有要求。另外,对于线性链表,只能进行顺序查找。
1 #include<iostream>
2 using namespace std;
3
4 #define MAX 11
5
6 typedef int key_type;
7 typedef struct element
8 {
9 key_type key; //关键字
10 }elem;
11
12 int seq_search(elem e[], key_type key, int count)
13 {
14 int i = 0;
15
16 while(e[i].key != key && i < count)
17 {
18 i++;
19 }
20
21 if(i < count)
22 {
23 return(i + 1);
24 }
25 else
26 {
27 return -1;
28 }
29
30 }
31
32 int main(int argc, char** argv)
33 {
34 elem linelist[MAX] = {1, 5, 9, 7, 12, 11, 10, 8, 6, 2, 3};
35
36 int count = 11;
37
38 int i = 0;
39
40 key_type key = 8;
41
42 printf("线性表中的元素为:\n");
43
44 while(i < count)
45 {
46 printf("%d\n", linelist[i].key);
47 i++;
48 }
49
50
51 printf("\n关键字[%d]在线性表中的位置为[%d]\n", key, seq_search(linelist, key, count));
52
53
54 return 0;
55 }
输出结果:
2.有序顺序表--折半查找
在有序表中,取中间元素作为比较对象,若给定值与中间元素的关键码相等,则查找成功;若给定值小于中间元素的关键码,则在中间元素的左半区继续查找;若给定值大于中间元素的关键码,则在中间元素的右半区继续查找。不断重复上述查找过程,直到查找成功,或所查找的区域无数据元素,查找失败。
【步骤】
① low=1;high=length; // 设置初始区间
② 当low>high 时,返回查找失败信息// 表空,查找失败
③ low≤high,mid=(low+high)/2; //确定该区间的中点位置
a. 若kx<tbl.elem[mid].key,high = mid-1;转② // 查找在左半区进行
b. 若kx>tbl.elem[mid].key,low = mid+1; 转② // 查找在右半区进行
c. 若kx=tbl.elem[mid].key,返回数据元素在表中位置// 查找成功
有序表按关键码排列如下:
7,14,18,21,23,29,31,35,38,42,46,49,52
在表中查找关键码为14 的数据元素:
1 #include<iostream>
2 using namespace std;
3
4 #define MAX 12
5
6 typedef int key_type;
7 typedef struct element
8 {
9 key_type key; //关键字
10 }elem;
11
12 int binary_search(elem* e, key_type key, int count)
13 {
14 int i = -1;
15 int low = 0;
16 int high = count - 1;
17 int mid;
18
19 while(low < high)
20 {
21 mid = (low + high) / 2;
22
23 if(key < e[mid].key)
24 high = mid - 1;
25 else if(key > e[mid].key)
26 low = mid + 1;
27 else
28 {
29 i = mid;
30 break;
31 }
32 }
33
34 if(i < 0)
35 return -1;
36 else
37 return (i + 1);
38 }
39
40
41 int main(int argc, char** argv)
42 {
43 elem linelist[MAX] = {3, 5, 6, 8, 9, 12, 17, 23, 30, 35, 39, 42};
44 int count = 11;
45
46 int i = 0;
47
48 key_type key = 8;
49
50 printf("线性表中的元素为:\n");
51
52 while(i < count)
53 {
54 printf("%d\n", linelist[i].key);
55 i++;
56 }
57
58
59 printf("\n关键字[%d]在线性表中的位置为[%d]\n", key, binary_search(linelist, key, count));
60
61
62 return 0;
63 }
输出结果:
3.分块查找(索引查找)
分块查找又称索引顺序查找,是对顺序查找的一种改进。分块查找要求将查找表分成 若干个子表,并对子表建立索引表,查找表的每一个子表由索引表中的索引项确定。索引 项包括两个字段:关键码字段(存放对应子表中的最大关键码值) ;指针字段(存放指向对 应子表的指针) ,并且要求索引项按关键码字段有序。查找时,先用给定值kx 在索引表中 检测索引项,以确定所要进行的查找在查找表中的查找分块(由于索引项按关键码字段有序,可用顺序查找或折半查找) ,然后,再对该分块进行顺序查找。
如关键码集合为:
(22,12,13,9,20,33,42,44,38,24,48,60,58,74,49,86,53)
按关键码值31,62,88 分为三块建立的查找表及其索引表如下:
设表共n个结点,分b块,s=n/b
(分块查找索引表)平均查找长度=Log2(n/s+1)+s/2
(顺序查找索引表)平均查找长度=(S2+2S+n)/(2S)
1 #include<iostream>
2 using namespace std;
3
4 #define MAX 16
5
6 typedef int key_type;
7 typedef struct element
8 {
9 key_type key; //关键字
10 }elem;
11
12 typedef struct _index
13 {
14 key_type key;
15
16 int low, high;
17 }index;
18
19 int index_search(elem* e, key_type key, int count, index *idx, int idx_length)
20 {
21 int i;
22
23 int low = 0;
24 int high = idx_length - 1;
25 int mid;
26
27 //从块的索引表中查找关键字所在的块(使用折半查找法)
28 while(low <= high)
29 {
30 mid = (low + high) / 2;
31 if(key < idx[mid].key)
32 high = mid - 1;
33 else if(key > idx[mid].key)
34 low = mid + 1;
35 else
36 {
37 break;
38 }
39 }
40
41 //从块中查找关键值(使用顺序查找)
42 i = idx[mid].low;
43 while(i < idx[mid].high && e[i].key != key)
44 i++;
45
46 if(i > idx[mid].high)
47 return -1;
48 else
49 return i + 1;
50 }
51
52 int main(int argc, char** argv)
53 {
54 elem linelist[MAX] = {
55 8, 20, 13, 17,
56
57 40, 42, 45, 32,
58
59 49, 58, 50, 52,
60
61 67, 79, 78, 80
62 };
63
64 int count = sizeof(linelist)/sizeof(elem);
65 int i = 0;
66
67 key_type key = 50;
68
69 //建立索引表
70 index index_table[4] ={{20,0,3}, {45,4,7}, {58,8,11}, {80,12,15}};
71 int idx_length = sizeof(index_table)/sizeof(index);
72
73 printf("线性表中的元素为:\n");
74
75 while(i < count)
76 {
77 printf("%d\n", linelist[i].key);
78 i++;
79 }
80
81
82 printf("\n关键字[%d]在线性表中的位置为[%d]\n", key, index_search(linelist, key, count, index_table, idx_length));
83
84
85 return 0;
86 }
输出结果:
二.动态查找
1.二叉排序树
二叉排序树:
又称二叉查找树,或是一棵空树,其具有的性质如下:
1)若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值。
2)若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值。
3)左右子树本身也分别为二叉排序树。
一棵二叉排序树一定是二叉树,按中序遍历二叉排序树,所得到的中序遍历序列是一个递增有序序列。
1 #include<iostream>
2 #include<stdlib.h>
3 using namespace std;
4 struct BSTree
5 {
6 int data;
7 BSTree *left;
8 BSTree *right;
9 };
10 //标记在插入时,如果已存在,则为true ,表示不需要插入,否则为false
11 bool flag = false;
12 int a[100];
13 //查找操作
14 BSTree *search(BSTree *r,int x)
15 {
16 if(r==NULL)
17 return NULL;
18 else
19 {
20 if(r->data==x)
21 return r;
22 else if(r->data>x)
23 return search(r->left,x);
24 else
25 return search(r->right,x);
26 }
27 }
28 //插入操作
29 BSTree* insert(BSTree *r,BSTree *s)
30 {
31 //首先查找树中是否已存在此节点
32 BSTree *p = search(r,s->data);
33 if(p==NULL)
34 {
35 if(r==NULL)
36 {
37 r=s;
38 }
39 else if(s->data<r->data)
40 r->left = insert(r->left,s);
41 else if(s->data>r->data)
42 r->right = insert(r->right,s);
43 }
44 else
45 flag = true;
46 return r;
47 }
48 //建树
49 BSTree * createBSTree(BSTree *r,int *a,int n)
50 {
51 int i;
52 BSTree * t;
53 t = r;
54 for(i=0;i<n;i++)
55 {
56 BSTree *s = (BSTree*)malloc(sizeof(BSTree));
57 s->data=a[i];
58 s->left=NULL;
59 s->right=NULL;
60 t = insert(t,s);
61 }
62 return t;
63 }
64 //获得其父节点
65 BSTree *getFather(BSTree *r, BSTree *s)
66 {
67 BSTree *sf;
68 if(r==NULL||r==s)
69 sf=NULL;
70 else
71 {
72 if(s==r->left||s==r->right)
73 sf= r;
74 else if(s->data > r->data)
75 sf=getFather(r->right,s);
76 else
77 sf=getFather(r->left,s);
78 }
79 return sf;
80 }
81
82 //前序输出
83 void preOrder(BSTree *r)
84 {
85 if(r==NULL)
86 return;
87 else
88 {
89 cout<<r->data<<" ";
90 preOrder(r->left);
91 preOrder(r->right);
92 }
93 }
94 //中序输出
95 void inOrder(BSTree *r)
96 {
97 if(r==NULL)
98 return ;
99 else
100 {
101 inOrder(r->left);
102 cout<<r->data<<" ";
103 inOrder(r->right);
104 }
105 }
106 //后续输出
107 void postOrder(BSTree *r)
108 {
109 if(r==NULL)
110 return ;
111 else
112 {
113 postOrder(r->left);
114 postOrder(r->right);
115 cout<<r->data<<" ";
116 }
117 }
118 int main()
119 {
120
121 int n;
122 flag = false;
123 BSTree *root=NULL;
124 cout<<"请输入元素个数:"<<endl;
125 cin>>n;
126 int i;
127 cout<<"请输入这些元素:"<<endl;
128 for(i=0;i<n;i++)
129 cin>>a[i];
130 cout<<"建立二叉排序树!"<<endl;
131 root = createBSTree(root,a,n);
132 if(root!=NULL)
133 cout<<"二叉排序树建立成功!"<<endl;
134 else
135 {
136 cout<<"二叉排序树建立失败!"<<endl;
137 return 0;
138 }
139 cout<<"此二叉树根的值为:"<<endl;
140 cout<<root->data<<endl;
141 cout<<"请选择您要进行的操作:"<<endl;
142 cout<<"1.插入(I/i)"<<endl;
143 cout<<"2.查找(S/s)"<<endl;
144 cout<<"3.先序输出(P/p)"<<endl;
145 cout<<"4.中序输出(M/m)"<<endl;
146 cout<<"5.后序输出(L/l)"<<endl;
147 cout<<"6.退出(E/e)"<<endl;
148 char s;
149 cin>>s;
150 while(1)
151 {
152 if(s=='E'||s=='e')
153 break;
154 else if(s=='I'||s=='i')
155 {
156 cout<<"请输入您要插入的值:"<<endl;
157 int x;
158 cin>>x;
159 BSTree *p =(BSTree*)malloc(sizeof(BSTree));
160 p->data = x;
161 p->left = NULL;
162 p->right = NULL;
163 root = insert(root,p);
164 if(flag==false)
165 cout<<"插入成功!"<<endl;
166 else
167 {
168 cout<<"此二叉树中已存在此值!"<<endl;
169 flag=false;//恢复原值
170 }
171 }
172 else if(s=='S'||s=='s')
173 {
174 cout<<"请输入您要查找的值:"<<endl;
175 int x;
176 cin>>x;
177 BSTree *p=search(root,x);
178 BSTree *pfather=getFather(root,p);
179 cout<<"查找的值为:"<<p->data<<endl;
180 if(pfather!=NULL)
181 cout<<"其父节点的值为:"<<pfather->data<<endl;
182 else
183 cout<<"它是根节点,没有父节点!"<<endl;
184 if(p->left==NULL&&p->right==NULL)
185 cout<<"它是叶子节点,没有子节点"<<endl;
186 else
187 {
188 if(p->left != NULL)
189 cout<<"其左儿子节点的值为:"<<p->left->data<<endl;
190 else
191 cout<<"其左儿子节点为空!"<<endl;
192 if(p->right != NULL)
193 cout<<"其右儿子的值为:"<<p->right->data<<endl;
194 else
195 cout<<"其右儿子节点为空!"<<endl;
196 }
197 }
198
199 else if(s=='P'||s=='p')
200 {
201 cout<<"其前序输出为:"<<endl;
202 preOrder(root);
203 cout<<endl;
204 }
205 else if(s=='M'||s=='m')
206 {
207 cout<<"其中序输出为:"<<endl;
208 inOrder(root);
209 cout<<endl;
210 }
211 else if(s=='L'||s=='l')
212 {
213 cout<<"其后序输出为:"<<endl;
214 postOrder(root);
215 cout<<endl;
216 }
217 else
218 {
219 cout<<"命令有误,请重新输入!"<<endl;
220 }
221 cout<<"请选择您要进行的操作:"<<endl;
222 cin>>s;
223 }
224
225
226 return 0;
227 }
输出结果:
2.平衡二叉树
在二叉查找树中,如果插入元素的顺序接近有序,那么二叉查找树将退化为链表,从而导致二叉查找树的查找效率大为降低。如何使得二叉查找树无论在什么样情况下都能使它的形态最大限度地接近满二叉树以保证它的查找效率呢?
前苏联科学家G.M. Adelson-Velskii 和 E.M. Landis给出了答案。他们在1962年发表的一篇名为《An algorithm for the organization of information》的文章中提出了一种自平衡二叉查找树(self-balancing binary search tree)。这种二叉查找树在插入和删除操作中,可以通过一系列的旋转操作来保持平衡,从而保证了二叉查找树的查找效率。最终这种二叉查找树以他们的名字命名为“AVL-Tree”,它也被称为平衡二叉树(Balanced Binary Tree)。
什么是平衡
为了保证平衡,AVL树中的每个结点都有一个平衡因子(balance factor,以下用BF表示),它表示这个结点的左、右子树的高度差,也就是左子树的高度减去右子树的高度的结果值。AVL树上所有结点的BF值只能是-1、0、1。反之,只要二叉树上一个结点的BF的绝对值大于1,则该二叉树就不是平衡二叉树。图1演示了平衡二叉树和非平衡二叉树。
具体操作和理解请参照:http://www.cnblogs.com/abatei/archive/2008/11/17/1335031.html
http://blog.csdn.net/happycock/article/details/20874 这两个博客讲得很清楚了,以下给出C++实现代码:
1 //AvlTreeNode平衡二叉树节点
2 #include <iostream>
3 using namespace std;
4 template <class T>
5 class AvlTreeNode
6 {
7 public:
8 AvlTreeNode<T> *left;
9 AvlTreeNode<T> *right;
10 AvlTreeNode<T> *parent;
11 int balanceFactor;//平衡因子
12 T data;
13 AvlTreeNode(const T& item, AvlTreeNode<T>* lptr, AvlTreeNode<T>* rptr, AvlTreeNode<T>* par, int balfac);
14 };
15 //AvlTree平衡二叉树
16 template <class T>
17 class AvlTree
18 {
19 public:
20 AvlTreeNode<T>* root;
21 AvlTreeNode<T>* current;
22 int size;
23 AvlTreeNode<T>* LL(AvlTreeNode<T>* tree);
24 AvlTreeNode<T>* LR(AvlTreeNode<T>* tree);
25 AvlTreeNode<T>* RR(AvlTreeNode<T>* tree);
26 AvlTreeNode<T>* RL(AvlTreeNode<T>* tree);
27 void buildParent(AvlTreeNode<T>* tree);
28
29 bool RotateSubTree(AvlTreeNode<T>* tree);
30 AvlTree(void);
31 AvlTreeNode<T>* Search(const T item);
32 bool Insert(T item);
33 bool Delete(T item);
34 void Destory(AvlTreeNode<T>* tree);
35 ~AvlTree(void);
36 };
37 template <class T>
38 AvlTreeNode<T>::AvlTreeNode(const T &item, AvlTreeNode<T> *lptr = NULL, AvlTreeNode<T> *rptr = NULL, AvlTreeNode<T>* pr=NULL, int balfac = 0):data(item),left(lptr),right(rptr),parent(pr),balanceFactor(balfac)
39 { }
40 template <class T>
41 AvlTree<T>::AvlTree():root(NULL),size(0),current(NULL)
42 { }
43
44 //向平衡二叉树中插入一个元素
45 template <class T>
46 bool AvlTree<T>::Insert(const T item)
47 {
48 //先找到位置
49 AvlTreeNode<T>* tempPosition = this->root;
50 AvlTreeNode<T>* pre = tempPosition;
51 for(;;)
52 {
53 //如果已经到达要插入的地方,则插入新节点
54 if(tempPosition == NULL)
55 {
56 //实例化新节点
57 AvlTreeNode<T>* newNode = new AvlTreeNode<T>(item);
58 //如果插入位置为其父节点的左节点,则把新节点挂接到其父节点的左节点上,否则挂接到其父节点的右节点上
59 if(tempPosition==this->root)
60 {
61 root = newNode;
62 size++;
63 return true;
64 }
65 else if(tempPosition == pre->left && item<pre->data)
66 {
67 newNode->parent = pre;
68 pre->left = tempPosition = newNode;
69 size++;
70 break;
71 }
72 else
73 {
74 newNode->parent = pre;
75 pre->right = tempPosition = newNode;
76 size++;
77 break;
78 }
79 }
80
81 else if(item < tempPosition->data)
82 {
83 //保存tempPosition前一节点
84 pre = tempPosition;
85 //在左子数寻找插入点
86 tempPosition = tempPosition->left;
87 }
88 else if(item > tempPosition->data)
89 {
90 //保存tempPosition前一节点
91 pre = tempPosition;
92 //在右子数寻找插入点
93 tempPosition = tempPosition->right;
94 }
95 else
96 {
97 //已存在此元素
98 return false;
99 }
100 }
101 //调整以使得树平衡
102 int bf = 0;
103 while(tempPosition->parent)
104 {
105 //bf表示平衡因子的改变量,当新节点插入到左子树,则平衡因子+1
106 //当新节点插入到右子树,则平衡因子-1
107 bf = item<tempPosition->parent->data?1:-1;
108
109 tempPosition = tempPosition->parent;//将指针指向父节点
110
111 tempPosition->balanceFactor += bf;//改变父节点的平衡因子
112
113 bf = tempPosition->balanceFactor;//获取当前节点的平衡因子
114 //判断当前节点平衡因子,如果为表示改子树已平衡,不需要在回溯
115 //
116 if(bf==0)
117 {
118 return true;
119 }
120 else if(bf==2||bf==-2)
121 {
122 //调整树以使其平衡
123 RotateSubTree(tempPosition);
124 buildParent(root);
125 root->parent = NULL;
126 return true;
127 }
128 }
129 return true;
130 }
131 //寻找值为item的AvlTree节点
132 template <class T>
133 AvlTreeNode<T>* AvlTree<T>::Search(const T item)
134 {
135 current = this->root;
136 for(;;)
137 {
138 //current为NULL,没有此节点
139 if(current==NULL)
140 return NULL;
141 //current不为NULL,继续寻找
142 if(item == current->data)
143 return current;
144 else if(item < current->data)
145 current = current->left;
146 else if(item > current->data)
147 current = current->right;
148 }
149 }
150
151 //删除一个节点
152 template <class T>
153 bool AvlTree<T>::Delete(const T item)
154 {
155 AvlTreeNode<T>* deleteNode = Search(item);
156 //如果所删节点不存在,返回
157 if(deleteNode==NULL)
158 return false;
159 //pre在以下的程序中是tempPosition的父节点
160 AvlTreeNode<T>* tempPosition = NULL;
161 AvlTreeNode<T>* pre = tempPosition;
162 //存储真正删除的节点
163 AvlTreeNode<T>* trueDeleteNode;
164 //当被删除结点存在左右子树时
165 if (deleteNode->left != NULL && deleteNode->right != NULL)
166 {
167 //获取左子树
168 tempPosition = deleteNode->left;
169 //获取deleteNode的中序遍历前驱结点,并存放于tempPosition中
170 while (tempPosition->right != NULL)
171 {
172 //找到左子树中的最右下结点
173 tempPosition = tempPosition->right;
174 }
175 //用中序遍历前驱结点的值代替被删除结点的值
176 deleteNode->data = tempPosition->data;
177
178 if (tempPosition->parent == deleteNode)
179 {
180 //如果被删元素的前驱是其左孩子
181 tempPosition->parent->left = tempPosition->left;
182 }
183 else
184 {
185 tempPosition->parent->right = tempPosition->left;
186 }
187 //得到真正删除的节点
188 trueDeleteNode = tempPosition;
189 }
190 else //当只有左子树或右子树或为叶子结点时
191 {
192 //首先找到惟一的孩子结点
193 pre = deleteNode->parent;
194 tempPosition = deleteNode->left;
195 if (tempPosition == NULL) //如果只有右孩子或没孩子
196 {
197 tempPosition = deleteNode->right;
198 }
199
200 if (deleteNode!=root)
201 {
202 //如果删除节点不是根节点
203 if (deleteNode->parent->left == deleteNode)
204 { //如果被删结点是左孩子
205 deleteNode->parent->left = tempPosition;
206 }
207 else
208 { //如果被删结点是右孩子
209 deleteNode->parent->right = tempPosition;
210 }
211 }
212 else
213 {
214 //当删除的是根结点时
215 root = tempPosition;
216 }
217 //得到真正删除的节点
218 trueDeleteNode = deleteNode;
219 }
220 //pre为真正删除节点的父节点
221 pre = trueDeleteNode==NULL?NULL:trueDeleteNode->parent;
222 //删除完后进行旋转,现在pre指向实际被删除的结点
223 while (pre)
224 { //bf表示平衡因子的改变量,当删除的是左子树中的结点时,平衡因子-1
225 //当删除的是右子树的孩子时,平衡因子+1
226 int bf = (trueDeleteNode->data <= pre->data) ? -1 : 1;
227 pre->balanceFactor += bf; //改变当父结点的平衡因子
228 tempPosition = pre;
229 pre = pre->parent;
230 bf = tempPosition->balanceFactor; //获取当前结点的平衡因子
231 if (bf != 0) //如果bf==0,表明高度降低,继续后上回溯
232 {
233 //如果bf为或-1则说明高度未变,停止回溯,如果为或-2,则进行旋转
234 //当旋转后高度不变,则停止回溯
235 if (bf == 1 || bf == -1 || !RotateSubTree(tempPosition))
236 {
237 break;
238 }
239 }
240 }
241 buildParent(root);
242 if(root!=NULL)
243 root->parent = NULL;
244 delete trueDeleteNode;//析构真正删除的节点
245 size--;
246 return true;
247 }
248 //调整函数
249 template <class T>
250 bool AvlTree<T>::RotateSubTree(AvlTreeNode<T>* tree)
251 {
252 this->current = tree;
253 bool tallChange = true;
254 int bf = tree->balanceFactor;
255 AvlTreeNode<T>* newRoot = NULL;
256
257 if (bf == 2) //当平衡因子为时需要进行旋转操作
258 {
259 int leftBF = current->left->balanceFactor;//得到左子树的平衡因子
260 if (leftBF == -1)
261 {
262 newRoot = LR(tree);//LR型旋转(左子树中插入右孩子,先左后右双向旋转)
263 }
264 else if (leftBF == 1)
265 {
266 newRoot = LL(tree); //LL型旋转(左子树中插入左孩子,右单转)
267 }
268 else //当旋转根左孩子的bf为时,只有删除时才会出现
269 {
270 newRoot = LL(tree);
271 tallChange = false;
272 }
273 }
274 if (bf == -2) //当平衡因子为-2时需要进行旋转操作
275 {
276 int rightBF = current->right->balanceFactor; //获取旋转根右孩子的平衡因子
277 if (rightBF == 1)
278 {
279 newRoot = RL(tree); //RL型旋转(右子树中插入左孩子,先右后左双向旋转)
280 }
281 else if (rightBF == -1)
282 {
283 newRoot = RR(tree); //RR型旋转(右子树中插入右孩子,左单转)
284 }
285 else //当旋转根左孩子的bf为时,只有删除时才会出现
286 {
287 newRoot = RR(tree);
288 tallChange = false;
289 }
290 }
291 //更改新的子树根
292 if (current->parent!=NULL)
293 {
294 //如果旋转根为不是AVL树的根
295
296 //newRoot为新旋转后得到的当前子树的根,一下判断的作用是
297 //如果原来的根是其父节点的左子树,则新根也挂接到其父节点的左子树,
298 //否则挂接到右子树
299 if (tree->data < (tree->parent)->data)
300 {
301 current->parent->left = newRoot;
302 }
303 else
304 {
305 current->parent->right = newRoot;
306 }
307 }
308 else
309 {
310 //如果旋转根为AVL树的根,则指定新AVL树根结点
311 this->root = newRoot;
312 }
313 return tallChange;
314 }
315
316 //LL型旋转,返回旋转后的新子树根
317 template <class T>
318 AvlTreeNode<T>* AvlTree<T>::LL(AvlTreeNode<T>* tree)
319 {
320 AvlTreeNode<T>* treeNext = tree->left;
321 tree->left = treeNext->right;
322 treeNext->right = tree;
323
324 if (treeNext->balanceFactor == 1)
325 {
326 tree->balanceFactor = 0;
327 treeNext->balanceFactor = 0;
328 }
329 else //treeNext->balanceFactor==0的情况,删除时用
330 {
331 tree->balanceFactor = 1;
332 treeNext->balanceFactor = -1;
333 }
334 return treeNext; //treeNext为新子树的根
335 }
336 //LR型旋转,返回旋转后的新子树根
337 template <class T>
338 AvlTreeNode<T>* AvlTree<T>::LR(AvlTreeNode<T>* tree)
339 {
340 AvlTreeNode<T>* treeNext = tree->left;
341 AvlTreeNode<T>* newRoot = treeNext->right;
342 tree->left = newRoot->right;
343 treeNext->right = newRoot->left;
344 newRoot->left = treeNext;
345 newRoot->right = tree;
346
347 switch (newRoot->balanceFactor) //改变平衡因子
348 {
349 case 0:
350 tree->balanceFactor = 0;
351 treeNext->balanceFactor = 0;
352 break;
353 case 1:
354 tree->balanceFactor = -1;
355 treeNext->balanceFactor = 0;
356 break;
357 case -1:
358 tree->balanceFactor = 0;
359 treeNext->balanceFactor = 1;
360 break;
361 }
362 newRoot->balanceFactor = 0;
363 return newRoot; //newRoot为新子树的根
364 }
365 //RR型旋转,返回旋转后的新子树根
366 template <class T>
367 AvlTreeNode<T>* AvlTree<T>::RR(AvlTreeNode<T>* tree)
368 {
369 AvlTreeNode<T>* treeNext = tree->right;
370 tree->right = treeNext->left;
371 treeNext->left = tree;
372
373 if (treeNext->balanceFactor == -1)
374 {
375 tree->balanceFactor = 0;
376 treeNext->balanceFactor = 0;
377 }
378 else //treeNext->balanceFactor==0的情况,删除时用
379 {
380 tree->balanceFactor = -1;
381 treeNext->balanceFactor = 1;
382 }
383 return treeNext; //treeNext为新子树的根
384 }
385 //RL型旋转,返回旋转后的新子树根
386 template <class T>
387 AvlTreeNode<T>* AvlTree<T>::RL(AvlTreeNode<T>* tree)
388 {
389 AvlTreeNode<T>* treeNext = tree->right;
390 AvlTreeNode<T>* newRoot = treeNext->left;
391 tree->right = newRoot->left;
392 treeNext->left = newRoot->right;
393 newRoot->right = treeNext;
394 newRoot->left = tree;
395
396 switch (newRoot->balanceFactor) //改变平衡因子
397 {
398 case 0:
399 tree->balanceFactor = 0;
400 treeNext->balanceFactor = 0;
401 break;
402 case 1:
403 tree->balanceFactor = 0;
404 treeNext->balanceFactor = -1;
405 break;
406 case -1:
407 tree->balanceFactor = 1;
408 treeNext->balanceFactor = 0;
409 break;
410 }
411 newRoot->balanceFactor = 0;
412 return newRoot; //newRoot为新子树的根
413 }
414 //重新建立所有节点的父节点连接
415 template <class T>
416 void AvlTree<T>::buildParent(AvlTreeNode<T>* tree)
417 {
418 //如果树为空则直接返回,如果树有节点的parent置为当前tree节点
419 if(tree==NULL)
420 {
421 return;
422 }
423 if(tree->left!=NULL)
424 {
425 tree->left->parent = tree;
426 }
427 if(tree->right!=NULL)
428 {
429 tree->right->parent = tree;
430 }
431 //递归遍历子节点建立父节点连接
432 buildParent(tree->left);
433 buildParent(tree->right);
434 }
435 //销毁平衡树
436 template <class T>
437 void AvlTree<T>::Destory(AvlTreeNode<T>* tree)
438 {
439 //如果树为空
440 if(tree==NULL)
441 return;
442 //如果树不为空
443 if(tree->left!=NULL || tree->right!=NULL)
444 {
445 //析构左子树
446 if(tree->left!=NULL)
447 {
448 Destory(tree->left);
449 tree->left = NULL;
450 }
451 //析构右子树
452 if(tree->right!=NULL)
453 {
454 Destory(tree->right);
455 tree->right = NULL;
456 }
457 }
458 //析构树
459 delete tree;
460 }
461
462 template <class T>
463 AvlTree<T>::~AvlTree(void)
464 {
465 Destory(root);
466 }
467 //主函数测试
468 int main()
469 {
470 AvlTree<int>* intAvlTree = new AvlTree<int>();
471 intAvlTree->Insert(10);
472 intAvlTree->Insert(14);
473 intAvlTree->Insert(9);
474 intAvlTree->Insert(13);
475 intAvlTree->Insert(15);
476 intAvlTree->Insert(12);
477 intAvlTree->Insert(11);
478
479 intAvlTree->Delete(13);
480 intAvlTree->Delete(11);
481 intAvlTree->Delete(14);
482 intAvlTree->Delete(10);
483 intAvlTree->Delete(9);
484 intAvlTree->Delete(15);
485 intAvlTree->Delete(12);
486 //cout<<intAvlTree->Search(11)->data<<endl;
487 delete intAvlTree;
488 return 0;
489 }
以上代码参考:http://www.cnblogs.com/hxf829/archive/2009/04/10/1659805.html
3.B-树和B+树
注:B-树,即为B树。因为B树的原英文名称为B-tree,而国内很多人喜欢把B-tree译作B-树,其实,这是个非常不好的直译,很容易让人产生误解。如人们可能会以为B-树是一种树,而B树又是一种一种树。而事实上是,B-tree就是指的B树。
B-树 是一种多路搜索树(并不是二叉的):
1.定义任意非叶子结点最多只有M个儿子;且M>2;
2.根结点的儿子数为[2, M];
3.除根结点以外的非叶子结点的儿子数为[M/2, M];
4.每个结点存放至少M/2-1(取上整)和至多M-1个关键字;(至少2个关键字)
5.非叶子结点的关键字个数=指向儿子的指针个数-1;
6.非叶子结点的关键字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];
7.非叶子结点的指针:P[1], P[2], …, P[M];其中P[1]指向关键字小于K[1]的
子树,P[M]指向关键字大于K[M-1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树;
8.所有叶子结点位于同一层;
如:(M=3)
B-树的搜索,从根结点开始,对结点内的关键字(有序)序列进行二分查找,如果命中则结束,否则进入查询关键字所属范围的儿子结点;重复,直到所对应的儿子指针为空,或已经是叶子结点;
B-树的特性:
1.关键字集合分布在整颗树中;
2.任何一个关键字出现且只出现在一个结点中;
3.搜索有可能在非叶子结点结束;
4.其搜索性能等价于在关键字全集内做一次二分查找;
5.自动层次控制;
由于限制了除根结点以外的非叶子结点,至少含有M/2个儿子,确保了结点的至少利用率,其最底搜索性能为:
其中,M为设定的非叶子结点最多子树个数,N为关键字总数; 所以B-树的性能总是等价于二分查找(与M值无关),也就没有B树平衡的问题;
由于M/2的限制,在插入结点时,如果结点已满,需要将结点分裂为两个各占M/2的结点;删除结点时,需将两个不足M/2的兄弟结点合并;
B+树
B+树是B-树的变体,也是一种多路搜索树:
1.其定义基本与B-树同,除了:
2.非叶子结点的子树指针与关键字个数相同;
3.非叶子结点的子树指针P[i],指向关键字值属于[K[i], K[i+1])的子树
(B-树是开区间);
5.为所有叶子结点增加一个链指针;
6.所有关键字都在叶子结点出现;
如:(M=3)
B+的搜索与B-树也基本相同,区别是B+树只有达到叶子结点才命中(B-树可以在非叶子结点命中),其性能也等价于在关键字全集做一次二分查找;
B+的特性:
1.所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的;
2.不可能在非叶子结点命中;
3.非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层;
4.更适合文件索引系统;
B*树
是B+树的变体,在B+树的非根和非叶子结点再增加指向兄弟的指针;
B*树定义了非叶子结点关键字个数至少为(2/3)*M,即块的最低使用率为2/3(代替B+树的1/2);
B+树的分裂:当一个结点满时,分配一个新的结点,并将原结点中1/2的数据复制到新结点,最后在父结点中增加新结点的指针;B+树的分裂只影响原结点和父结点,而不会影响兄弟结点,所以它不需要指向兄弟的指针;
B*树的分裂:当一个结点满时,如果它的下一个兄弟结点未满,那么将一部分数据移到兄弟结点中,再在原结点插入关键字,最后修改父结点中兄弟结点的关键字(因为兄弟结点的关键字范围改变了);如果兄弟也满了,则在原结点与兄弟结点之间增加新结点,并各复制1/3的数据到新结点,最后在父结点增加新结点的指针;
所以,B*树分配新结点的概率比B+树要低,空间使用率更高;
总结:
B-树:多路搜索树,每个结点存储M/2到M个关键字,非叶子结点存储指向关键
字范围的子结点;
所有关键字在整颗树中出现,且只出现一次,非叶子结点可以命中;
B+树:在B-树基础上,为叶子结点增加链表指针,所有关键字都在叶子结点
中出现,非叶子结点作为叶子结点的索引;B+树总是到叶子结点才命中;
B*树:在B+树基础上,为非叶子结点也增加链表指针,将结点的最低利用率
从1/2提高到2/3;
三.散列表
哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。说白了哈希表的原理其实就是通过空间换取时间的做法。。
哈希表的做法其实很简单,就是把Key通过一个固定的算法函数既所谓的哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,将value存储在以该数字为下标的数组空间里。
而当使用哈希表进行查询的时候,就是再次使用哈希函数将key转换为对应的数组下标,并定位到该空间获取value,如此一来,就可以充分利用到数组的定位性能进行数据定位。。
哈希函数的构造方法:
若对于关键字集合中的任一个关键字,哈希函数映像到地址集合中任何一个地址的概率是相等的,则称此类哈希函数为均匀的哈希函数。换句话说,就是使关键字经过哈希函数得到一个“随机的地址“,以便使一组关键字的哈希地址均匀分布在整个地址区间中,从而减少冲突。
(1)直接定址法
取关键字或关键字的某个线性函数值为哈希地址。即: H(key)=key或H(key)=a*key+b; 其中a和b为常数(这种哈希函数叫做自身函数)。
由于直接定址所得地址集合和关键字集合的大小相同。因此,对于不同的关键字不会发生冲突。但实际中使用这种哈希函数的情况很少。
(2)数字分析法
假设关键字是以r为基的数(如:以10为基的十进制数),并且哈希表中可能出现的关键字都是事先知道的,则可取关键字的若干数位组成哈希地址。
(3)平方取中法
取关键字平方后的中间几位为哈希地址。
(4)斐波那契(Fibonacci)散列法
平方散列法的缺点是显而易见的,所以我们能不能找出一个理想的乘数,而不是拿value本身当作乘数呢?答案是肯定的。
1)对于16位整数而言,这个乘数是40503
2)对于32位整数而言,这个乘数是2654435769
3)对于64位整数而言,这个乘数是11400714819323198485
这几个“理想乘数”是如何得出来的呢?这跟一个法则有关,叫黄金分割法则,而描述黄金分割法则的最经典表达式无疑就是著名的斐波那契数列,即如此形式的序列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946,…。另外,斐波那契数列的值和太阳系八大行星的轨道半径的比例出奇吻合。
对我们常见的32位整数而言,公式: index = (value * 2654435769) >> 28
(5)折叠法
将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为哈希地址,这方法称为折叠法。
(6)除留余数法(==最常用的方法)
取关键字被某个不大于哈希表表长m的数p除后所得余数为哈希地址。即H(key)=key MOD p,(p<=m),这是一种最简单,也是最常用的构造哈希函数的方法。它不仅可以对关键字直接取模(MOD),也可在折叠、平方取中等运算之后取模。
(7)随机数法
选择一个随机函数,取关键字的随机函数值为它的哈希地址,即H(key)=random(key),其中random为随机函数。通常,当关键字长度不等时采用此法构造哈希函数较切当。
总结:
实际工作中需视不同情况采用不同的哈希函数,通常,考虑的因素有:
(1)计算哈希函数所需时间(包括硬件指令的因素);
(2)关键字的长度;
(3)哈希表的大小;
(4)关键字的分布情况;
(5)记录的查找频率处理冲突的方法(冲突只能减少,不能避免) (1)开放定址 (2)再哈希法
(3)链地址法
(4)建立一个公共溢出区
哈希表的查找及其分析:
在哈希表上进行查找的过程和哈希造表的过程基本一致。给定的K值,根据造表时设定的哈希函数求得哈希地址,若表中此位置上没有记录,则查找不成功;否则必将关键字,若和给定的值相等,则查找成功;否则根据造表时设定的处理冲突的方法找”下一地址“,直至哈希表中的某个位置为”空“或者表中所填记录的关键字等于给定值时为止。
哈希表的装填因子定义为:α=(表中填入的记录数)/(哈希表的长度)
参考:
http://blog.csdn.net/hguisu/article/details/7786014
http://www.cppblog.com/Cass/archive/2011/10/05/157576.html
http://www.cnblogs.com/oldhorse/archive/2009/11/16/1604009.html
http://www.cnblogs.com/abatei/archive/2008/11/17/1335031.html