[資料結構][C++] 查詢和排序(雜湊表儲存基本思想)
雜湊表類概念摘要
雜湊表類SqHash的建立、查詢。設有若干個學生的考試成績,採用除留餘數求雜湊地址,將學生的資訊儲存到該地址空間,並且採用線性探測法解決衝突問題。
雜湊表又稱散列表。
雜湊表儲存的基本思想是:以資料表中的每個記錄的關鍵字 k為自變數,通過一種函式H(k)計算出函式值。把這個值解釋為一塊連續儲存空間(即陣列空間)的單元地址(即下標),將該記錄儲存到這個單元中。在此稱該函式H為雜湊函式或雜湊函式。按這種方法建立的表稱為雜湊表或散列表。
除留餘數法:
取關鍵字k被某個不大於表長m的數p除後所得餘數作為雜湊函式地址的方法。即:
H(k)=k mod p
這種方法的關鍵是選擇好p。使得資料集合中的每一個關鍵字通過該函式轉化後對映到雜湊表的任意地址上的概率相等。理論研究表明,一般取p為小於m的最大質數或不包含小於20的質因素的合數。
線性探測再雜湊:
當發生衝突時,將依次探測“下一個位置”,直到找到其關鍵字相匹配的元素或找到一個空位插入。設雜湊空間長度為m,“下一個位置”由下式確定:
Hi=(H(key)+di) mod m
H(key):雜湊函式
m:雜湊表長度
di:求“下一個位置”的增量
di=1,2,…,m-1。
這種di的取法稱為線性探測再雜湊。即“下一個位置”為雜湊表的直接後繼。若當di=m-1時仍未查到,則說明表滿,還要查詢另外的溢位表。缺點:容易產生“二次聚集”
我來簡單總結一下這個究竟講了什麼這次的內容是查詢和排序那麼跟查詢有關的有雜湊,跟排序有關的有氣泡排序,快速排序關於雜湊的工作原理,大家可以仔細閱讀上面那段話那麼下面先貼出的是
雜湊查詢的演示程式碼
#include <iostream.h> #include <conio.h> #include <string.h> #include <iomanip.h> //--------------------------------------------------------------------------- typedef int KeyType; // 關鍵字的型別 const int MAXSIZE=100; // 陣列的容量 struct ElemType //學生的記錄資訊 { KeyType key ; //學號 char name[8]; //姓名 int english; //成績 int math; //成績 } ; class SqHash { private: ElemType *ht; // 雜湊表陣列 int length; // 雜湊表容量 KeyType p; // 除留餘數法的大質數 public: SqHash( int n1,int p1); ~SqHash(){delete []ht; length=0;}; int find( KeyType K ); void creat_hash(); void PrintOut(); }; //------------------------------------------------------------- SqHash::SqHash(int n1,int p1) { length=n1; p=p1; ht=new ElemType[length]; for(int i=0;i<length;i++)ht[i].key=0; } void SqHash::creat_hash() { int i,j,K,en,ma;char na[8]; cout<<"\n 請逐一輸入各個學號(關鍵字值)(-1結束):"; cin>>K; while(K!=-1) { i=find(K); if(i==-32767) cout<<"\n 成功查詢到,不再插入!"; else if(i!=32767){ cout<<"\n 請輸入學生的姓名,英語成績和高數成績:"; cin>>na>>en>>ma; ht[i].key=K; strcpy(ht[i].name,na); //用串拷貝賦值 ht[i].english=en; ht[i].math=ma; // 插入學生記錄K cout<<"\n 插入成功!" ; cout<<"\n 請逐一輸入各個學號(關鍵字值)(-1結束):"; cin>>K; } } } int SqHash::find( KeyType K ) { int i,j; j=K%p; i=j-1; while((ht[j].key!=0)&&(ht[j].key!=K)&&(j!=i)) j=(j+1)%MAXSIZE;//解決衝突 if(ht[j].key==K) { cout<<"\n 成功查詢到學號為"<<K<<"的學生,位置在:"<<j<<"上"; cout<<"\n\n 該學生的基本資訊為:\n\n"; cout<<"姓名:"<<ht[j].name<<"英語成績:"<<ht[j].english<<"高數成績:"<<ht[j].math; j=-32767; } else if(j==i){ cout<<"\n 表滿,出現異常! OverFlow!";//溢位 j=32767; } return j; } void SqHash::PrintOut() { int i,j; for (i=0;i<length; i++) cout<<"\n i="<<i<<" 學號:"<<setw(6)<<ht[i].key<<" 姓"<<setw(6)<<ht[i].name <<" 英語成績:"<<setw(6)<<ht[i].english<<" 高數成績:"<<setw(6)<<ht[i].math; } int main() { int p0,n0; cout<<"\n 請輸入n值(n值應是記錄總數的1.3-1.5倍)"; cin>>n0; cout<<"\n 請輸入P值(應是不大於n 的大質數):"; cin>>p0; SqHash ha(n0,p0); ElemType a; int k; char ch; do { cout<<"\n\n\n"; cout<<"\n 1. 建立雜湊表(開放地址法) "; cout<<"\n 2. 在雜湊表中查詢某位學生 "; cout<<"\n 3. 輸出雜湊表"; cout<<"\n 4. 結束"; cout<<"\n======================================="; cout<<"\n 輸入您的選擇(1,2,3,4):"; cin>>k; switch(k) { case 1:{ ha.creat_hash(); } break; case 2:{ cout<<"\n 請輸入待查詢的學生學號:"; cin>>a.key; int i=ha.find(a.key); if(i!=-32767) cout<<"\n 此學生"<<a.key<<" 不存在!" ; } break; case 3:{ ha.PrintOut(); } break; } }while(k>=1&&k<=3); _getch(); return 0; }
氣泡排序(Bubble Sort),是一種電腦科學領域的較簡單的排序演算法。
它重複地走訪過要排序的元素列,依次比較兩個相鄰的元素,如果他們的順序(如從大到小、首字母從A到Z)錯誤就把他們交換過來。走訪元素的工作是重複地進行直到沒有相鄰元素需要交換,也就是說該元素已經排序完成。
這個演算法的名字由來是因為越大的元素會經由交換慢慢“浮”到數列的頂端(升序或降序排列),就如同碳酸飲料中二氧化碳的氣泡最終會上浮到頂端一樣,故名“氣泡排序”。
冒泡法排序演示例子
#include<iostream.h>
class intsz
{
public:
int i;
};
int main()
{
int j,t;
intsz i[100];
cout<<"請輸入你需要輸入的資料個數總數:";
cin>>j;
for(int a=0;a<j;a++)
{
cout<<"請輸入第"<<a+1<<"個數值:";
cin>>i[a];
}
for(int b=0;b<j;b++)
for(int c=0;c<j;c++)
{
if(i[b]<i[c])
{
t=i[b];
i[b]=i[c];
i[c]=t;
}
}
cout<<"從小到大排序:"<<endl;
for(a=0;a<j;a++)
{
cout<<i[a]<<" ";
}
cout<<endl<<"從大到小排序:"<<endl;
j--;
for(a=0;a<j+1;j--)
{
cout<<i[j]<<" ";
}
cout<<endl;
return 0;
}
排序除了氣泡排序外還有快速排序、希爾排序
快速排序概念
快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通過一趟排序將要排序的資料分割成獨立的兩部分,其中一部分的所有資料都比另外一部分的所有資料都要小,然後再按此方法對這兩部分資料分別進行快速排序,整個排序過程可以遞迴進行,以此達到整個資料變成有序序列。
設要排序的陣列是A[0]……A[N-1],首先任意選取一個數據(通常選用陣列的第一個數)作為關鍵資料,然後將所有比它小的數都放到它前面,所有比它大的數都放到它後面,這個過程稱為一趟快速排序。值得注意的是,快速排序不是一種穩定的排序演算法,也就是說,多個相同的值的相對位置也許會在演算法結束時產生變動。
一趟快速排序的演算法是:
1)設定兩個變數i、j,排序開始的時候:i=0,j=N-1;
2)以第一個陣列元素作為關鍵資料,賦值給key,即key=A[0];
3)從j開始向前搜尋,即由後開始向前搜尋(j--),找到第一個小於key的值A[j],將A[j]和A[i]互換;
4)從i開始向後搜尋,即由前開始向後搜尋(i++),找到第一個大於key的A[i],將A[i]和A[j]互換;
5)重複第3、4步,直到i=j; (3,4步中,沒找到符合條件的值,即3中A[j]不小於key,4中A[i]不大於key的時候改變j、i的值,使得j=j-1,i=i+1,直至找到為止。找到符合條件的值,進行交換的時候i, j指標位置不變。另外,i==j這一過程一定正好是i+或j-完成的時候,此時令迴圈結束)。
快速排序又分為兩種,一種是遞迴,一種是非遞迴 1.遞迴快速排序演算法:略,如果需要的朋友可以留言(或者自己去百度) 2.非遞迴快速排序演算法:
#include <iostream.h>
#include <conio.h>
struct node //記錄結構。為簡化問題,設定記錄中只含關鍵字
{int key;
}a[20]; //a陣列是全域性變數
int n; //n也是全域性變數
void print( )
{int i;
for (i=0;i<n;i++) cout<<a[i].key<<" ";
cout<<"\n";
}
void creat()
{int i; n=0;
cout<<"input keys"; cin>>i;
while (i!=-9999) {a[n].key=i; n++;
cin>>i;
}
}
template <class T>
int hoare(T a[20],int l,int h) /*分割槽處理函式*/
{int i,j; node x;
i=l;j=h;x.key=a[i].key;
do{ while((i<j) && (a[j].key>=x.key)) j--;
if(i<j) {a[i].key=a[j].key;i++;}
while((i<j) && (a[i].key<=x.key)) i++;
if (i<j) {a[j].key=a[i].key;j--;}
}while (i<j);
a[i].key=x.key; return i;
}//hoare end
template <class T>
void quick1(T a[20],int n) //非遞迴的快速排序主體函式
{int i,l,h,tag,top;
int s[20][2];
l=0;h=n-1;tag=1;top=0;
do {while (l<h) {i=hoare(a,l,h);
top++; s[top][0]=i+1; s[top][1]=h;h=h-1;
}
if(top==0) tag=0;
else { l=s[top][0];
h=s[top][1]; top--;
}
}while (tag==1);
cout<<"\n 輸出非遞迴快速排序的結果:"<<endl;
}//quick1 end
int main()
{ creat(); print();
quick1(a,n); print(); // a陣列和n都是全域性變數
_getch(); return 0;
}
希爾排序
希爾排序(Shell's Sort)是插入排序的一種又稱“縮小增量排序”(Diminishing Increment Sort),是直接插入排序演算法的一種更高效的改進版本。希爾排序是非穩定排序演算法。該方法因D.L.Shell於1959年提出而得名。
希爾排序是把記錄按下標的一定增量分組,對每組使用直接插入排序演算法排序;隨著增量逐漸減少,每組包含的關鍵詞越來越多,當增量減至1時,整個檔案恰被分成一組,演算法便終止。
先取一個小於n的整數d1作為第一個增量,把檔案的全部記錄分組。所有距離為d1的倍數的記錄放在同一個組中。先在各組內進行直接插入排序;然後,取第二個增量d2<d1重複上述的分組和排序,直至所取的增量 =1( < …<d2<d1),即所有記錄放在同一組中進行直接插入排序為止。
該方法實質上是一種分組插入方法
比較相隔較遠距離(稱為增量)的數,使得數移動時能跨過多個元素,則進行一次比較就可能消除多個元素交換。D.L.shell於1959年在以他名字命名的排序演算法中實現了這一思想。演算法先將要排序的一組數按某個增量d分成若干組,每組中記錄的下標相差d.對每組中全部元素進行排序,然後再用一個較小的增量對它進行,在每組中再進行排序。當增量減到1時,整個要排序的數被分成一組,排序完成。
一般的初次取序列的一半為增量,以後每次減半,直到增量為1。
給定例項的shell排序的排序過程
假設待排序檔案有10個記錄,其關鍵字分別是:
49,38,65,97,76,13,27,49,55,04。
增量序列的取值依次為:5,2,1
希爾排序演示程式碼:
#include <iostream.h>
#include <conio.h>
struct node //記錄結構。為簡化問題,設定記錄中只含關鍵字
{int key;
}a[20];
int n;
void print( )
{ int i;
for (i=0;i<n;i++) cout<<a[i].key<<endl
}
void creat()
{int i; n=0;
cout<<"input keys:"; cin>>i;
while (i!=-9999) {a[n].key=i;n++;
cin>>i;
}
}
template <class T>
void shell(T a[n],int n) //希爾排序
{ int i,j,k;
for(i=n;i>=1;i--) a[i].key=a[i-1].key;
k=n/2;
while (k>=1)
{ for (i=k+1;i<=n;i++)
{ a[0].key=a[i].key; j=i-k;
while((a[j].key>a[0].key)&&(j>=0))
{ a[j+k].key=a[j].key; j=j-k; }
a[j+k]=a[0];
}
k=k/2;
}
for (i=0;i<n;i++) a[i].key=a[i+1].key;
cout<<"\n 輸出希爾排序的結果:"<<endl;
}//shell end
int main()
{ creat(); print();
shell(a,n); print();
_getch(); return 0;
}