时间、空间复杂度比较[td]
算法思路: 对于任意一个序列,从一端开始,顺序扫描,依次将扫描到的结点关键字与给定值k相比较,若相等则表示查找成功;若扫描结束仍没有找到关键字等于k的结点,表示查找失败。 代码: #include <stdio.h>/ r( C! F+ M* y; D5 j q4 ~0 {) x. j#include <stdlib.h>& E, m" O4 X. T4 w [ #define keyType int! p! E' b0 d3 @9 k* A //2020.05.24* s. y$ R& l6 P6 @% B typedef struct { keyType key;//查找表中每个数据元素的值( P, P; n+ z6 D A }ElemType; ) ]) B k4 C: w3 }5 E( R2 q. i7 t typedef struct" m1 o) o/ ?" F# V8 t { ElemType *elem;//存放查找表中数据元素的数组 int length;//记录查找表中数据的总数量/ K! i# p0 a8 @) w2 Y4 D }SSTable;/ b! H) o: G0 ] _/ F //创建查询数据$ j/ @2 o6 ?7 f2 @' v void Create(SSTable **st,int length)+ J' j7 V: G& D6 j/ r' x { (*st)=(SSTable*)malloc(sizeof(SSTable)); (*st)->length=length; (*st)->elem =(ElemType*)malloc((length+1)*sizeof(ElemType));: ^% j# ~- }0 u; S' f printf("输入表中的数据元素:\n"); //根据查找表中数据元素的总长度,在存储时,从数组下标为 1 的空间开始存储数据; x+ O$ k% H- q for (int i=1; i<=length; i++) { scanf("%d",&((*st)->elem.key)); }1 U5 i' Z& C2 L' h1 J+ a } //顺序查找函数,其中key为要查找的元素% a- M' O6 ]9 V int Search_seq(SSTable *str,keyType key)8 j# {! m2 a* V, d3 X { //str->elem[0].key=key;//将关键字作为一个数据元素存放到查找表的第一个位置,起监视哨的作用 int len = str->length; //从最后一个数据元素依次遍历,一直遍历到数组下标为0 for(int i=1; i<len+1; i++) //创建数据从数组下标为1开始,查询也从1开始 { if(str->elem.key == key)6 Y6 A* g. K' j+ B {( x5 l5 }' g; ?" s. ]$ @ return i;. Q! v# n' m* H8 e } } //如果 i=0,说明查找失败;查找成功返回要查找元素key的位置i0 }: r+ ?+ C2 t$ U return 0; } & Y/ m/ w! i# P+ K* I9 c, u int main()1 {/ ?4 C1 L, Q: }1 [ { SSTable *str;! [: _( A6 K- h int num; printf("请输入创建数据元素的个数:\n"); scanf("%d",&num); Create(&str, num); getchar();* b! l3 z7 \" c" A g8 A printf("请输入要查找的数据:\n"); int key;6 W* V( V/ w$ R9 |3 F9 U1 {3 N' L' P4 | scanf("%d",&key);7 q5 D- u% W/ _) ~# l' l int location=Search_seq(str, key);& T- J2 a1 U2 Q6 k. P& b% T if (location==0) { printf("查找失败"); }else{ printf("要查找的%d的顺序为:%d",key,location);% V% z9 J: O: E/ F1 B# U3 w( U } return 0; }: F( J) c; T8 J5 q 运行结果:
2 二分查找(折半查找)" ~( K6 I" c1 i7 W: m& U& B. g 算法思路:
说明:
代码: #include <stdio.h>#include <stdlib.h>( X* l' {: H; A; ` #define keyType int6 }3 r; x$ c% K typedef struct6 j5 ^2 n' u: X$ X/ r4 h { keyType key;//查找表中每个数据元素的值, N7 G& k# M2 ^: ]' }2 } }ElemType;2 @% r0 l4 ?5 O y6 p ) P3 d1 \& l: Y3 v typedef struct9 h, F4 `' O( O2 E: w6 y1 K, G$ b {; k; `6 O: O! @! m# O% Q: A ElemType *elem;//存放查找表中数据元素的数组3 _! {4 c, Y4 [$ o3 w" S int length;//记录查找表中数据的总数量1 |$ L* R2 ~) M; d7 L3 p3 ]; {7 k }SSTable;* x6 k0 R) e3 |, B R //创建查询数据0 k3 B# G+ R* {" L7 {" [% ], Q; Y5 h/ h void Create(SSTable **st,int length)7 F) ]+ J; [+ Q. }2 e8 X( _ { (*st)=(SSTable*)malloc(sizeof(SSTable));( Z) C7 y; |) r0 L, |! c8 q (*st)->length=length;0 P5 F2 }: @0 U6 J; L' Y (*st)->elem =(ElemType*)malloc((length+1)*sizeof(ElemType)); printf("输入表中的数据元素:\n");# @" O* V/ V1 E //根据查找表中数据元素的总长度,在存储时,从数组下标为 1 的空间开始存储数据6 T* k8 ?+ S- u S3 e2 B for (int i=1; i<=length; i++) {9 H' w5 O" K/ F( h( F, ]% m scanf("%d",&((*st)->elem.key));' ^5 C3 f! I. u } } 0 k0 W4 C, g; v; P3 ^! ~ //折半查找函数 key为要查找的元素% U# s5 y* A7 Y2 q int Search_Bin(SSTable *str,keyType key)5 }. I7 h8 I3 u- m {4 v) H, Q% U" d' s2 f! R4 s int low=1;//初始状态 low 指针指向第一个关键字 int high=str->length;//high 指向最后一个关键字! J: n3 ^6 K% p+ r- I int mid; while (low<=high) {% w _7 e# b2 h: C mid=(low+high)/2;//int 本身为整形,所以,mid 每次为取整的整数 if(str->elem[mid].key==key)//如果 mid 指向的同要查找的相等,返回 mid 所指向的位置* K+ T1 t- D: @1 Z6 a {( @, P; D4 b C return mid;% g1 I6 L, ~& Z* U1 b4 S, J. D } else if(str->elem[mid].key>key)//如果mid指向的关键字较大,则更新 high 指针的位置$ }: {; b, n1 P1 H. ~ {% R: T9 u0 Q$ q1 ]/ t high=mid-1; } //反之,则更新 low 指针的位置# J$ ~+ ~1 a1 S8 D0 g$ _ else. Y# ?+ f8 @$ u; d# n3 H { low=mid+1; } }. k9 P! A6 Z* i( F8 X2 Z, J. e4 J return 0;% D$ ^: s5 q- `3 d } / `: G, a7 L) r; ~/ W$ y int main()3 v! y: R, U8 Z {/ B' W4 ` k3 s/ v. u SSTable *str; int num; printf("请输入创建数据元素的个数:\n");* B6 e* m7 _" L5 _* f scanf("%d",&num);4 Y( c( Y: m: o9 o! {( k Create(&str, num);/ H1 ~- c. p1 ?. }/ b getchar();. ~' ]8 r7 c! D2 x7 i printf("请输入要查找的数据:\n");1 \) c0 C, Y9 J; G. z5 N8 v4 e int key; scanf("%d",&key); int location=Search_Bin(str, key);( Z6 q- Y9 j/ l2 G9 k if (location==0) { printf("没有查找到");. z( ^3 v( m: R+ Q8 w2 s- J }else{8 z) e: ?* v/ P5 x* Q \( v, q printf("要查找的%d的顺序为:%d",key,location); }: R6 x* {1 }) U return 0;- K' ~8 N8 `( v! Q } 运行结果:
3 插值查找 插值查找基于二分查找算法,将查找点的选择改进为自适应选择,可以提高查找效率。当然,差值查找也属于有序查找 算法思路:
说明:
代码: #include<stdio.h>" w& n- E5 v2 t; b: {+ A+ O# _9 Q+ Q7 ?: a! b8 s( a int array[10] = { 1, 4, 9, 16, 27, 31, 33, 35, 45, 64 };' f" {2 S, s3 h3 K7 B0 l 2 Q* b3 y6 ~1 X int InsertionSearch(int data)! X e, ~' I6 |9 ? {% ? |7 p9 M, e+ H+ N1 q int low = 0; int high = 10 - 1;0 `. G& G/ _9 v( {" C int mid = -1; int comparisons = 1; int index = -1;/ e8 v) c" @+ Q$ u . A7 G5 y: N% F8 v/ A while(low <= high)0 p! u0 j1 d7 F0 f' p {, G/ [- y2 d% o5 i/ @* F printf("比较 %d 次\n" , comparisons );1 G& I. }6 ]/ B0 | printf("low : %d, list[%d] = %d\n", low, low, array[low]); \- k2 x" t' i; R' A3 E Q printf("high : %d, list[%d] = %d\n", high, high, array[high]); 4 O! o- X- u" i9 P comparisons++;; N& u5 O: U6 \. I/ Z9 c mid = low + (((double)(high - low) / (array[high] - array[low])) * (data - array[low])); printf("mid = %d\n",mid);: R/ x. [3 x" d 2 n! ~5 w; s0 R, m" T! g1 |( E // 没有找到 if(array[mid] == data) { index = mid; break; }0 ^# H2 F& f$ [% A6 q else { if(array[mid] < data) {& v3 C; D' Y# N: N0 _) e! Z, j [ _ low = mid + 1;% {3 E; G2 }1 a( v) I( P+ G6 T( t } else { high = mid - 1; }3 y8 a- S) k; ?/ w, k' w! k; ` }/ U8 h e4 y/ d* w: o }: y3 V0 r; k0 u O2 I printf("比较次数: %d\n", --comparisons);, U, q% o; i9 ` return index; }& H4 I T3 ^; t9 h/ b9 y , j4 R& X' ~- G* B6 i int main()) u- B1 P8 U" d- C0 x" h6 q ? {6 ~- a8 P$ `+ k1 { int location = InsertionSearch(27); //测试代,查27,可以找到 if(location != -1) {: D! k# F$ b w5 L% X# X* [8 L printf("查找元素顺序为: %d\n" ,(location+1)); p" z* `1 {* T; S8 ^2 l } else+ Z( G! |0 i; l0 j, x' w {6 d- d E3 J" _+ D! [6 y printf("没有查找到"); } return 0;2 R# y* Q2 U1 q$ _ t! j. I0 T/ r3 t. ^ }9 k4 j3 K& Z- a 运行结果:
4 斐波那契查找8 v1 C, Z# M Y: ~5 r: \; j, @2 u 斐波那契查找与折半查找很相似,他是根据斐波那契序列的特点对有序表进行分割的。他要求开始表中记录的个数为某个斐波那契数小1,及n=F(k)-1;开始将k值与第F(k-1)位置的记录进行比较(及mid=low+F(k-1)-1). 算法思路:
说明: low=mid+1说明待查找的元素在[mid+1,high]范围内,k-=2 说明范围[mid+1,high]内的元素个数为n-(F(k-1))= Fk-1-F(k-1)=Fk-F(k-1)-1=F(k-2)-1个,所以可以递归的应用斐波那契查找。 代码: #include "stdafx.h"#include <memory>0 d* \, x" |; \+ |1 O #include <iostream> using namespace std; 3 J# j( g) r8 ]0 f9 P const int max_size=20;//斐波那契数组的长度 ' N5 T7 }7 l( O# N/ _ /*构造一个斐波那契数组*// p M. d5 n C% D2 u void Fibonacci(int * F) { F[0]=0; F[1]=1;! O& T9 D9 Z& {, \( q& b: h* `- v for(int i=2;i<max_size;++i), z( j2 D- l; X F=F[i-1]+F[i-2]; }6 A! d3 N8 B" d6 I /*定义斐波那契查找法*/8 R: B z9 x# T int FibonacciSearch(int *a, int n, int key) //a为要查找的数组,n为要查找的数组长度,key为要查找的关键字' O1 U8 V$ x* _# j5 i" H* ^. R {1 B4 X+ O4 ^# }/ R! v+ y int low=0;* h# p6 c' J2 {7 F! \, G" a, a int high=n-1; int F[max_size]; Fibonacci(F);//构造一个斐波那契数组F4 ?# z' e6 D: a6 M& S/ F int k=0;5 n* _4 U B. U* Q+ f$ ^ while(n>F[k]-1)//计算n位于斐波那契数列的位置0 z( v4 M9 E! o5 B, G# f6 A ++k;( c; }# V2 U# |- \7 K5 i int * temp;//将数组a扩展到F[k]-1的长度 temp=new int [F[k]-1];. \% W s! {' |+ W# O: q memcpy(temp,a,n*sizeof(int));4 k9 `( t* \9 X! j# A for(int i=n;i<F[k]-1;++i)7 u- q1 `. C. f4 `7 @) b8 a temp=a[n-1];3 H" j' c1 u' c ! }0 d4 Q: p2 x1 S& {' Y while(low<=high) { int mid=low+F[k-1]-1; if(key<temp[mid])6 Y, Z. O& G" n0 S7 P5 ?9 ?6 p, S { high=mid-1;6 S4 h! P+ U" a; e k-=1;6 j$ D# H* m1 Q4 I2 Y5 v } else if(key>temp[mid]) { low=mid+1;# _7 w; x. F$ k/ T* U k-=2;. q4 s U: E0 C3 K* Z } else8 @. a& n' |- s+ C, ]6 z* q+ ~6 K6 R {6 W9 i' V4 w }( t8 V; ^" s/ l$ ~ if(mid<n) return mid; //若相等则说明mid即为查找到的位置. }, A2 ~& H8 d; r v- z' S else" P( x) y) X$ k. |" F; V& W* D H return n-1; //若mid>=n则说明是扩展的数值,返回n-1 } }5 D+ |/ A! d' z) X delete [] temp;" @' J9 ^! g/ A, K2 N8 m; ` return 0;! ?4 o# |* c" T, o }2 J! |; W8 J) e/ [ 3 l/ Y3 L- t; w5 C) ~ int main()( E, q0 c% Q$ m {/ ^, L5 C+ |; X# U& q' N int a[] = {0,1,4,35,47,53,62,78,88,99}; int key=47; int index=FibonacciSearch(a,sizeof(a)/sizeof(int),key);& w Q) }) ?/ r/ e% \2 _# ?5 w if(index == 0)! _! J2 H# r0 }6 j, w {, \- B+ u, O z s+ Z0 ] cout<<"没有找到"<<key; p4 w( z S- D, \+ F* L } else- t! [; K. d( K1 |/ ? { cout<<key<<" 的位置是:"<<index;' C7 [* U& W- n1 ~6 w3 W% n1 ~! U; s }. h8 w! j; m- {+ T' w& E* Z8 U9 k return 0; B* N) l! m, E' g! Y } 运行结果:
5 哈希查找9 V) I& U0 i) X8 u& S1 S 哈希表: 我们使用一个下标范围比较大的数组来存储元素。可以设计一个函数(哈希函数, 也叫做散列函数),使得每个元素的关键字都与一个函数值(即数组下标)相对应,于是用这个数组单元来存储这个元素;也可以简单的理解为,按照关键字为每一个元素"分类",然后将这个元素存储在相应"类"所对应的地方。但是,不能够保证每个元素的关键字与函数值是一一对应的,因此极有可能出现对于不同的元素,却计算出了相同的函数值,这样就产生了"冲突",换句话说,就是把不同的元素分在了相同的"类"之中。后面我们将看到一种解决"冲突"的简便做法。 "直接定址"与"解决冲突"是哈希表的两大特点。 哈希函数: 规则:通过某种转换关系,使关键字适度的分散到指定大小的的顺序结构中,越分散,则以后查找的时间复杂度越小,空间复杂度越高。 算法思路: 如果所有的键都是整数,那么就可以使用一个简单的无序数组来实现:将键作为索引,值即为其对应的值,这样就可以快速访问任意键的值。这是对于简单的键的情况,我们将其扩展到可以处理更加复杂的类型的键。
代码: #include<stdio.h>#include<stdlib.h> & d! w$ p: i8 p" }0 a. Z% e #define SUCCESS 1" [7 N3 U4 k+ d5 Q, J- [ ]/ D+ r9 B #define UNSUCCESS 02 I6 x8 ^6 c% }4 y' [2 @ #define OVERFLOW -1 \/ F7 X: v1 U1 G% q7 u #define OK 1# G9 }% @3 H( g6 Y8 h' o #define ERROR -1 #define MAXNUM 9999 // 用于初始化哈希表的记录 key: C! y' P( |4 Y. c5 R! u: o3 a ! ]" t9 h; B8 D8 E" ?, e7 K3 n7 o" v- G typedef int Status;+ [; k$ J) d. a8 j1 T typedef int KeyType;1 u4 G: y+ x) I; y" L // 哈希表中的记录类型 typedef struct { KeyType key; }RcdType;$ i1 b& @/ K3 G' Q& R: F% H // 哈希表类型9 r9 |- M- I9 _ N, U% b/ n& g3 q typedef struct {8 f, R0 q8 R7 f; U7 W& P5 B* ^ RcdType *rcd; int size;" {4 w0 p E' O% F: \ int count; int *tag;: L& D# I% w5 W }HashTable;% s, x# p# ?" W( z, |- k$ @1 D3 U- V 8 q* ?) T* |' S. _% X: }6 R, w& ~ // 哈希表每次重建增长后的大小 int hashsize[] = { 11, 31, 61, 127, 251, 503 };3 E9 T" |) m6 q ^ int index = 0;! W% ]/ j/ V! W. D0 B6 z/ I ' Y" E+ G: z6 R$ h) \7 e // 初始哈希表 Status InitHashTable(HashTable &H, int size)2 y# f4 b) J3 q8 b! A- `8 p { int i;6 ?: f! l9 j! d H.rcd = (RcdType *)malloc(sizeof(RcdType)*size);. `4 F5 T! a/ e! W& I8 d! @; J H.tag = (int *)malloc(sizeof(int)*size); if (NULL == H.rcd || NULL == H.tag) return OVERFLOW; KeyType maxNum = MAXNUM; for (i = 0; i < size; i++)7 Q' x: c7 c+ r; G0 f {3 ~" S1 P" A" g' j5 K% l$ q H.tag = 0;* _' N2 ^* {' o8 } H.rcd.key = maxNum;1 o# [1 e: W. C! [ } H.size = size; H.count = 0; return OK; } // 哈希函数:除留余数法 int Hash(KeyType key, int m) { return (3 * key) % m; } // 处理哈希冲突:线性探测* `7 N) t7 c' M8 P! }9 s void collision(int &p, int m) { p = (p + 1) % m;; U& v# \" d% |, B" ^ }% E- G% i7 C9 o2 o; y M% f5 ] B // 在哈希表中查询 Status SearchHash(HashTable H, KeyType key, int &p, int &c) { p = Hash(key, H.size);2 d5 h5 i3 w [) y0 ]4 i int h = p; c = 0; while ((1 == H.tag[p] && H.rcd[p].key != key) || -1 == H.tag[p]) { collision(p, H.size); c++;8 x& Z* k- q- {+ g4 }; w } $ B) g0 U( t( {' [7 G if (1 == H.tag[p] && key == H.rcd[p].key) return SUCCESS; else return UNSUCCESS; } 5 n1 s" m! P: ~; A% o# T7 r //打印哈希表 void printHash(HashTable H) { int i; printf("key : ");8 I) t* P; A* H for (i = 0; i < H.size; i++)" E( o) N) M9 k1 d: d0 N7 r printf("%3d ", H.rcd.key); printf("\n"); printf("tag : ");8 W$ D% a' p- D$ c. v for (i = 0; i < H.size; i++) printf("%3d ", H.tag); printf("\n\n"); } // 函数声明:插入哈希表 Status InsertHash(HashTable &H, KeyType key);' O: z, s' K0 V! d- t4 F: } ) w" r) H, g! F8 Q" H3 ^ // 重建哈希表 Status recreateHash(HashTable &H) { RcdType *orcd;3 j* }% O, x. ~% g* Q1 V: t7 \+ \ int *otag, osize, i; orcd = H.rcd; otag = H.tag; osize = H.size;. V+ [' V/ {0 l8 Y' J+ n8 Z ' Z" j. Y7 `6 R# A, o" z" T InitHashTable(H, hashsize[index++]); //把所有元素,按照新哈希函数放到新表中 for (i = 0; i < osize; i++) { if (1 == otag) {9 B" j% { k3 c InsertHash(H, orcd.key); }0 N) }. R1 t' r }. p5 K% {' B3 {! P# ^5 T7 F9 F return OK; } , M: J- j2 i* S$ A/ e: h+ @/ H // 插入哈希表 Status InsertHash(HashTable &H, KeyType key) { int p, c;) [$ d2 d7 C0 Z7 W2 | if (UNSUCCESS == SearchHash(H, key, p, c)) { //没有相同key if (c*1.0 / H.size < 0.5) { //冲突次数未达到上线) o7 C* H& p: x //插入代码! h: _$ i1 k: u H.rcd[p].key = key; H.tag[p] = 1;% {5 J! j. H/ |$ V" w6 K, Y4 V* Y H.count++;( R; m6 d2 o! y0 ^. E5 R return SUCCESS; } else recreateHash(H); //重构哈希表. T' W# L$ }6 y8 q' Q( J: c( t0 d } return UNSUCCESS; } : A/ ^, p7 a/ E // 删除哈希表3 \8 r8 X. M3 G Status DeleteHash(HashTable &H, KeyType key)' X8 `( U2 v0 _ { int p, c; if (SUCCESS == SearchHash(H, key, p, c))+ o! S& s' \* ?( `2 T { //删除代码 H.tag[p] = -1;3 \2 T) P# t, W4 d% ?4 z! f! Q0 i H.count--;4 J: t/ b* u4 |) l return SUCCESS;- b* e3 O5 @; i- C4 ]6 U q( g4 l } else return UNSUCCESS;/ a8 y: K3 Y# l6 N } int main() { printf("-----哈希表-----\n"); HashTable H; int i;$ t! @9 J! z. o int size = 11;, e1 |" W5 `& E: d2 S" }7 c KeyType array[8] = { 22, 41, 53, 46, 30, 13, 12, 67 }; KeyType key; 2 S9 e% F% r( B: e //初始化哈希表 printf("初始化哈希表\n");" u i! `7 i% N/ u/ v if (SUCCESS == InitHashTable(H, hashsize[index++])) printf("初始化成功\n"); //插入哈希表4 N8 B, T; Y: i printf("插入哈希表\n");' R# C8 [+ ?1 i1 A for (i = 0; i <= 7; i++)& H3 m5 X( o$ u' G, p {. n; r- f4 L9 c) u$ J, b# ~ key = array;3 J" f) g8 G. a! I6 A: v+ L6 F InsertHash(H, key); printHash(H);# S+ O6 I0 s/ U/ N }+ x) s0 t7 b; @- J% H5 x) [0 q; m 8 j$ a" V1 @0 M2 U d( E5 T3 b0 T //删除哈希表" P. a3 m6 b% N( d! l/ f printf("删除哈希表中key为12的元素\n");) \- I5 X/ J' i: \7 F- T+ A% k int p, c; if (SUCCESS == DeleteHash(H, 12)) { printf("删除成功,此时哈希表为:\n");/ F' R( |4 @) J9 h+ s! | printHash(H);- P* V0 a x" s, N }5 k; D6 k9 P( [# ? //查询哈希表 printf("查询哈希表中key为67的元素\n"); if (SUCCESS == SearchHash(H, 67, p, c)) printf("查询成功\n");8 L' r3 j1 W2 `4 u5 p" y : Z G2 C% v/ e8 L //再次插入,测试哈希表的重建 printf("再次插入,测试哈希表的重建:\n");$ z$ C. e' l8 ]1 Y- M( X9 g KeyType array1[8] = { 27, 47, 57, 47, 37, 17, 93, 67 }; for (i = 0; i <= 7; i++)! d. L( P) O" r0 G, V" z6 U* H { key = array1; InsertHash(H, key);, |- A! b% B* B( O6 a! A; M/ m0 t printHash(H);) g' u) v) N( I' a; G D } getchar();/ |# J i" ?% l0 k) h$ {/ [ return 0; }8 K7 a/ t' X l `3 K& q 6 二叉树查找 二叉查找树是先对待查找的数据进行生成树,确保树的左分支的值小于右分支的值,然后在就行和每个节点的父节点比较大小,查找最适合的范围。这个算法的查找效率很高,但是如果使用这种查找方法要首先创建树。 算法思路:
代码: 递归和非递归 //非递归查找算法BSTNode *BST_Search(BiTree T,ElemType key,BSTNode *&p) { //查找函数返回指向关键字值为key的结点指针,不存在则返回NULL p=NULL;5 Y% u% K' X2 d$ Z+ ^2 U while(T!=NULL&&key!=T->data) {8 {6 i5 C, |* ^ ?' v p=T; //指向被查找结点的双亲 if(key<T->data)2 h+ a0 M. [/ A- ]5 Z T=T->lchild; //查找左子树 else' N" a6 ]3 s$ F& z: H+ R# O T=T->rchild; //查找右子树 } return T; }1 w$ V2 t F" m% S8 }8 X9 L / n ?( ?& f6 @% X% D% s+ e& e. A //递归算法 Status Search_BST(BiTree T, int key, BiTree f, BiTree *p) { //查找BST中是否存在key,f指向T双亲,其初始值为NULL //若查找成功,指针p指向数据元素结点,返回true; //若失败,p指向查找路径上访问的最后一个结点并返回false" |$ c1 ~$ z: c7 A# I5 L4 Z2 V( s if(!T)+ i+ @4 m0 ?! h( S1 B, j% `2 b { *p=f;* n% J1 K5 M& l6 ~ return false; }1 D$ J' }1 c4 G$ n" j else if(key==T->data) { //查找成功 *p=T;3 S3 H. C% W# W8 l7 i return true; } else if(key<T->data)/ l6 T- o6 f, p6 P2 a3 R2 s return Search_BST(T->lchild,key,T,p); //递归查找左子树6 i" m( h J8 G else return Search_BST(T->rchild,key,T,p); //递归查找右子树 $ B' P9 ]/ \- v$ y9 k, ? }6 K" @1 ] Z6 l. J 1 C' T2 a) U/ K) _* p, O! f 7 2-3树 2-3树运行每个节点保存1个或者两个的值。对于普通的2节点(2-node),他保存1个key和左右两个自己点。对应3节点(3-node),保存两个Key,2-3查找树的定义如下:
算法思路: 要判断一个键是否在树中,我们先将它和根结点中的键比较。如果它和其中的任何一个相等,查找命中。否则我们就根据比较的结果找到指向相应区间的链接,并在其指向的子树中递归地继续查找。如果这是个空链接,查找未命中。
代码: two_three *search23(two_three *t, element x)! @1 [/ q. A. A6 \" l4 A1 r{ while(t) { if (x < t->data_l)" x7 M1 R% Y7 C( e$ S/ g( Q+ d {$ a" ?) h: a3 o" w t = t->left_child; }# B7 X% m9 x+ G- ^: `* A else if (x > t->data_l && x < t->data_r)2 t z) G0 [) j; j; y6 P { t = t->middle_child; } else if (x > t->data_r) { t = t->right_child;+ ~; }2 A8 [4 A }5 D" d0 M; w- G7 u; W( `( | else# M% W M2 j0 X7 l% L {( a! x; [8 h H' K" s$ j K return t; } } return NULL; } 8 红黑树 2-3查找树能保证在插入元素之后能保持树的平衡状态,最坏情况下即所有的子节点都是2-node,树的高度为lgn,从而保证了最坏情况下的时间复杂度。但是2-3树实现起来比较复杂,于是就有了一种简单实现2-3树的数据结构,即红黑树(Red-Black Tree)。 理解红黑树一句话就够了:红黑树就是用红链接表示3-结点的2-3树。 现在我们对2-3树进行改造,改造成一个二叉树。怎么改造呢?对于2节点,保持不变;对于3节点,我们首先将3节点中左侧的元素标记为红色,然后我们将其改造成图3的形式; 再将3节点的位于中间的子节点的父节点设置为父节点中那个红色的节点,如图4的所示;最后我们将图4的形式改为二叉树的样子,如图5所示。图5是不是很熟悉,没错,这就是我们常常提到的大名鼎鼎的红黑树了。如下图所示。
为什么使用红黑树:
红黑树性质:
算法思路: 红黑树的思想就是对2-3查找树进行编码,尤其是对2-3查找树中的3-nodes节点添加额外的信息。红黑树中将节点之间的链接分为两种不同类型,红色链接,他用来链接两个2-nodes节点来表示一个3-nodes节点。黑色链接用来链接普通的2-3节点。特别的,使用红色链接的两个2-nodes来表示一个3-nodes节点,并且向左倾斜,即一个2-node是另一个2-node的左子节点。这种做法的好处是查找的时候不用做任何修改,和普通的二叉查找树相同。 代码: #define BLACK 1#define RED 0 #include <iostream> C+ I' k% R. `5 b7 l7 G. x$ j 1 w O b7 o6 m using namespace std;& E" t9 p, q! {% {4 s/ O9 p5 i; I / X; E0 z& V6 f z6 w& @6 t class bst { private:) _+ A8 ~7 ~' C! `" N3 h. ? 3 M3 o% C u: U/ m& \' @ struct Node { int value;% E% ]: U0 x1 g- Y bool color;8 j3 m' M2 u$ i& ?. I) N Node *leftTree, *rightTree, *parent; G' v c% u7 l) i, g F' I7 g7 E9 l# U1 Z4 g Node() : value(0), color(RED), leftTree(NULL), rightTree(NULL), parent(NULL) { }" b: F# _# s$ x% A2 N6 p- P : `+ c: O5 x7 \1 O5 _; X# n2 d Node* grandparent() { if (parent == NULL)' M& b! `# b5 |; P% B {' k$ v# f3 B/ \- ?( X6 o return NULL;4 T: g1 n. c8 y1 `' u3 N6 g } return parent->parent;9 t2 a5 N4 A/ f# F6 F } % J* L, k; Y' o% Z Node* uncle(), Y3 L; Q+ R& L4 m {# f# o {* g2 Z, s- [% M5 L; }1 G" X if (grandparent() == NULL) { return NULL;8 y$ U' o* y) I( B: T! Y. a }" h) R+ X1 q3 a: E if (parent == grandparent()->rightTree)3 y: K3 h) Z+ K return grandparent()->leftTree; else return grandparent()->rightTree; }, f+ N0 e1 t3 U7 o" [9 N/ j Node* sibling() { if (parent->leftTree == this)( i$ e4 L' t0 D5 R1 E0 s% e return parent->rightTree; else: u( J! E' N0 Z3 s9 b3 _; p return parent->leftTree; } };- a6 a- x" p, g6 A3 g1 X void rotate_right(Node *p). g2 |! t1 V) j) z8 i/ m {# E* W p1 Y7 D$ ]; T+ M( ?+ `0 C Node *gp = p->grandparent(); Node *fa = p->parent;+ \$ v8 q( r9 L Node *y = p->rightTree; . b& _; f$ P9 s fa->leftTree = y;7 p" }6 }+ W# P. |1 ~, Y ! V3 Y5 }! B! D8 ^" ] if (y != NIL) y->parent = fa; p->rightTree = fa;# N4 i+ q: J. t/ f9 w fa->parent = p;5 G( @+ X$ S U- z) R% s v . R' d D' K( ?: R: U# k if (root == fa) root = p;0 J- G8 @- F" ?- N p->parent = gp;5 S; J0 | ], V. L) ` if (gp != NULL) { if (gp->leftTree == fa)& b& A7 j' T4 [ l% @6 I gp->leftTree = p;8 K9 X1 G: D, x8 r: T else gp->rightTree = p; }4 W2 n0 o& F" O. }% f* P6 d # d3 @4 P6 Q# T/ i ? q" t } Z. L- N8 H# R0 ^( Z void rotate_left(Node *p) { if (p->parent == NULL) E8 R: Q$ v4 B) x& s& P$ H {; K. r' p1 M# K( R0 t1 d2 R root = p; return; } Node *gp = p->grandparent(); Node *fa = p->parent;+ z4 h ]# T8 H/ ^; { Node *y = p->leftTree; fa->rightTree = y; if (y != NIL): O7 w/ }6 P$ i; P y->parent = fa; p->leftTree = fa;+ ~) y O/ X8 U; ~% Q fa->parent = p;. a7 a6 m/ i: h1 U! {$ Y" ~4 j if (root == fa) root = p; p->parent = gp;% b# G: X, |& S ^3 N) P! J if (gp != NULL)* f2 o- o. q' e3 S { if (gp->leftTree == fa)3 T: j6 @( ^' [' I$ m4 ^' U gp->leftTree = p;% o. C6 d5 k, ?3 H else: y. w* k1 ~) f' n8 b gp->rightTree = p; }9 n' a$ H* }3 A1 I } - r: p( W0 D+ q4 m6 X3 k void inorder(Node *p)7 V( B4 f0 I; a: F; P0 Q { if (p == NIL)4 [; O& V5 V2 [; S return; if (p->leftTree)# N# _2 B( j6 F9 ~ inorder(p->leftTree);+ V) {& P% S6 @( c5 T/ d1 D 9 x% t" _5 ^1 |: v4 \ cout << p->value << " ";0 s) ]' V7 k1 l# I if (p->rightTree)7 m a L$ h3 B, X7 A! _7 R inorder(p->rightTree);- a8 [: { n4 ^! ` }+ i8 B6 C- \" l6 v% q ( @- M; I! I# J$ G" H( u, L2 D string outputColor(bool color) {* _- I8 l* F* Z return color ? "BLACK" : "RED";/ i. W7 v8 A3 ]$ p% C2 W) f } . l0 {! I5 y/ F, |8 `( ^1 Z4 U( \. \ Node* getSmallestChild(Node *p)! J$ T" o! F7 D { if (p->leftTree == NIL)1 l, o3 D4 z# w1 F- l. _' Z return p; return getSmallestChild(p->leftTree);2 _1 T$ ^" z+ |' o9 c } k9 f6 n8 K& J, C; Y7 b bool delete_child(Node *p, int data): G0 |8 }6 ^" @ { if (p->value > data) { if (p->leftTree == NIL) {9 A- R! W: C% _5 o5 P0 }! h return false; } return delete_child(p->leftTree, data); }8 c: A& ^$ [3 |& O else if (p->value < data) {) G0 v! d- Z( W' E( M+ j if (p->rightTree == NIL) { return false;$ F( j' K- Z! t; C( o6 e }( B8 e1 a/ |( L1 \2 E( B( { return delete_child(p->rightTree, data);: z: u* t8 S9 y4 v0 m& \ }/ Y ~1 A* X' i8 N; g. Q2 S% o3 D- a& n else if (p->value == data) { if (p->rightTree == NIL) { p7 B; n, l; u9 w* ^ delete_one_child(p);8 z: v% r9 Z, }4 K; N ~4 ? h return true;2 Y% ~" G) k2 d, ]1 t( w4 r% ^9 { } Node *smallest = getSmallestChild(p->rightTree);9 j4 `9 S; G- Q- p! L# w swap(p->value, smallest->value); delete_one_child(smallest);; o1 J G# D' {$ L6 A( g return true;: J0 Q2 m6 q! c+ U: ]- p! S } else {0 [* Y5 r" Q4 U% z* ] return false;3 M0 f* Y. [& x4 C; ?) R. @ E }5 W* v' l6 I8 ]" S& J, R }4 Y% D0 _! A B2 ]3 c' L5 K ( d( I" ]3 U- D5 j void delete_one_child(Node *p) {7 D2 u, Y; K8 a Node *child = p->leftTree == NIL ? p->rightTree : p->leftTree;; q' O0 A+ b8 _7 ` if (p->parent == NULL && p->leftTree == NIL && p->rightTree == NIL) { p = NULL; root = p; return; }3 G+ G) }/ Y% L8 p3 T h) g 7 P* b5 L! M3 f0 T; C) q if (p->parent == NULL)% l6 x, g' J3 p) b {3 s! t$ s1 u! H" z delete p;: U( d( y+ q+ E, Q i child->parent = NULL;0 s; ~$ Z! \9 i7 h0 A root = child; root->color = BLACK;/ V: y( i Q3 w return; }8 L6 P l: R* p0 }. R* P ( C' S- j) O7 h7 ?* B( f if (p->parent->leftTree == p)- l# O" a4 z/ b7 A: `- G* i {1 ~% v7 Q3 ^& A6 O p->parent->leftTree = child; }( L3 S( k& o5 `7 @3 n else {, Q y' w: y% c G, A p->parent->rightTree = child;& `1 N$ H3 b+ E }% o- n7 Q/ V3 k6 G2 i' } child->parent = p->parent;* ?( o! ?0 F* L1 l" b5 | 7 ^5 y2 F$ w' N" N. _; C if (p->color == BLACK) {6 G* ]4 @9 {9 q) \& X1 D) i if (child->color == RED) {3 j3 T" a4 | j4 c* U7 Z child->color = BLACK; } else! k. w- E0 Z$ o* K; x3 U9 i delete_case(child); }+ A# s" ?# g1 T; b) V delete p; }. u2 B; t( W9 w' } void delete_case(Node *p)6 y* y; S# W% w5 W# [0 Z { if (p->parent == NULL) { p->color = BLACK; return;; z! \, Z. o3 z% n }+ a9 Z* G: s6 ? if (p->sibling()->color == RED)( B! ]% ?1 L6 q. q+ W& m { p->parent->color = RED;) L3 p* b5 o9 i9 { p->sibling()->color = BLACK;( a' V" n7 g3 Q% |( y if (p == p->parent->leftTree) rotate_left(p->sibling()); o3 n& q* p4 m( G else rotate_right(p->sibling()); } if (p->parent->color == BLACK && p->sibling()->color == BLACK9 q$ i" s& O( h/ R/ d. Z && p->sibling()->leftTree->color == BLACK && p->sibling()->rightTree->color == BLACK)4 }4 z! P J3 B( Z+ j1 Z {% {' k: [+ h1 u1 q p->sibling()->color = RED;/ J+ P. ?+ L. _5 ? delete_case(p->parent);' l5 o: V4 R B. Z. l4 l }2 r8 I9 K7 U2 D8 f% e, z5 z+ w else if (p->parent->color == RED && p->sibling()->color == BLACK && p->sibling()->leftTree->color == BLACK && p->sibling()->rightTree->color == BLACK) { p->sibling()->color = RED;- B7 b1 s p9 h p->parent->color = BLACK; } else {& t' x9 n1 x4 H$ C if (p->sibling()->color == BLACK)6 h) |+ |( D3 M* F; L {+ T* V: ~; P9 @ ]! W- @ if (p == p->parent->leftTree && p->sibling()->leftTree->color == RED && p->sibling()->rightTree->color == BLACK) { p->sibling()->color = RED;2 a) X: k2 D3 a# S p->sibling()->leftTree->color = BLACK; rotate_right(p->sibling()->leftTree);5 |$ B3 d. H) u0 y } L& b- E5 e; q1 m else if (p == p->parent->rightTree && p->sibling()->leftTree->color == BLACK && p->sibling()->rightTree->color == RED) { p->sibling()->color = RED; p->sibling()->rightTree->color = BLACK; rotate_left(p->sibling()->rightTree);0 _1 q1 K4 @# p- X; G3 p6 H3 M }0 k) X8 V- h0 S }* U7 y% s; u- f# V1 X0 f \ p->sibling()->color = p->parent->color; p->parent->color = BLACK; if (p == p->parent->leftTree) {. S1 O: I& t8 y. d8 q, F p->sibling()->rightTree->color = BLACK;/ n6 e$ g- N+ n. d( b rotate_left(p->sibling());9 X1 K0 o' G9 [. P. b } else% X5 p. H: d; B) i |. W( D0 ]* ~ { p->sibling()->leftTree->color = BLACK; rotate_right(p->sibling()); } } }) N( ]* |9 {! u5 y void insert(Node *p, int data): J6 J, F6 V+ y l6 E { if (p->value >= data)$ W& V9 R2 O8 G% S; d {+ j: B* `& g! g2 w if (p->leftTree != NIL) insert(p->leftTree, data);% y; e4 L S" t/ _( c9 r" t( | else { Node *tmp = new Node();; E- ~7 c8 p& c* L1 |2 M tmp->value = data;- @% A! C. u0 | tmp->leftTree = tmp->rightTree = NIL;" p2 z- i: M& z9 x& ^& o; _- J8 w tmp->parent = p;& o( X$ ?, v: B0 K p->leftTree = tmp; insert_case(tmp);7 W4 ]8 @5 [9 X: ?2 B+ v } } else {2 I& [* l+ Z3 d4 p& }1 `/ { if (p->rightTree != NIL) insert(p->rightTree, data); else { Node *tmp = new Node(); tmp->value = data;4 z- ^ E* o3 \# r) o4 t* x tmp->leftTree = tmp->rightTree = NIL; tmp->parent = p; p->rightTree = tmp; insert_case(tmp); } }. w. s# P* q- G }& U' W0 B! o. E# B void insert_case(Node *p)! w, a2 t; M3 `+ g( P S! e {+ M# n* Z6 L! W4 j3 N M if (p->parent == NULL) { root = p;) J: h5 q( v' I% l) H( P1 s4 F p->color = BLACK;7 k7 Y$ K+ B$ K7 M8 | return;( D0 J6 @3 A4 S g1 a, N% O4 T" Q( Q4 D }$ g2 R& n$ d( l% }3 v" R3 o if (p->parent->color == RED) { if (p->uncle()->color == RED) {" x3 V* j0 Z w- o: I0 j }+ L p->parent->color = p->uncle()->color = BLACK; p->grandparent()->color = RED;% m' l/ ^7 o/ T* i4 r! N insert_case(p->grandparent()); }+ u' m" X' B: ?$ D8 f3 r7 Q3 B3 A else {- }" ^2 s/ t* M9 C* {! H if (p->parent->rightTree == p && p->grandparent()->leftTree == p->parent)9 {/ V' S/ d7 A+ q: J {$ A% q2 H% q |; b rotate_left(p); rotate_right(p); p->color = BLACK; p->leftTree->color = p->rightTree->color = RED; o |4 x) S% Z6 _& T Q# U$ n2 B } else if (p->parent->leftTree == p && p->grandparent()->rightTree == p->parent)- m% i; F% `5 k9 P {2 U6 ^7 D6 P4 \2 ^( A rotate_right(p);( ]' `+ s' T& K7 l! E5 N rotate_left(p); p->color = BLACK; p->leftTree->color = p->rightTree->color = RED;+ ~1 ~! h$ d. n8 D2 m- w } else if (p->parent->leftTree == p && p->grandparent()->leftTree == p->parent) {6 I3 J z7 t1 y( I! d p->parent->color = BLACK; p->grandparent()->color = RED;- n+ X) O1 C, n rotate_right(p->parent); } else if (p->parent->rightTree == p && p->grandparent()->rightTree == p->parent) {; C- M# ?) e) w+ R# \/ H( D p->parent->color = BLACK; p->grandparent()->color = RED;& \3 O! t8 H( k3 R: P z2 N0 R# d rotate_left(p->parent);) m6 c( U1 f/ Z% P }1 q6 B) Z) _6 ?% t2 b } } } void DeleteTree(Node *p)8 w6 D p+ |8 Q# z {' _& C& n3 J4 b2 j t6 u. [ if (!p || p == NIL) {, u' i) j# A# Y2 J return; } DeleteTree(p->leftTree); DeleteTree(p->rightTree);% U' B2 v5 f5 `) A& q; Q% A delete p; } public: 9 `; K( J4 G( q: P2 j) t7 a. _$ T# W bst()" l8 _2 g" }4 S7 | { NIL = new Node();) ^3 m z# C ~; O. @ NIL->color = BLACK;8 ~1 l6 l- U+ y4 v! {& m; f, _# ] root = NULL; q6 E! F' p: j& a9 g; g+ D }: L! O+ i9 j! `0 g ~bst() {* ?! e$ ? d0 F3 c: L if (root)0 w- T3 z- T0 y# X* P3 d DeleteTree(root);8 a3 @8 ?" @, Z" v, ` delete NIL;" B& w, I" Q/ x/ t( v } " J9 o- h# E0 a& p; v) D void inorder()5 X6 b7 G' O* x, ?2 F. u; @; F { if (root == NULL). f8 M2 Y! v- N) K# y return;) v0 p: j; f: a5 o- X! q# `1 ~) C- ~ inorder(root);& K* m. H8 ^" Z. d, h9 @ cout << endl; }( }( Q; G$ G5 ~8 z! J- H : D V' c6 ?$ C. r& e void insert(int x)0 Z5 G# N' S6 X6 e. X" J/ ` {% |+ \& k( p' m+ j! Q4 f1 d if (root == NULL)2 m) x0 }) |' B- s4 k6 E {9 y2 T7 T6 q1 m" o! T% f0 R3 {5 N root = new Node();2 y% O$ X' u: Y% j root->color = BLACK; root->leftTree = root->rightTree = NIL;# F5 W$ T$ i( r! O- U root->value = x; } else { insert(root, x); } } bool delete_value(int data) { return delete_child(root, data); }7 d8 \3 C8 w/ V. v1 C8 w private:: w7 X) y: J/ O1 q" ]3 B Node *root, *NIL; }; 9 J5 N* p5 [6 n, } int main()' k) _3 v. m4 [ {& n7 t* c2 n9 E; U! d0 k2 W- D ?0 u cout << "---【红黑树】---" << endl; // 创建红黑树 bst tree; - f/ Q' H& ^! C2 i; Q# V: j; z // 插入元素 tree.insert(2); tree.insert(9); tree.insert(-10); tree.insert(0);2 C2 S# o/ @* t7 o4 F% P tree.insert(33); tree.insert(-19);4 c" a" d) Q$ i, q K , X5 {& W( y/ S4 }2 d5 v // 顺序打印红黑树 cout << "插入元素后的红黑树:" << endl; tree.inorder();" Y' p6 p/ v! ] // 删除元素- H& B" c" F y2 T+ E6 W) j tree.delete_value(2); ' I+ P- y& k/ ] // 顺序打印红黑树 cout << "删除元素 2 后的红黑树:" << endl; tree.inorder(); I/ ?, ]" K: {- S, s3 F5 K: l) G7 g // 析构 tree.~bst();$ E) m$ u$ ]5 B# ~& I ] ! u% X% l' ?, f4 `5 m getchar(); return 0; }3 P4 `- K* G! m( Z 9 B树/B+树
B 树可以看作是对2-3查找树的一种扩展,即他允许每个节点有M-1个子节点。
如:(M=3)
算法思路: 从根结点开始,对结点内的关键字(有序)序列进行二分查找,如果命中则结束,否则进入查询关键字所属范围的儿子结点;重复,直到所对应的儿子指针为空,或已经是叶子结点;
代码: BTNode* BTree_recursive_search(const BTree tree, KeyType key, int* pos){3 t0 }, J2 E0 J* c9 N" F9 t int i = 0; while (i < tree->keynum && key > tree->key) {- s8 f) h/ j& {3 @! W ++i; }: _# A6 q' V( Y+ o // 查找关键字9 x2 z- e8 j* Q if (i < tree->keynum && tree->key == key) { *pos = i;1 J, X5 x2 `$ R return tree; }5 k% W' C# z! r- O" ]! G7 |) g4 H , p( ?8 [0 ^1 v: x% A2 H6 c // tree 为叶子节点,找不到 key,查找失败返回 if (tree->isLeaf) { return NULL; }7 ?; w' H' _" Q1 }4 W; { // 节点内查找失败,但 tree->key[i - 1]< key < tree->key, // 下一个查找的结点应为 child // 从磁盘读取第 i 个孩子的数据 disk_read(&tree->child); $ p8 c7 v* ?, W$ H" s$ F // 递归地继续查找于树 tree->child' w# M _2 `5 w( B% c return BTree_recursive_search(tree->child, key, pos);/ @" X) ^. I, a } B+树: B+树是B树的变体,也是一种多路搜索树: 其定义基本与B-树同,除了:
如:(M=3)
算法思路: B+的搜索与B树也基本相同,区别是B+树只有达到叶子结点才命中(B树可以在 非叶子结点命中),其性能也等价于在关键字全集做一次二分查找;
|
| 很强大 |
微信公众号
手机版