1 / 135

第 六 章 查找

第 六 章 查找. 6.1 静态查找技术 6.2 二叉排序树 6.3 平衡二叉排序树 (AVL 树 ) *6.4 红 - 黑树 * 6.5 B- 树和 B+ 树 6.6 哈希 (Hash) 方法. i. 静态查找技术. 1 、搜索:. 在数据集合之中,搜索具有特定关键字的结点。. 通常分为 静态搜索表 :集合中的结点总数是固定的或者很少发生变化。可以无序或组 织成有序表。 动态搜索表 :集合中的结点总数是经常在发生变化。组织成树形结构。 在 内存 中进行的搜索:重点减少比较、或查找的次数。评价标准:平均搜索长度。

Download Presentation

第 六 章 查找

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第 六 章 查找 6.1 静态查找技术 6.2 二叉排序树 6.3 平衡二叉排序树(AVL树) *6.4 红-黑树 *6.5 B-树和B+树 6.6 哈希(Hash)方法

  2. i 静态查找技术 • 1、搜索: • 在数据集合之中,搜索具有特定关键字的结点。 • 通常分为静态搜索表:集合中的结点总数是固定的或者很少发生变化。可以无序或组 织成有序表。 • 动态搜索表:集合中的结点总数是经常在发生变化。组织成树形结构。 • 在内存中进行的搜索:重点减少比较、或查找的次数。评价标准:平均搜索长度。 • 在外存中进行的搜索:重点在于减少访问外存的次数。评价标准:读盘次数。 • 2、静态搜索结构: 采用静态向量(或数组),0号单元用作哨兵单元,1号单元到n号单元保存结点。 哨兵单元 Vector 8 100 10 ……………… 0 7 1 3 0 1 2 n-3 n-2 n-1 n

  3. 静态查找表(ADT) template <class Type> class Vector { public: Vector ( int size); // 构造函数,静态查找表的结点个数为size. ~ Vector ( ) { delete [ ]Array; } const Type & operator [ ] ( int index ) const; //具有越界检查的下标操作。 const Vector & operator = ( const Vector & R); //大小相等的向量的复制。 int Length( ) const { return ArraySize; } void Double( ) { Resize( ArraySize * 2 );} // 在向量的单元用完时,容量加倍。 void Resize( int Newsize); // 修改向量的大小。 void Sentinel( const Type key ){ Array[0] = key; } //置0号单元的内容为待查key。 protected: Type * Array; // 保存结点的数组 intArraySize; // 大小。 void GetArray( ); Vector(const Vector & R ); // 冻结使用构造函数复制另一向量的功能。 };

  4. 静态查找表(ADT) • 部分操作的实现: template <class Type> const Type & Vector<Type> :: operator [ ] ( int index ) const{ Exception( index ≤0 || index > ArraySize, “Out of boundary!”); return Array[index]; } template <class Type> const Vector<Type> & Vector<Type> :: operator = ( const Vector<Type> R ){ if ( this != &R ) { Exception( ArraySize != R.ArraySize, “Size is not same!”); for(int k = 1; k <= ArraySize; k++ ) Array[k] = R.Array[k]; } return *this; }

  5. 静态查找表(ADT) • 部分操作的实现: template <class Type> void Vector<Type> :: Resize( int NewSize ){ Type * oldArray = Array; const int minsize = Min(ArraySize, NewSize); // 取二者小者。 ArraySize = NewSize; GetArray( ); for(int k = 1; k <= minsize; k++ ) Array[k] = oldArray[k]; delete [ ]oldArray; } template <class Type> void Vector<Type> :: GetArray(){ Array = new Type[ArraySize+1]; } template <class Type> Vector<Type> :: Vector( int Size){ ArraySize = Size; GetArray( ); }

  6. i 顺序查找 • 应用范围:顺序表或线性链表表示的静态查找表。表内元素之间无序或有序。 template <class Type> int SeqSearch( const Vector<Type> & A, const Type key,int N ){ A[0] = key; // 将待查key 置于哨兵单元,可省掉越界检查。 for ( int k = N; A[k] != key; --k ) return k; // 返回0,查找失败。否则,找到关键字为key 的结点的下标 } 设置哨兵的好处: 在顺序表中总可以找到待查结点。否则,必须将 判断条件 i >= 0 加进 for 语句。 e.g: 查找 x = 8 的结点所在的数组元素的下标。 key 8 100 10 ……………… 0 7 1 3 向量 Vector 0 1 2 n-3 n-2 n-1 n

  7. i 顺序查找 • 应用范围:顺序表或线性链表表示的静态查找表。表内元素之间无序或有序。 template <class Type> int SeqSearch( const Vector<Type> & A, const Type key,int N ){ A[0] = key; // 将待查key 置于哨兵单元,可省掉越界检查。 for ( int k = N; A[k] != key; --k ) return k; // 返回0,查找失败。否则,找到关键字为key 的结点的下标 } 设置哨兵的好处: 在顺序表中总可以找到待查结点。否则,必须将 判断条件 i >= 0 加进 for 语句。 e.g: 查找 x = 8 的结点所在的数组元素的下标。 key 8 100 10 ……………… 0 7 1 3 向量 Vector 0 1 2 n-3 n-2 n-1 n

  8. i 顺序查找 • 应用范围:顺序表或线性链表表示的静态查找表。表内元素之间无序或有序。 template <class Type> int SeqSearch( const Vector<Type> & A, const Type key,int N ){ A[0] = key; // 将待查key 置于哨兵单元,可省掉越界检查。 for ( int k = N; A[k] != key; --k ) return k; // 返回0,查找失败。否则,找到关键字为key 的结点的下标 } 设置哨兵的好处: 在顺序表中总可以找到待查结点。否则,必须将 判断条件 i >= 0 加进 for 语句。 e.g: 查找 x = 8 的结点所在的数组元素的下标。 key 8 100 10 ……………… 0 7 1 3 向量 Vector 0 1 2 n-3 n-2 n-1 n

  9. i 顺序查找 • 应用范围:顺序表或线性链表表示的静态查找表。表内元素之间无序或有序。 template <class Type> int SeqSearch( const Vector<Type> & A, const Type key,int N ){ A[0] = key; // 将待查key 置于哨兵单元,可省掉越界检查。 for ( int k = N; A[k] != key; --k ) return k; // 返回0,查找失败。否则,找到关键字为key 的结点的下标 } 设置哨兵的好处: 在顺序表中总可以找到待查结点。否则,必须将 判断条件 i >= 0 加进 for 语句。 e.g: 查找 x = 8 的结点所在的数组元素的下标。 key 8 100 10 ……………… 0 7 1 3 向量 Vector 0 1 2 n-3 n-2 n-1 n

  10. i 顺序查找 • 应用范围:顺序表或线性链表表示的静态查找表。表内元素之间无序或有序。 template <class Type> int SeqSearch( const Vector<Type> & A, const Type key,int N ){ A[0] = key; // 将待查key 置于哨兵单元,可省掉越界检查。 for ( int k = N; A[k] != key; --k ) return k; // 返回0,查找失败。否则,找到关键字为key 的结点的下标 } 设置哨兵的好处: 在顺序表中总可以找到待查结点。否则,必须将 判断条件 i >= 0 加进 for 语句。 e.g: 查找 x = 8 的结点所在的数组元素的下标。 key 8 100 10 ……………… 0 7 1 3 向量 Vector 0 1 2 n-3 n-2 n-1 n

  11. i 顺序查找 • 应用范围:顺序表或线性链表表示的静态查找表。表内元素之间无序或有序。 template <class Type> int SeqSearch( const Vector<Type> & A, const Type key,int N ){ A[0] = key; // 将待查key 置于哨兵单元,可省掉越界检查。 for ( int k = N; A[k] != key; --k ) return k; // 返回0,查找失败。否则,找到关键字为key 的结点的下标 } 设置哨兵的好处: 在顺序表中总可以找到待查结点。否则,必须将 判断条件 i >= 0 加进 for 语句。 e.g: 查找 x = 8 的结点所在的数组元素的下标。 key 8 100 10 ……………… 0 7 1 3 向量 Vector 0 1 2 n-3 n-2 n-1 n

  12. i 顺序查找 • 应用范围:顺序表或线性链表表示的静态查找表。表内元素之间无序或有序。 template <class Type> int SeqSearch( const Vector<Type> & A, const Type key,int N ){ A[0] = key; // 将待查key 置于哨兵单元,可省掉越界检查。 for ( int k = N; A[k] != key; --k ) return k; // 返回0,查找失败。否则,找到关键字为key 的结点的下标 } 设置哨兵的好处: 在顺序表中总可以找到待查结点。否则,必须将 判断条件 i >= 0 加进 for 语句。 e.g: 查找 x = 8 的结点所在的数组元素的下标。 key 8 100 10 ……………… 0 7 1 3 向量 Vector 0 1 2 n-3 n-2 n-1 n

  13. i 顺序查找 • 应用范围:顺序表或线性链表表示的静态查找表。表内元素之间无序或有序。 template <class Type> int SeqSearch( const Vector<Type> & A, const Type key,int N ){ A[0] = key; // 将待查key 置于哨兵单元,可省掉越界检查。 for ( int k = N; A[k] != key; --k ) return k; // 返回0,查找失败。否则,找到关键字为key 的结点的下标 } 设置哨兵的好处: 在顺序表中总可以找到待查结点。否则,必须将 判断条件 i >= 0 加进 for 语句。 e.g: 查找 x = 7 的结点所在的数组元素的下标。 key 7 100 10 ……………… 0 7 1 3 向量 Vector 0 1 2 n-3 n-2 n-1 n

  14. i 顺序查找 • 应用范围:顺序表或线性链表表示的静态查找表。表内元素之间无序或有序。 template <class Type> int SeqSearch( const Vector<Type> & A, const Type key,int N ){ A[0] = key; // 将待查key 置于哨兵单元,可省掉越界检查。 for ( int k = N; A[k] != key; --k ) return k; // 返回0,查找失败。否则,找到关键字为key 的结点的下标 } 设置哨兵的好处: 在顺序表中总可以找到待查结点。否则,必须将 判断条件 i >= 0 加进 for 语句。 e.g: 查找 x = 7 的结点所在的数组元素的下标。 key 7 100 10 ……………… 0 7 1 3 向量 Vector 0 1 2 n-3 n-2 n-1 n

  15. i 顺序查找 • 应用范围:顺序表或线性链表表示的静态查找表。表内元素之间无序或有序。 template <class Type> int SeqSearch( const Vector<Type> & A, const Type key,int N ){ A[0] = key; // 将待查key 置于哨兵单元,可省掉越界检查。 for ( int k = N; A[k] != key; --k ) return k; // 返回0,查找失败。否则,找到关键字为key 的结点的下标 } 设置哨兵的好处: 在顺序表中总可以找到待查结点。否则,必须将 判断条件 i >= 0 加进 for 语句。 e.g: 查找 x = 7 的结点所在的数组元素的下标。 key 7 100 10 ……………… 0 7 1 3 向量 Vector 0 1 2 n-3 n-2 n-1 n

  16. 顺序查找的性能 • 设 n 为结点的总数。 • 平均查找长度AVL(Average Search Length ) • 成功查找情况下:设每个结点的查找概率相等 • 1 • ASL=∑(( n-i+1) ) • i=n • = (n+1)/ 2 1 n

  17. 顺序查找的性能 • 一般查找情况下(包括成功、不成功两种情况):设成功与不成功两种情况 • 可能性相等,每个结点的查找概率也相等。 • 1 • ASL=∑(( n-i+1) ) +∑((n+1) ) • = 3(n+1)/4 1 1 1 2(n+1) 2n i=n i=n+1 0 1 3 8 10 100 100 10 0 8 1 3 0 1 2 3 4 5 6 共有n+1=7种不成功的查找情况

  18. 折半查找(二分查找) • 应用范围:顺序表,表内元素之间有序。不可直接用于线性链表。 e.g: 查找 key = 9 的结点所在的数组元素的下标地址。 • 查找成功的情况:数组 Vector 如下图所示有序 • 数组 Vector :递增序 Vector[i]. Key <= Vector[I+1]. Key; i= 1,2,……n • 查找范围 :low(低下标)= 1; high(高下标)= 7 (初始时为最大下标 n ); • 比较对象:中点元素,其下标地址为 mid = (low+high)/ 2 =4 mid=4 但 key=9 < 10, 向左 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 high=7 low=1

  19. 折半查找 • 应用范围:顺序表,表内元素之间有序。不可直接用于线性链表。 e.g: 查找 key = 9 的结点所在的数组元素的下标地址。 • 查找成功的情况:数组 Vector 如下图所示有序 • 数组 Vector :递增序 Vector[i]. Key <= Vector[I+1]. Key; i= 1,2,……n • 查找范围 :low(低下标)= 1; high(高下标)= 3 mid=4 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 high=3 (mid -1) low=1

  20. 折半查找 • 应用范围:顺序表,表内元素之间有序。不可直接用于线性链表。 e.g: 查找 key = 9 的结点所在的数组元素的下标地址。 • 查找成功的情况:数组 Vector 如下图所示有序 • 数组 Vector :递增序 Vector[i]. Key <= Vector[I+1]. Key; i= 1,2,……n • 查找范围 :low(低下标)= 1; high(高下标)= 3 • 比较对象:中点元素,其下标地址为 mid = (low+high)/ 2 =2 mid=2 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 high=3 (mid -1) low=1

  21. 折半查找 • 应用范围:顺序表,表内元素之间有序。不可直接用于线性链表。 e.g: 查找 key = 9 的结点所在的数组元素的下标地址。 • 查找成功的情况:数组 Vector 如下图所示有序 • 数组 Vector :递增序 Vector[i]. Key <= Vector[I+1]. Key; i= 1,2,……n • 查找范围 :low(低下标)= 1; high(高下标)= 3 • 比较对象:中点元素,其下标地址为 mid = (low+high)/ 2 =2 mid=2;但 key=9 > 8, 向右 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 high=3 (mid -1) low=1

  22. 折半查找 • 应用范围:顺序表,表内元素之间有序。不可直接用于线性链表。 e.g: 查找 key = 9 的结点所在的数组元素的下标地址。 • 查找成功的情况:数组 Vector 如下图所示有序 • 数组 Vector :递增序 Vector[i]. Key <= Vector[I+1]. Key; i= 1,2,……n • 查找范围 :low(低下标)= 3; high(高下标)= 3 mid=2 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 high=3 (mid -1) low=3 (mid +1)

  23. 折半查找 • 应用范围:顺序表,表内元素之间有序。不可直接用于线性链表。 e.g: 查找 key = 9 的结点所在的数组元素的下标地址。 • 查找成功的情况:数组 Vector 如下图所示有序 • 数组 Vector :递增序 Vector[i]. Key <= Vector[I+1]. Key; i= 1,2,……n • 查找范围 :low(低下标)= 3; high(高下标)= 3 • 比较对象:中点元素,其下标地址为 mid = (low+high)/ 2 =3 mid=3 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 high=3 low=3

  24. 折半查找 • 应用范围:顺序表,表内元素之间有序。不可直接用于线性链表。 e.g: 查找 key = 9 的结点所在的数组元素的下标地址。 • 查找成功的情况:数组 Vector 如下图所示有序 • 数组 Vector :递增序 Vector[i]. Key <= Vector[I+1]. Key; i= 1,2,……n • 查找范围 :low(低下标)= 3; high(高下标)= 3 • 比较对象:中点元素,其下标地址为 mid = (low+high)/ 2 =3 mid=3; key=9 且中点值也为 9 ,找到 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 high=3 low=3

  25. 折半查找 e.g: 查找 key = 5 的结点所在的数组元素的下标地址。 • 查找不成功的情况:数组 Vector 如下图所示有序 • 数组 Vector :递增序 Vector[i]. Key <= Vector[I+1]. Key; i= 1,2,……n • 查找范围 :low(低下标)= 1; high(高下标)= 7 (初始时为最大下标 n ); • 比较对象:中点元素,其下标地址为 mid = (low+high)/ 2 mid=4 但 key=5 < 10, 向左 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 high=7 low=1

  26. 折半查找 e.g: 查找 key = 5 的结点所在的数组元素的下标地址。 • 查找不成功的情况:数组 Vector 如下图所示有序 • 数组 Vector :递增序 Vector[i]. Key <= Vector[I+1]. Key; i= 1,2,……n • 查找范围 :low(低下标)= 1; high(高下标)= 3 ; mid=4 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 low=1 high=3(mid-1)

  27. 折半查找 e.g: 查找 key = 5 的结点所在的数组元素的下标地址。 • 查找不成功的情况:数组 Vector 如下图所示有序 • 数组 Vector :递增序 Vector[i]. Key <= Vector[I+1]. Key; i= 1,2,……n • 查找范围 :low(低下标)= 1; high(高下标)= 3 ; • 比较对象:中点元素,其下标地址为 mid = (low+high)/ 2 =2 mid=2 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 low=1 high=3(mid-1)

  28. 折半查找 e.g: 查找 key = 5 的结点所在的数组元素的下标地址。 • 查找不成功的情况:数组 Vector 如下图所示有序 • 数组 Vector :递增序 Vector[i]. Key <= Vector[I+1]. Key; i= 1,2,……n • 查找范围 :low(低下标)= 1; high(高下标)= 3 ; • 比较对象:中点元素,其下标地址为 mid = (low+high)/ 2 =2 mid=2 ; 但 key=5 < 8, 向左 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 low=1 high=3(mid-1)

  29. 折半查找 e.g: 查找 key = 5 的结点所在的数组元素的下标地址。 • 查找不成功的情况:数组 Vector 如下图所示有序 • 数组 Vector :递增序 Vector[i]. Key <= Vector[I+1]. Key; i= 1,2,……n • 查找范围 :low(低下标)= 1; high(高下标)= 1 mid=2 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 low=1 high=1(mid-1)

  30. 折半查找 e.g: 查找 key = 5 的结点所在的数组元素的下标地址。 • 查找不成功的情况:数组 Vector 如下图所示有序 • 数组 Vector :递增序 Vector[i]. Key <= Vector[I+1]. Key; i= 1,2,……n • 查找范围 :low(低下标)= 1; high(高下标)= 1 • 比较对象:中点元素,其下标地址为 mid = (low+high)/ 2 = 1 mid=1 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 low=1 high=1

  31. 折半查找 e.g: 查找 key = 5 的结点所在的数组元素的下标地址。 • 查找不成功的情况:数组 Vector 如下图所示有序 • 数组 Vector :递增序 Vector[i]. Key <= Vector[I+1]. Key; i= 1,2,……n • 查找范围 :low(低下标)= 1; high(高下标)= 1 • 比较对象:中点元素,其下标地址为 mid = (low+high)/ 2 = 1 mid=1 ; 但 key=5 > 4, 向右 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 low=1 high=1

  32. 折半查找 e.g: 查找 key = 5 的结点所在的数组元素的下标地址。 • 查找不成功的情况:数组 Vector 如下图所示有序 • 数组 Vector :递增序 Vector[i]. Key <= Vector[I+1]. Key; i= 1,2,……n • 查找范围 :low(低下标)= 1; high(高下标)= 1 • 比较对象:中点元素,其下标地址为 mid = (low+high)/ 2 = 1 失败条件:low > high; 处于间隙中的键值导致这种情况! mid=1 ; 但 key=5 > 4, 向右 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 low=2 high=1

  33. 折半查找 template <class Type> int BinarySearch( const Vector<Type> & A, const Type key,int N ){ int low = 1, // 所查找的那一段的下界结点的下标。 high = N, // 所查找的那一段的上界结点的下标。 mid; // 所查找的那一段的中间结点的下标。 while(low <= high ) { mid = (low + high ) /2; if ( A[mid] = = key) return mid; else if (key < A[mid]) high = mid –1; else low = mid +1; } return 0; } // 返回0,查找失败.。否则,找到值为key 的结点的索引或下标。

  34. 折半查找的性能 注意:(n-1)/2 = n/2 最坏情况分析:设 key 和中点的二次比较的时间代价 1 如果 n = 1, 则 low = high = mid , 则代价为 1,记为 S(1)= 1 如果 n 是奇数,那么中点元素的左、右段各有 (n-1)/2个元素 如果 n 是偶数,中点元素的左段有 n/2-1 个元素;右段有 n/2个元素 因此,算法工作的那一段,最多有 n/2 项 ∴ S(n)= 1 + S( n/2 ) = 1 + 1 + S( n/22) = 1 + 1 + 1 + S( n/23) = 1 + 1 + 1 + ……… + 1 + S( n/2k) 注意:n/2 = n/2 总共 K 个 1 当 1 <= n/2k< 2 时;则 n/2k = 1 此时: 2k <= n < 2k+1 即 k <= log2n < k+1 注意:k 不可为小数,它是正整数。 ∴ k = log2n 故: S(n)= 1 + log2n

  35. mid= 4 4 8 9 10 11 13 19 20 0 1 2 3 4 5 6 7 8 high=8 low=1 mid= 4 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 high=7 low=1 折半查找的性能

  36. 折半查找的性能 最坏情况分析: 定理:在最坏情况下,二分查找法的查找有序表的最大的比较次数为 1+ log2n ,大体上和log2n 成正比。 也可用判定树的方法进行推导。 如: ? key = k4 4 < > 1 2 3 4 5 6 7 8 6 2 4 8 9 10 11 13 19 29 < > < 当寻找 key = 8 及小于、大 于 8 的键值的相应结点时, 查找次数最大。达到了判定 树的深度或高度。 1 3 5 7 < > < > < > < > 0 1 2 3 4 5 6 8 < > 注意:当判定树为 n = 2t -1 ( t=1,2,3 …… )时,为满二叉树。 否则,除最下一层外,余为满二叉树。 7 8

  37. 折半查找的性能 平均情况分析(在成功查找的情况下): 设每个 结点的查找概率相同都为 1/n。为了简单起见,设结点个数为 n = 2t -1 (t = 1,2,3 …… )。 ∴ 经过 1 次比较确定的结点个数为 1 = 20 个 ,红色标识的结点。 经过 2 次比较确定的结点个数为 2 = 21 个 ,绿色标识的结点。 经过 3 次比较确定的结点个数为 4 = 22 个 ,蓝色标识的结点。 . . . 经过 t 次比较确定的结点个数为 2t-1 个 ,黑色标识的结点。 注意:∵ 20 + 21 + 22 + … + 2t-1 = 2t -1 ∴ 最多经过 t 次比较可以找到有序表中的任何一个结点 e.g: 当 t = 4 时的例子:最多经过 t=4 次比较找到任何一个结点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 4 8 9 10 11 13 19 29 32 47 65 77 81 93 99

  38. 折半查找的性能 平均情况分析(在成功查找的情况下): ASL = ( 20×1+ 21×2+ 22×3+ … + 2t-1 ×t) / n t = ∑ (i × 2i-1 ) / n i=1 = [(n + 1) ×( log2(n + 1) - 1 ) + 1 ] / n = (n + 1) ×( log2(n + 1) / n - 1 = (n + 1) ×( log2(n + 1) / n - 1 结论:在成功查找的情况下,平均查找的代价约为 ASL = log2(n + 1) - 1 或者简单地记为:ASL = log2n - 1

  39. 4 < > 6 2 < > < 1 3 5 7 < > < > < > < > 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 4 8 9 10 11 13 19 折半查找的性能 平均情况分析(考虑成功、非成功查找两种的情况下):为了简单起见,设结点个数为 n = 2t -1 (t = 1,2,3 …… )。 这样,成功查找的情况共有 n 种情况,非成功查找的情况共有 n + 1 情况。 设每种情况出现的概率相同,即都为 1/(2n+1) 。 t ASL = ( ∑ (i × 2i-1 )+ t × ( n + 1 )) / (2n+1) i=1 = log2n + 1/2 e.g: 当 t = 3 时的例子: 成功:最多经过 t=3 次比较 失败:都必须经过 t = 3 次比较

  40. 差值查找 1、除中点下标的选择和二分查找不同外,其余类似。用于关键字值 均匀的情况,平均特性更好。 • 2、实现 • 设 mid 为中点的下标。low 为具有最小关键字值结点的下标, high 为具有最大关键字值结点的下标。 • (x-Element[low].key) • mid = low + (high-low-1)× • (Eelement[high].key-Element[low].key)

  41. = Fibonacci查找 1、Fibonacci 数 定义: F0 = 0 F1 = 1 Fk = Fk-1 + Fk-2 ( K >= 2) 如:0, 1, 1,2,3,5,8,13,21,34,55,89,144,233 • 2、实现 • 设结点的总数为 n = Fu - 1,查找键值为 key 的结点 • 首先比较 key ST[Fu-1].key • 如果 key < ST[Fu-1].key ,则比较 key = ST[Fu-1 -Fu-3].key • 如果 key > ST[Fu-1].key ,则比较 key = ST[Fu-1+Fu-3].key ? ? • 3、注意: • 参照下图,设根结点(或子树的根结点)同它的左、右儿子的下标之差为Fu-3 • 那么,根结点(或子树的根结点)的左儿子的同它的儿子的下标之差为Fu-4 • 根结点(或子树的根结点)的右儿子的同它的儿子的下标之差为Fu-5 • 可以设计类似于二分查找的算法。但先要把 Fu-3 、 Fu-4 、 Fu-5计算出来,它们也构成 • Fibonacci 数

  42. Fibonacci查找 如:0, 1, 1,2,3,5, 8,13,21,34,55,89,144,233 0, 1, 2,3,4, 5,6, 7, 8, 9, 10,11,12, 13, • 4、e.g: n = F7- 1 = 13 - 1 = 12个结点的查找过程 • Fu-1 = 8 • Fu-3 = 3 • Fu-4 = 2 • Fu-5 = 1 注意:Fu-2 = 5 ST[Fu-1].key 8 差为 Fu-3 = 3 > < 11 5 > 差为 Fu-5 = 1 差为 Fu-4 = 2 < > < 3 7 10 12 注意:本示意图从1开始编号,书上是从0开始进行编号。 < > < < 2 4 6 9 共 Fu-2-1 =5-1=4个结点 < 共 Fu-1-1 =8-1=7个结点 1 5、优点:只用 +、-法,不用除法。平均查找速度更快。O(log2n)级。 缺点:最坏情况下比二分查找法差。必须先给出 Fibonacci 数。

  43. 二叉排序树 • 特点:用于频繁进行插入、删除、查找的所谓动态查找表。 • 二叉排序树(二叉查找树):空或有一个根,根的左子树若非空,则左子树上的所有结点的关键 • 字值均小于根结点的值。根的右子树若非空,则右子树上的所有结点的关键字值 • 均大于根结点的值。根结点的左右子树同样是二叉排序树。 e、g:二叉排序树(二叉查找树),确定结点的大小,可根据结点类型进行定义。 L 122 N 250 C 99 P 300 M 200 E 110 Y 105 230 216

  44. 二叉排序树(ADT) template <class Type> class BST { // 二叉排序树的ADT public: BST ( ) { } // 二叉排序树的构造函数。 ~BST ( ) { } // 二叉排序树的析构函数。 virtual int Insert( const Type & x) = 0; // 插入x。 virtual int Remove( const Type & x ) = 0; // 删除x。 virtual const Type & Find(const Type & x) = 0;// 查找值为x 的结点。 virtual int IsFound( const Type & x )= 0;// 若x 找到,则返回1。 virtual const Type & FindMin( ) = 0; // 返回最小值。 virtual const Type & FindMax( ) = 0; //返回最大值。 virtual int IsEmpty( ) const = 0;// 二叉排序树为空,返回1,否则为0。 virtual int IsFull( )const = 0;// 二叉排序树为满,返回1,否则为0。 virtual void MakeEmpty( ) = 0; //清除二叉排序树中的所有结点。 };

  45. 二叉排序树的结点类 • 二叉排序树中的结点类的实现,为了表示简单,没有采用继承的方式,采用结构。 • 二叉排序树BST的结点表示。 template <class Type> struct BSTNode { // 二叉排序树的结点的表示。 Type data; // 结点的数据场。 BSTNode * left; // 给出结点的左儿子的地址。 BSTNode * right; // 给出结点的右儿子的地址。 int BalanceFactor; // 结点的平衡度,用于AVL 树。 int Size; // 以本结点为根的子树的所有结点的个数,用于顺序统计。 BSTNode ( ): left(NULL), right(NULL), Size(1), BalanceFactor(1) { } BSTNode ( const Type x ) : data(x), left(NULL), right(NULL), Size(1), BalanceFactor(1) { } BSTNode (const Type x, BSTNode * L, BSTNode * R ): data(x), left(L), right(R), Size(1), BalanceFactor(1) { } ~BSTNode( ) { } };

  46. 二叉排序树类 template <class Type> class BinarySearchTree:public BST<Type> { public: BinarySearchTree ( ) : LastFind(NULL), Root(NULL) { } ~BinarySearchTree ( ) { FreeTree(Root); } int Insert( const Type & x) { return Insert(x,Root);} //插入x 到以Root 为根的二叉排序树。成功则返回1。 const Type & Find( const Type & x ) { return ( LastFind = Find(x,Root)) ? LastFind->data:ItemNotFound } // 若查找x 成功,则返回二叉排序树中的相应数据项。否则,返回ItemNotFound。 const Type & FindMin( )const { const BSTNode * p = FindMin(Root); return p ? p->data:ItemNotFound; } //返回二叉排序树中的最小的数据项。若树为空,返回ItemNotFound。 const Type & FindMax( ) const { const BSTNode * p = FindMax(Root); return p ? p->data:ItemNotFound; } //返回二叉排序树中的最大的数据项。若树为空,返回ItemNotFound。

  47. 二叉排序树类 int IsFound (const Type & x) { return Find(x,Root)!= NULL;} //若x找到,则返回True。 int WasFound ( ) const { return LastFind != NULL;}//最近一次查找成功,则返回True。 int Remove( const Type & x ) { return Remove(x, Root);} // 从二叉排序树中删除值为x 的结点,删除成功返回True。 int RemoveMin( ) { return RemoveMin(Root);} // 从二叉排序树中删除最小结点,删除成功返回True。 int IsEmpty( ) const { return Root == NULL; } //二叉排序树为空,返回True,否则为False。 void MakeEmpty ( ){ FreeTree(Root); Root = NULL;} //清除二叉排序树中的所有结点。 protected: BSTNode<Type> * Root; constBSTNode<Type> * LastFind; Type ItemNotFound; // 用于查找失败时返回。 constBSTNode<Type> * Find ( const Type & x,constBSTNode<Type> * T) const; constBSTNode<Type> * FindMin ( constBSTNode<Type> * T) const; constBSTNode<Type> * FindMax ( constBSTNode<Type> * T) const; int Insert( const Type & x,BSTNode<Type> * & T); int RemoveMin(BSTNode<Type> * & T); int Remove( const Type & x,BSTNode<Type> * & T); };

  48. 二叉排序树的查找 • 分割式查找法: • 查找步骤:若根结点的关键字值等于查找的关键字,成功。 • 否则,若小于根结点的关键字值,查其左子树。大于根结点的关键 字值,查其右子树。在左右子树上的操作类似。 122 template <class Type> BSTNode<Type > * BinarySearchTree<Type>:: Find(const Type & x, const BSTNode<Type > * T) const { while ( T != NULL ) if ( X < T->data ) T = T->left else if ( X > T->data ) T = T->right; else return T; return NULL; } // 查找失败,返回空。查找成功,返回指向相应结点的指针。 250 99 200 300 110 105 230 216

  49. 二叉排序树的查找分析 • 平均情况分析(在成功查找两种的情况下) • e.g: 下述两种情况下的成功的平均查找长度 ASL 15 50 20 20 60 30 15 30 70 ASL=(1+2+2+3+3+3)/6=14/6 50 60 70 AVL=(1+2+3+4+5+6)/6=21/6

  50. 二叉排序树的查找分析 • 平均情况分析(在成功查找两种的情况下) • 在一般情况下,设 P(n,i)且它的左子树的结点个数为 i 时的平均查找长 • 度。右图的结点个数为 n = 6 且 i = 3; 则 • P(n,i)= P(6, 3) = [ 1+ ( P(3) + 1) * 3 + ( P(2) + 1) * 2 ] / 6 • = [ 1+ ( 5/3 + 1) * 3 + ( 3/2 + 1) * 2 ] / 6 • 注意:这里 P(3)、P(2) 是具有 3 个结点、2 个结点的二叉排序树的平均查找 • 长度。 在一般情况下,P(i)为具有 i 个结点二叉排序树的平均查找 • 长度。 • P(3) = (1+2+2)/ 3 = 5/3 • P(2) = (1+2)/ 2 = 3/2 • ∴ P(n,i)= [ 1+ ( P(i) + 1) * i + ( P(n-i-1) + 1) * (n-i-1) ] / n • ∴ n-1 • P(n)= ∑P(n,i)/ n • i=0 • <= 2(1+I/n)lnn • 因为: 2(1+I/n)lnn≈1.38logn • 故:P(n)=O(logn) 50 20 60 15 30 70 左子树0到n-1个结点 右子树n-1到0个结点

More Related