感受查找這一章學的不深,下面作一些簡單小結吧。node
一、查找表:是由同一類型的數據元素(或記錄)構成的集合。ios
二、關鍵字:是數據元素(或記錄)中某個數據項的值,用它能夠標識一個數據元素(或記錄)。算法
主關鍵字:若此關鍵字能夠惟一地標識一個記錄,則稱此關鍵字爲主關鍵字。函數
反之稱爲次關鍵字。學習
三、查找flex
(1)靜態查找表:在查找的同時不對錶進行修改操做。this
(2)動態查找表:在查找的同時對錶作修改操做(如插入、刪除)。spa
🔺四、平均查找長度:指針
爲肯定記錄在查找表中的位置,需和給定值進行比較的關鍵字個數的指望值,稱爲查找算法的在查找成功時的平均查找長度。(ASL)code
ASL = ∑ PiCi(從i = 1 到 i = n求和)
其中,Pi爲查找表中第i個記錄的機率,且∑ P = 1;
Ci爲找到表中其關鍵字與給定值相等的第i個記錄時,和給定值已進行過比較的關鍵字個數。(顯然,Ci隨查找過程的不一樣而不一樣)。
查找:
(1)首先是順序查找
其數據類型的定義以下:
1 typedef KeyType int;//這個根據具體狀況去定義;在這裏定義爲int; 2 3 typedef struct{ 4 KeyType key; 5 InfoType otherinfo;//這個根據具體狀況去改,這裏只是抽象的說成還要添加這些類型。 6 }ElemType; 7 8 typedef struct{ 9 ElemType *R; 10 int length; 11 }SSTable;
1)普通的順序查找
1 int Search_Seq(SSTable ST,KeyType key) 2 {//在順序表ST中順序查找其關鍵字等於Key的數據元素。若找到,則函數值爲該元素在表中的位置,不然爲0; 3 for(int i = ST.length ; i >= 1 ;i--) 4 { 5 if(ST.R[i].key==key) 6 return i;//從後往前找; 7 return 0; 8 } 9 10 }
2)設置監視哨的順序查找
1 int Search_Seq(SSTable ST,KeyType key) 2 {//在順序表ST中順序查找其關鍵字等於key的數據元素。若找到,則函數值爲該元素在表中的位置,不然爲0; 3 ST.R[0].key = key; 4 for(int i = ST.length;ST.R[i].key!=key;--i); 5 return i ; 6 7 8 }
(2)折半查找(二分查找)
算法描述:
1 int Search_Bin(SSTable ST,KeyType key) 2 {//在有序表ST表中折半查找其關鍵字等於key的數據元素。若找到,則函數值爲該元素在表中的位置,不然爲0; 3 int low = 1 ,high = ST.length;//置查找區間初值。 4 int mid; 5 while(low<=high) 6 { 7 mid = (low+high)/2; 8 if(key==ST.R[mid].key) //找到待查找元素; 9 return mid; 10 else 11 if(key<ST.R[mid].key) high = mid - 1;//繼續在前一子表進行查找; 12 else 13 low = mid + 1;//繼續在後一子表中進行查找; 14 } 15 return 0; //表中不存在待查元素; 16 17 }
以上是非遞歸結構(即迭代)實際上二分也能夠用遞歸結構。
鏈式結構也能夠進行二分查找,可是不能在對數時間內完成。由於實際上,每次比較的話,鏈表都得遍歷一遍,花了O(n)時間。
二分查找應用場景的侷限性:
(1)基於順序表的存儲結構;
(2)針對有序數據;
(3)數據量小且比較操做不耗時時,不須要二分;
(4)數據量太大也不行(超出內存可用連續空間);
(3)分塊查找;
針對前面查找的侷限性,咱們纔有了下面的排序(在樹表中進行查找);
(4)二叉排序樹;
定義:二叉排序樹或者是一顆空樹或者是具備下列性質的二叉樹;
1)若它的左子樹不空,則左子樹上全部結點的值均小於它的根結點的值。
2)若它的右子樹不空,則右子樹上全部結點的值均大於它的根結點的值。
3)它的左、右子樹也分別爲二叉排序樹。
二叉排序樹的二叉鏈表存儲表示
1 typedef struct{ 2 KeyType key;//關鍵字項; 3 InfoType otherinfo; //其餘數據項; 4 }ElemType; 5 typedef struct BSTNode{ 6 ElemType data;//每一個結點的數據域包括關鍵字項和其餘數據項 7 struct BSTNode *lchild,*rchild;//左右孩子的指針; 8 }BSTNode,*BSTree;
二叉排序樹的查找
1 BSTNode* Search(BSTree t ,KeyType x) 2 { 3 if(t==NULL)return NULL; 4 if(t->data.key==x) return t; 5 if(t->data.key>x) return Search(t->lchild,x); 6 if(t->data.key<x) return Search(t->rchild,x); 7 }
二叉排序樹的插入
1 void InsertBST(BSTree &T,ElemType e) 2 {//當二叉排序樹T中不存在關鍵字等於e.key的數據元素時,則插入該元素; 3 if(T==NULL) 4 { 5 s = new BSTNode; 6 s->data = e; 7 s->lchild = s->rchild = NULL; 8 T = s; 9 }else 10 if(e.key<T->data.key) 11 InsertBST(T->lchild,e); 12 else if(e.key>T->data.key) 13 InsertBST(T->rchild,e); 14 15 }
二叉排序樹的建立
1 void CreatBST(BSTree &T) 2 {//依次讀入一個關鍵字爲key的結點,將此結點插入二叉排序樹T中; 3 T = NULL;//將二叉排序樹T初始化爲空樹; 4 cin>>e; 5 while(e.key!=ENDFLAG)//ENDFLAG爲自定義變量,做爲輸入結束的標誌; 6 { 7 InsertBST(T,e); 8 cin>>e; 9 } 10 }
實際上,二叉排序樹的查找複雜度是介於O(log2n)~O(n)之間的,那麼如何提升二叉排序樹的查找效率呢?
就是儘可能讓二叉排序樹的形狀均衡;
因此接着引入了平衡樹二叉樹。
平衡樹(AVL樹)具備以下特徵:
(1)左子樹和右子樹的深度之差的絕對值不超過1;
(2)左子樹和右子樹也是平衡二叉樹;
對於平衡樹我是不太熟悉的,只知道基本概念;可是它的查找和修改基本是以對數時間的,解決了二分查找中只能進行靜態查找的侷限;
接着又講了B-樹和B+樹,B-樹解決了數據量大時的查找,而B+樹則在B-樹的基礎上解決了區間查找問題。
對於B-樹和B+樹我也不太熟悉,在這裏便不展開了。
散列表的查找
散列查找法主要研究一下兩方面的問題:
(1)如何構造散列函數;
(2)如何處理衝突;
1)造表時如何解決; 2)查找時如何解決;
散列表的構造方法:
1)數字分析法; 2)平方取中法 ;3)摺疊法; 4)除留餘數法;
🔺處理衝突的方法:
一、開放地址法 (1)線性探測法 ; (2)二次探測法;(3)僞隨機探測法;
二、鏈地址法(實際上就是鄰接表);
這一章的做業題是:利用哈希表進行二次探測法處理衝突;
The task of this problem is simple: insert a sequence of distinct positive integers into a hash table, and output the positions of the input numbers. The hash function is defined to be H(key)=key%TSize where TSize is the maximum size of the hash table. Quadratic probing (with positive increments only) is used to solve the collisions.
Note that the table size is better to be prime. If the maximum size given by the user is not prime, you must re-define the table size to be the smallest prime number which is larger than the size given by the user.
Input Specification:
Each input file contains one test case. For each case, the first line contains two positive numbers: MSize (≤104) and N (≤MSize) which are the user-defined table size and the number of input numbers, respectively. Then N distinct positive integers are given in the next line. All the numbers in a line are separated by a space.
Output Specification:
For each test case, print the corresponding positions (index starts from 0) of the input numbers in one line. All the numbers in a line are separated by a space, and there must be no extra space at the end of the line. In case it is impossible to insert the number, print "-" instead.
Sample Input:
4 4 10 6 4 15
Sample Output:
0 1 4 -
解這道題要知道二次探測法的公式: hi=(h(key)+i*i)%m 0≤i≤m-1 //即di=i2
即探查序列爲d=h(key),d+12,d+22,…,等。
代碼以下:
1 #include<iostream> 2 #include<stdio.h> 3 #include<cmath> 4 using namespace std; 5 6 const int maxn = 1e4+5; 7 int mod , n; //定義題目給的模數和數的個數; 8 int a[maxn]; 9 bool isprime(int tmp) //判斷是否爲素數; 10 { 11 int flag = 0; 12 int tp = sqrt(tmp); //取根號一半去判斷就行了; 13 if(tmp==1) //1不是素數,這裏單獨判斷; 14 return false; 15 else 16 { 17 for(int i = 2 ; i <= tp ;i++) 18 { 19 if(tmp%i==0) //若是能被除1外的數整除,則不是素數; 20 { 21 flag = 1; 22 break; 23 } 24 } 25 if(flag==1) 26 { 27 return false; 28 } 29 return true; 30 31 } 32 33 } 34 bool vis[maxn];//用於判斷哪些位置被佔用了; 35 int fl = 0; 36 int main() 37 { 38 scanf("%d%d",&mod,&n); 39 for(int i = 0 ; i < n ;i++) 40 { 41 scanf("%d",&a[i]); 42 } 43 while(isprime(mod)==0) mod++; //找大於題目給的數的最小素數; 44 for(int i = 0 ; i < n ; i++) 45 { 46 fl = 0; 47 for(int j = 0 ; j <= mod ;j++) 48 { 49 if(vis[(a[i]+j*j)%mod]==0) //利用二次探測法;若是位置沒被佔用; 50 { 51 printf("%d",(a[i]+j*j)%mod); 52 vis[(a[i]+j*j)%mod] = 1; 53 fl = 1; 54 break; 55 } 56 } 57 if(fl==0) //若是找不到位置 58 { 59 printf("-"); 60 } 61 if(i!=n-1) //格式問題; 62 { 63 printf(" "); 64 } 65 } 66 return 0; 67 }
實踐題:
實現QQ新賬戶申請和老賬戶登錄的簡化版功能。最大挑戰是:聽說如今的QQ號碼已經有10位數了。
輸入格式:
輸入首先給出一個正整數N(≤105),隨後給出N行指令。每行指令的格式爲:「命令符(空格)QQ號碼(空格)密碼」。其中命令符爲「N」(表明New)時表示要新申請一個QQ號,後面是新賬戶的號碼和密碼;命令符爲「L」(表明Login)時表示是老賬戶登錄,後面是登錄信息。QQ號碼爲一個不超過10位、但大於1000(聽說QQ老總的號碼是1001)的整數。密碼爲不小於6位、不超過16位、且不包含空格的字符串。
輸出格式:
針對每條指令,給出相應的信息:
1)若新申請賬戶成功,則輸出「New: OK」;
2)若新申請的號碼已經存在,則輸出「ERROR: Exist」;
3)若老賬戶登錄成功,則輸出「Login: OK」;
4)若老賬戶QQ號碼不存在,則輸出「ERROR: Not Exist」;
5)若老賬戶密碼錯誤,則輸出「ERROR: Wrong PW」。
輸入樣例:
5 L 1234567890 myQQ@qq.com N 1234567890 myQQ@qq.com N 1234567890 myQQ@qq.com L 1234567890 myQQ@qq L 1234567890 myQQ@qq.com
輸出樣例:
ERROR: Not Exist New: OK ERROR: Exist ERROR: Wrong PW Login: OK
對於這道題,我偷懶用map去寫的,而後看了網上大佬的代碼,學習用哈希表去寫
如下是個人代碼:
1 #include<iostream> 2 #include<stdio.h> 3 #include<map> 4 using namespace std; 5 6 7 int N; 8 string s; 9 const int maxn = 1e5+5; 10 map<string,bool>mp; 11 struct info{ 12 string zh; 13 string password; 14 }in[maxn]; 15 string tmp; 16 int count1 = 0; 17 string tp1,tp2; 18 int flag = 0; 19 int main() 20 { 21 scanf("%d",&N); 22 while(N--) 23 { 24 flag = 0; 25 cin>>s; 26 if(s[0]=='N') 27 { 28 cin>>tp1>>tp2; 29 in[count1].zh = tp1; 30 in[count1++].password = tp2; 31 if(mp[tp1]==1) 32 { 33 printf("ERROR: Exist\n"); 34 }else 35 { 36 mp[tp1] = 1; 37 printf("New: OK\n"); 38 } 39 40 }else 41 if(s[0]=='L') 42 { 43 cin>>tp1>>tp2; 44 if(mp[tp1]==0) 45 { 46 printf("ERROR: Not Exist\n"); 47 }else 48 if(mp[tp1]==1) 49 { 50 for(int i = 0 ; i < count1;i++) 51 { 52 if(in[i].zh==tp1) 53 { 54 tmp = in[i].password; 55 flag = 1; 56 break; 57 } 58 } 59 if(tmp==tp2) 60 { 61 printf("Login: OK\n"); 62 }else 63 { 64 printf("ERROR: Wrong PW\n"); 65 } 66 } 67 } 68 } 69 return 0; 70 }
下面是網上別人用哈希表寫的代碼(我順便學習了一下)
代碼以下:
#include <cstdio> #include <cstdlib> #include <string.h> typedef struct node { char user[11]; char password[17]; struct node* next; } Node; typedef struct { int TableSize; Node* Table; } HashTable; int nextPrime( int n ) { while( 1 ) { bool flag = true; for( int i = 2; i < n; i++ ) { if( n % i == 0 ) { flag = false; break; } } if( flag ) break; n++; } return n; } HashTable* createHashTable( int n ) { HashTable* H = ( HashTable* )malloc( sizeof( HashTable ) ); H->TableSize = nextPrime( n ); H->Table = ( Node* )malloc( ( H->TableSize ) * sizeof( Node ) ); for( int i = 0; i < H->TableSize; i++ ) { H->Table[i].next = NULL; H->Table[i].user[0] = '\0'; H->Table[i].password[0] = '\0'; } return H; } int Hash( char key[] ) { int index = 0; for( int i = 0; i <= 3; i++ ) { index = index * 10 + key[i] - '0'; } return index; } Node* Find( HashTable* H, char key[] ) { int index = Hash( key ) % ( H->TableSize ); Node* ptr = H->Table[index].next; while( ptr && strcmp( ptr->user, key ) ) { ptr = ptr->next; } return ptr; } void Insert( HashTable* H, char key[], char pass[] ) { int index = Hash( key ) % ( H->TableSize ); Node* newNode = ( Node* )malloc( sizeof( Node ) ); newNode->next = H->Table[index].next; H->Table[index].next = newNode; strcpy( newNode->user, key ); strcpy( newNode->password, pass ); } int main() { //freopen( "123.txt", "r", stdin ); int n; scanf( "%d", &n ); HashTable* H = createHashTable( n ); //printf( "%d\n", H->TableSize ); char op; char account[20]; char key[20]; for( int i = 0; i < n; i++ ) { char ch = getchar(); scanf( "%c %s %s", &op, account, key ); //printf( "%s %s\n", account, key ); if( op == 'L' ) { Node* p = Find( H, account ); if( !p ) printf( "ERROR: Not Exist\n" ); // 老帳戶號碼不存在 else if( strcmp( p->password, key ) ) { // 老帳戶密碼錯誤 printf( "ERROR: Wrong PW\n" ); } else { // 登陸成功 printf( "Login: OK\n" ); } } if( op == 'N' ) { Node* p = Find( H, account ); if( p ) { // 申請的帳戶已經存在 printf( "ERROR: Exist\n" ); } else { Insert( H, account ,key ); printf( "New: OK\n" ); } } } return 0; }
對於這一章的學習,我以爲我學得比較淺,大多都是概念性的東西,沒有具體到代碼實現,並且哈希表挺重要的,我還不會本身實現,我會去進一步去本身實現一遍哈希表;
對於下一章的學習,收心學習,會代碼實現,靈活應用;