查找算法

概述:

查找算法:就是在是数据元素集合中查看是否存在于指定的关键字相等的元素。

查找分为两种:静态查找和动态查找。

1) 静态查找:是指在数据元素集合中查找与给定的关键字相等的元素

2) 动态查找:就是指在查找过程中,如果数据元素集合中不存在与给定的关键字相等的元素,则将该元素插入到数据元素集合中。

静态查找主要包括顺序表、有序顺序表和索引顺序表的查找。

1) 顺序表的查找,就是指从表的第一个元素与给定关键字比较,直到表的最后。

2) 有序顺序表的查找,在查找的过程中如果给定的关键字大于表的元素,就可以停止查找,说明表中不存在该元素(假设表中的元素按照关键字从小到大排列,并且查找从第一个元素开始比较)

3) 索引顺序表的查找是为主表建立一个索引,根据所以确定元素所在的范围,这样可以有效地提高查找的效率。

 

动态查找主要包括二叉排序树、平衡二叉树、B_树和B+树。

这些都是利用二叉树和树的特点对数据元素集合进行排序,通过将元素插入到二叉树或树中建立二叉树或树,然后通过对二叉树或树德遍历按照从小到大输出元素的序列。

 

散列表是利用散列函数的映射关系直接确定要查找元素的位置,大大减少了与元素的关键字的比较次数。

建立散列表的方法主要有直接定址法、平方取中法、折叠法和除留余数法(最常用)等。

但是会存在冲突,解决冲突的最为常用的方法主要有两个:开放定址法和链地址法。

 

一.静态查找

1.顺序表的查找

顺序查找(Sequential Search)又称为线性查找,是一种最简单的查找方法。
 

 从表的一端开始,向另一端逐个按给定值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 }  

输出结果:

折半查找算法的平均查找长度为O(logn),与顺序查找方法相比,折半查找算法的效率高。速度比顺序查找快。
 
但折半查找只能使用于有序表,需要对n个元素预先进行排序(仅限于顺序存储结构,对于线性链表无法in性折半查找)。
 

3.分块查找(索引查找)

分块查找又称索引顺序查找,是对顺序查找的一种改进。分块查找要求将查找表分成 若干个子表,并对子表建立索引表,查找表的每一个子表由索引表中的索引项确定。索引 项包括两个字段:关键码字段(存放对应子表中的最大关键码值) ;指针字段(存放指向对 应子表的指针) ,并且要求索引项按关键码字段有序。查找时,先用给定值kx 在索引表中 检测索引项,以确定所要进行的查找在查找表中的查找分块(由于索引项按关键码字段有序,可用顺序查找或折半查找) ,然后,再对该分块进行顺序查找。

 

如关键码集合为:

                           (22,12,13,9,20,33,42,44,38,24,48,60,58,74,49,86,53)

按关键码值31,62,88 分为三块建立的查找表及其索引表如下:

                              

 

分块查找的函数分为如下两步:
a)首先确定待查找的结点属于哪一块,即查找所在的块;
b)然后,在块内查找要查的结点。

 

设表共n个结点,分b块,s=n/b

(分块查找索引表)平均查找长度=Log2n/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

posted @ 2014-03-30 20:51  拦云  阅读(1874)  评论(0编辑  收藏  举报