本文爲原創,轉載使用請註明來至:http://blog.csdn.net/j903829182/article/details/38036873css
/* 1.查找是在一個數據元素集合中查找keyword等於某個給定keyword數據元素的過程。 2.查找主要有靜態查找,動態查找和哈希查找。 3.靜態查找是指在數據元素集合中查找是否存在keyword等於某個給定keyword的數據元素。 4.動態查找除包含靜態查找的要求,還包含在查找過程當中同一時候插入數據元素集合中不存在的數據元素,或者從數據元素集合中 刪除已存在的某個數據元素的要求。即假設在某個數據元素集合中進行了動態查找,則該數據元素集合可能會被改變。 5.哈希表是一種既適用於靜態查找問題。又適用於動態查找問題,並且查找效率很高的解決查找問題的存儲結構。*/ #include<stdio.h> #include<malloc.h> #define MaxSize 100 #define true 1 #define false 0 typedef int KeyType; typedef struct{ KeyType key; }DataType; typedef struct{ DataType list[MaxSize]; int size; }SeqList; //初始化 void ListInitiate(SeqList *L){ L->size=0;//設置初始元素的個數爲0 } //求當前數據元素個數 int GetListLength(SeqList L){ return L.size;//返回元素的個數 } //插入數據元素 int ListInsert(SeqList *L,int i,DataType data){ int j;//定義變量j if(L->size>=MaxSize){ printf("順序表已滿。沒法插入。。\n"); return false;//返回 } if(i<0||i>L->size+1){ printf("輸入的參數不合法。不能進行插入!html
!node
\n"); return false;//返回 }else{ for(j=L->size;j>=i;j--){ L->list[j]=L->list[j-1];//移動元素 } L->list[i-1]=data;//賦值 L->size++;//元素個數加一 return true; } } //刪除數據元素 int DeleteList(SeqList *L,int i,DataType *data){ int j=0; if(L->size==0){ printf("線性表爲空,不能執行刪除操做!!\n"); return false; } if(i<0||i>L->size){ printf("刪除的位置i不對。不能夠進行刪除!!\n"); return false; }else{ *data=L->list[i-1]; for(j=i;j<L->size;j++){ L->list[j-1]=L->list[j]; } L->size--; return true; } } //取數據元素 int ListGet(SeqList *L,int i,DataType *data){ if(i<0||i>L->size){ printf("取的位置不對,不能進行取值操做!!\n"); return false; }else{ *data=L->list[i-1]; return true; } } //推斷是否爲空的操做 int Empty(SeqList L){ if(L.size==0){ return true; }else{ return false; } } //打印所有輸出的函數 void displayData(SeqList L){ int i; for(i=0;i<L.size;i++){ printf("%d ",L.list[i]); } } /* 1.靜態查找的存儲結構主要有順序表。有序表和索引順序表三種存儲結構 2.順序表上查找的基本思想:從順序表的一端開始,用給定數據元素的keyword逐個與順序表中個數據元素的關鍵 字進行比較。若在順序表中查找到要查找的數據元素。則查找成功,函數返回該數據元素在順序表中的位置;不然查找失敗, 函數返回-1; 3.順序表的查找查找平均查找長度爲(n+1)/2 */ int SeqSearch(SeqList s,DataType x){ //順序表s中依次查找數據元素x //查找成功,則返回該數據元素的位置,不然返回-1 int i=0; while(i<s.size&&s.list[i].key!=x.key){ i++; } if(s.list[i].key==x.key){ return i; }else{ return -1; } } /* 1.有序順序表的查找算法主要有順序查找和折半查找 */ //順序查找(有序順序表上的查找算法平均查找長度爲(n+1)/2) int OrderSeqSearch(SeqList s,DataType x){ //在有序順序表s中順序查找數據元素x //查找成功。則返回該數據元素的位置。不然返回-1 int i=0; while(i<s.size&&s.list[i].key<x.key){ i++; } if(s.list[i].key==x.key){ return i; }else{ return -1; } } /* 1.有序順序表上折半查找算法的基本思想:在一個查找區間中,肯定出查找區間的中心位置。用待查找數據元素的keyword與中心 位置上數據元素的keyword進行比較。若二者相等,則查找成功;不然,若前者小於後者,則把查找區間定爲原查找區間的前半段繼續這種 過程。否若前者大於後者。則把查找區間定爲原查找區間的後半段繼續這種過程。這種查找過程一直進行到查找區間的上界小於查找區間的 下界爲止。 2.平均查找長度爲lbn */ //有序順序表上折半查找算法 int BinarySearch(SeqList s,DataType x){ //在有序順序表s中折半查找數據元素x //查找成功。則返回該數據元素的位置。不然返回-1 int low=0,high=s.size-1;//肯定初始查找區間上下界 int mid;// while(low<=high){ mid=(low+high)/2;//肯定查找區間的中心位置 if(s.list[mid].key==x.key){ return mid;//查找成功 }else if(s.list[mid].key<x.key){ low=mid+1; }else if(s.list[mid].key>x.key){ high=mid-1; } } return -1;//查找失敗 } /* 1.動態查找的存儲結構主要有二叉樹結構和樹結構兩種類型。二叉樹結構又分爲二叉排序樹,平衡二叉樹等。算法
樹結構又分爲B_樹和B+樹等。數組
2.二叉排序樹的基本概念:二叉排序樹或者是一棵空樹,或者是具備下列性質的二叉樹: a.若左子樹不空,則左子樹上所有結點的keyword值均小於根結點的keyword值。 b.若右子樹不空。則右子樹上的所有結點的keyword均大於等於根結點的keyword值。函數
c.左右子樹也均爲二叉排序樹 3.二叉排序樹一般採用二叉鏈存儲結構。二叉排序樹中結點的結構體定義例如如下: typedef struct node{ DataType data; struct node *leftChild; struct node *rightChild; }BiTreeNode; 4.在最壞狀況下,二叉排序樹的平均查找長度爲O(n),在普通狀況下,二叉排序樹平均查找長度爲O(lbn) */ //二叉排序樹的查找算法。就是遍歷二叉排序樹,並在遍歷過程當中尋找要查找的數據元素是否存在。post
typedef struct Node{ DataType data;//數據域 struct Node *leftChild;//左子樹指針 struct Node *rightChild;//右子樹指針 }BiTreeNode;//節點的結構體定義 //初始化 void Initiate(BiTreeNode **root){ *root=(BiTreeNode *)malloc(sizeof(BiTreeNode)); (*root)->leftChild=NULL; (*root)->rightChild=NULL; } //左插入節點 //若當前節點curr非空,則在curr的左子樹插入元素值爲x的新節點 //原curr所指節點的左子樹成爲新插入節點的左子樹 //若插入成功,則返回新插入節點的指針。不然返回空指針 BiTreeNode *InsertLeftNode(BiTreeNode *curr,DataType x){ BiTreeNode *s,*t; if(curr==NULL){//推斷當前節點是否爲空 return NULL;//是空則返回NULL } t=curr->leftChild;//保存原curr所指節點的左子樹 s=(BiTreeNode *)malloc(sizeof(BiTreeNode));//建立節點空間 s->data=x;//賦值 s->leftChild=t;//新插入節點的左子樹爲原curr的左子樹 s->rightChild=NULL;//右子樹爲空 curr->leftChild=s;//新節點成爲curr的左子樹 return curr->leftChild;//返回新插入節點的指針 } //右插入節點 //若當前節點curr非空,則在curr的右子樹插入元素值爲x的新節點 //原curr所指節點的右子樹成爲新插入節點的右子樹 //若插入成功,則返回新插入節點的指針,不然返回空指針 BiTreeNode *InsertRightNode(BiTreeNode *curr,DataType x){ BiTreeNode *s,*t; if(curr==NULL){//推斷當前節點是否爲空 return NULL;//是空則返回NULL } t=curr->rightChild;//保存原curr所指節點的右子樹 s=(BiTreeNode *)malloc(sizeof(BiTreeNode));//建立節點空間 s->data=x;//賦值 s->rightChild=t;//新插入節點的右子樹爲原curr的右子樹 s->leftChild=NULL;//右子樹爲空 curr->rightChild=s;//新節點成爲curr的右子樹 return curr->rightChild;//返回新插入節點的指針 } //左刪除子樹 //若curr非空,則刪除curr所指節點的左子樹 //若刪除成功,則返回刪除節點的雙親節點,不然返回空指針 BiTreeNode *DeleteLeftTree(BiTreeNode *curr){ //假設當前節點爲空或者左子樹爲空則返回NULL if(curr==NULL||curr->leftChild==NULL){ return NULL; } //釋放節點 //Destroy(&curr->leftChild); curr->leftChild=NULL;//刪除後。當前節點的左子樹爲NULL return curr;//返回刪除節點的雙親節點 } //右刪除子樹 //若curr非空,則刪除curr所指節點的右子樹 //若刪除成功,則返回刪除節點的雙親節點,不然返回空指針 BiTreeNode *DeleteRightTree(BiTreeNode *curr){ //假設當前節點爲空或者右子樹爲空則返回NULL if(curr==NULL||curr->rightChild==NULL){ return NULL; } //釋放節點 // Destroy(&curr->rightChild); curr->rightChild=NULL;//刪除後,當前節點的右子樹爲NULL return curr;//返回刪除節點的雙親節點 } void Visit(DataType item){ printf("%c ",item); } //前序遍歷 /* 1.訪問根節點 2.前序遍歷根節點的左子樹 3.前序遍歷根節點的右子樹 */ void PreOrder(BiTreeNode *root,void Visit(DataType item)){ //前序遍歷二叉樹root,訪問操做爲Visit()函數 if(root!=NULL){ Visit(root->data);//訪問數據 PreOrder(root->leftChild,Visit);//訪問左子樹 PreOrder(root->rightChild,Visit);//反問右子樹 } } //中序遍歷 /* 1.中序遍歷根節點的左子樹 2.訪問根節點 3.中序遍歷根節點的右子樹 */ void InOrder(BiTreeNode *root,void Visit(DataType item)){ //中序遍歷二叉樹root,訪問操做爲Visit()函數 if(root!=NULL){ InOrder(root->leftChild,Visit);//訪問左子樹 Visit(root->data);//訪問數據 InOrder(root->rightChild,Visit);//訪問右子樹 } } //後序遍歷 /* 1.後序遍歷根節點的左子樹 2.後序遍歷根節點的右子樹 3.訪問根節點 */ void PostOrder(BiTreeNode *root,void Visit(DataType item)){ //中序遍歷二叉樹root,訪問操做爲Visit()函數 if(root!=NULL){ PostOrder(root->leftChild,Visit);//訪問左子樹 PostOrder(root->rightChild,Visit);//訪問右子樹 Visit(root->data);//訪問根節點數據 } } //撤銷二叉樹操做 void Destroy(BiTreeNode **root){ if((*root)!=NULL&&(*root)->leftChild!=NULL){ Destroy(&(*root)->leftChild); } if((*root)!=NULL&&(*root)->rightChild!=NULL){ Destroy(&(*root)->rightChild); } free(*root); } void PrintBiTree(BiTreeNode *root,int n){ //逆時針旋轉90度,打印二叉樹root。n爲縮進層數。初始值爲0 int i; if(root==NULL){ return ;//遞歸出口 } PrintBiTree(root->rightChild,n+1);//遍歷打印右子樹 //訪問根節點 for(i=0;i<n-1;i++){ printf(" "); } if(n>0){ printf("---"); printf("%c\n",root->data); } PrintBiTree(root->leftChild,n+1);//遍歷打印右子樹 } /* //查找數據元素 BiTreeNode *Search(BiTreeNode *root,DataType x){ //查找數據元素x是否在二叉樹root中 //查找到則返回該節點指針,未查找到則返回空指針 BiTreeNode *find=NULL; if(root!=NULL){ if(root->data==x){ find=root; }else{ find=Search(root->leftChild,x);//在左子樹中找 if(find==NULL){ find=Search(root->rightChild,x);//在右子樹中找 } } } return find;//返回查找標誌 } */ int Search(BiTreeNode *root,DataType item){ //在二叉排序樹root上查找數據元素item是否存在 //查找成功,則返回1。不然返回0 BiTreeNode *p; if(root!=NULL){ p=root; while(p!=NULL){ if(p->data.key==item.key){ return 1;//查找成功 } if(item.key>p->data.key){ p=p->rightChild; }else{ p=p->leftChild; } } } return 0;//查找失敗 } //二叉樹的插入算法 //二叉樹的插入操做。要求首先查找數據元素是否已在二叉排序樹中存在。若一存在,則不插入。 //若不存在,則把該數據元素插入到二叉排序樹上查找失敗時結點的左孩子或右孩子 int Insert(BiTreeNode **root,DataType item){ //在二叉排序樹root中查找數據元素item是否存在,若存在,則返回0 //不然。把item結點插入到當前結點左孩子指針或右孩子指針上並返回1 BiTreeNode *current,*parent=NULL,*p; current=*root; while(current!=NULL){ if(current->data.key==item.key){ return 0;//數據元素已存在 } parent=current; if(current->data.key<item.key){ current=current->rightChild; }else{ current=current->leftChild; } } p=(BiTreeNode *)malloc(sizeof(BiTreeNode)); //生成新結點 p->data=item; p->leftChild=NULL; p->rightChild=NULL; if(parent==NULL){ *root=p;//新結點成爲根結點 }else if(item.key<parent->data.key){ parent->leftChild=p;//新結點成爲該結點的孩子結點 }else{ parent->rightChild=p;//新結點成爲該結點的右孩子結點 } return 1; } void InTraverse(BiTreeNode *root){ //中序遍歷二叉樹root,並在遍歷的過程當中輸出結點數據元素值 //結點數據元素設定爲int類型 if(root==NULL){ return ; } if(root->leftChild!=NULL){ InTraverse(root->leftChild); } printf("%d ",root->data.key); if(root->rightChild!=NULL){ InTraverse(root->rightChild); } } /* 1.平衡二叉樹的基本概念:平衡二叉樹或者是一棵空樹,或者是具備這樣性質的二叉排序樹:它的左子樹和右子樹都是平衡二叉樹, 並且左子樹和右子樹的深度之差的絕對值不超過1. 2.構造平衡二叉樹的基本方法是。在構造二叉排序樹的基礎上,假設插入了一個新結點後。使二叉樹中某個結點的左子樹和右子樹 的深度之差的絕對值超過1。則調整對應的二叉樹,使二叉樹中該結點的左子樹和右子樹的深度之差的絕對值不超過1. 3.B_樹是一種平衡多叉排序樹。平衡是指所有葉結點都在同一層上,從而可避免出現二叉排序樹那樣的分支退貨現象。多叉事指多於 二叉的排序樹將下降二叉樹高度,從而下降查找數據元素的比較次數。所以。B_樹是一種動態查找效率較二叉排序樹更高的樹形結構。.net
*/ /* 1.哈希表是一種既適合用於靜態查找問題。有適用於動態查找問題,並且查找效率很高的解決查找問題的存儲結構。設計
2.構造哈希表的方法:設要存儲的數據元素個數爲n。設置一個長度爲m(m>=n)的連續存儲內存單元。分別以每個數據元素的keywordki 爲(0<=i<=n-1)爲自變量,經過一個稱爲哈希函數的函數h(ki),把ki映射爲內存單元的某個地址h(ki),並把該數據元素存儲在這個內存單元中。從 數學角度的觀點看,哈希函數h(ki)其實是keywordki到內存單元的映射,所以,h(ki)也稱爲散列表,哈希表也稱散列表。 3.哈希函數構造方法: 1.除留餘數法: 哈希函數h(k)=kmodm,k數據元素keyword,m哈希表長度,裝填因子=n/m(取值範圍爲0.6--0.9之間),m=1.1n-1.7n之間的素數。 2.直接取地址法: h(k)=k+c,c爲某個數值常量 3.數字分析法 4.哈希衝突解決方法 1.開發地址法 (1)線性探索查法(easy產生堆積問題) 線性探查法的數學遞推公式爲: d0=h(k) di=(d(i-1)+1)mod m(1<=i<=m-1) (2)平方探查法(探查跨步比較大。可避免出現堆積問題) 平方探查法的數形遞推公式爲: d0=h(k) di=(d(i-1)+2^(i-1))mod m (a<=i<=m-1) (3)僞隨機數法(探查跨步是隨機的。可避免出現堆積問題) 僞隨機數法的遞推公式爲: d0=h(k) di=(d(i-1)+R)mod m(1<=i<=m-1) ,R能夠取一個僞隨機序列 2.鏈表法 鏈表法解決哈希衝突的基本思想是:假設沒有發生哈希衝突,則直接存放該數據元素;假設 發生了哈希衝突,則把發生哈希衝突的數據元素另外存放在某個單鏈表中。 用鏈表法解決哈希衝突一般有兩種方法:第一種方法是爲發生哈希衝突的不一樣的同義詞創建不一樣的單鏈表。 另一種方法是爲發生哈希衝突的所有同義詞創建單鏈表。 */ /* 1.哈希表設計要求: 1.哈希函數採用除留餘數法,解決哈希衝突採用開放地址法的線性探查法。 2.設計函數表構造頭文件。指針
頭文件包含告終點結構體定義。以及哈希表初始化,哈希表元素插入。哈希表元素刪除 ,哈希表查找和哈希表撤銷函數。
*/ typedef enum{ Empty2,Active2,Deleted2 }KindOfItem;//表項狀態的枚舉類型 typedef struct{ DataType data; KindOfItem info; }HashItem;//表項結構體 typedef struct{ HashItem *ht;//哈希表數組 int tableSize;//數組最大個數 int currentSize;//當前表項個數 }HashTable;//哈希表結構體 //哈希表操做初始化 int Initiate(HashTable *hash,int mSize){ //初始化函數 hash->tableSize=mSize; hash->ht=(HashItem*)malloc(sizeof(HashItem)*mSize); if(hash->ht==NULL){ return 0; }else{ hash->currentSize=0;return 1; } } //查找函數 int Find(HashTable *hash,DataType x){ //返回數據元素x的哈希地址 //查找成功,則返回大於等於0,其返回值爲數據元素x在哈希表中的位置 //查找失敗。則返回小於0。其返回值爲數據元素x的哈希地址的負值 int i=x.key%hash->tableSize; int j=i; while(hash->ht[j].info==Active2&&hash->ht[j].data.key!=x.key){ //說明存在衝突 j=(j+1)%hash->tableSize;//哈希衝突函數繼續查找 if(j==i){//說明已遍歷整個哈希表未找到且表已滿 return -hash->tableSize; } } if(hash->ht[j].info==Active2){ return j;//找到,返回正值 }else{ return -j;//未找到。返回負值 } } //插入函數 int Insert(HashTable *hash,DataType x){ //把數據元素x插入到哈希表hash中 int i=Find(hash,x);//調用Find函數 if(i>=0){ return 0;//數據元素x已存在 }else if(i!=-hash->tableSize){//數據元素不存在且哈希表未滿 hash->ht[-i].data=x;//數據元素賦值 hash->ht[-i].info=Active2;//置活動標誌 hash->currentSize++;//當前表項個數加1 return 1;//返回插入成功 }else{ return 0;//返回插入失敗 } } //刪除函數 int Delete(HashTable *hash,DataType x){ //刪除哈希表hash中的數據元素x int i=Find(hash,x);//調用Find函數 if(i>=0){//查找到 hash->ht[i].info=Deleted2;//置刪除標誌 hash->currentSize--;//當前表項個數減1 return 1;//返回刪除成功 }else{ return 0;//返回刪除失敗 } } //撤銷函數 void Destroy(HashTable *hash){ //釋放哈希表hash佔用的動態存儲空間 free(hash->ht); } //主函數 int main(){ SeqList myS={{710,342,45,686,6,841,429,134,68,264},10}; DataType x={686}; int i; if((i=SeqSearch(myS,x))!=-1){ printf("該數據元素位置爲%d\n",i); }else{ printf("查找失敗!!!\n"); } //二叉排序樹查找 DataType test[]={4,5,7,2,1,9,8,11,3},x2={9}; int n=9,s; BiTreeNode *root=NULL; for(i=0;i<n;i++){ Insert(&root,test[i]); } InTraverse(root); printf("\n"); s=Search(root,x2); if(s==1){ printf("\n數據元素%d存在\n",x2.key); }else{ printf("\n數據元素不存在\n"); } //哈希查找操做 printf("\n哈希操做:\n"); HashTable myhash; DataType a[]={180,750,600,430,541,900,460},item={430}; int j,k,m=13; Initiate(&myhash,m); for(i=0;i<7;i++){ Insert(&myhash,a[i]); } for(i=0;i<7;i++){ j=Find(&myhash,a[i]); printf("%d ht[]=%d\n",j,myhash.ht[j].data.key); } k=Find(&myhash,item); if(k>=0){ printf("查找成功。元素%d的哈希地址爲%d\n",item.key,k); }else{ printf("查找失敗!!!\n"); } Delete(&myhash,item); k=Find(&myhash,item); if(k>=0){ printf("查找成功,元素%d的哈希地址爲%d\n",item.key,k); }else{ printf("查找失敗!!!\n"); } Destroy(&myhash); return 0; }
執行結果: