索引查找:
索引查找是在索引表和主表(即线性表的索引存储结构)上进行的查找,但需要先建立索引表,索引表就类似图书的目录,能大提高查找效率。
给顺序表创建索引表:
typedef struct Stduent { int id; char name[20]; char sex; short age; float score; }Student; Student stu[10000]; // 主表 // 索引表元素结构 typedef struct Index { int id; void* addr; }Index; // 索引表 Index indexs[10000]; // 对主表与索引表之间建立索引 void create_index(Student* stu,Index* indexs,size_t len) { for(int i=0; i<len; i++) { indexs[i].id = stu[i].id; indexs[i].addr = stu+i; } } // 注意:建立索引表后,后面使用索引查找的是索引表,再通过找到的索引表中记录的主表元素的位置信息,来最终找到待查找的数据元素 // 注意:索引表如何建立索引,根据实际需求来选择
索引表的顺序查找:
// 它比直接查询数据的主表的跳转速度要快,如果使用stu[i]查找,i每加1,要跳转过sizeof(Student)个字节数,如果使用索引表indexs[i]查询,i每加1,则只需要跳转sizeof(Index)个字节数,比主表跳转字节数要少。 // 如果主表的数据不存在内存而是存储在磁盘上,而索引表是建立在内存中,通过索引表访问效率、速度远高于访问磁盘的主表 int order_index_search(Index* indexs,size_t len,int id) { for(int i=0; i<len; i++) { if(indexs[i].id == id) return i; } /* for(int i=0; i<len; i++) { if(stu[i].id == id) return i; } */ }
索引表二分查找:
// 需要对索引表先排序 // 对索引表排序的效率和速度要远比直接对主表的排序要快 void sort_index(Index* indexs,size_t len) { for(int i=0; i<len-1; i++) { int min = i; for(int j=i+1; j<len; j++) { if(indexs[j].id < indexs[min].id) min = j; } if(min != i) { Index temp = indexs[min]; indexs[min] = indexs[i]; indexs[i] = temp; } } } // 对索引表进行二分查找 // 因为索引表已经重新排序了,而主表没有排序过,所以不能返回在索引表中找到元素的下标,该下标与主表对应元素的下标很可能不一致了,所以需要直接返回主表对应元素的地址 Student* binary_index_search(Index* indexs,size_t len,int id) { int l = 0, r = len-1; while(l <= r) { int p = (l + r)/2; if(indexs[p].id == id) return indexs[p].addr; if(id < indexs[p].id) r = p-1; if(id > indexs[p].id) l = p+1; } return NULL; }
给链表创建索引表:
// 给链表head创建一张顺序的索引表 表中的元素是ListNode* 用来指向链表中的节点 // 返回值是返回索引表首地址,len_i输出型参数,返回索引表中元素的个数 ListNode** create_index_list(ListNode* head,size_t* len_i) { if(NULL == head || NULL == len_i) return NULL; // 索引表的长度 *len_i = 0; ListNode** indexs = NULL; // 遍历链表head 给每个节点建立普通索引 for(ListNode* n=head; NULL!=n; n=n->next) { // 申请索引表元素ListNode*的内存 indexs = realloc(indexs,sizeof(ListNode*)*(*len_i+1)); // 让索引表中的最后一个元素指向对应的链表节点 indexs[(*len_i)++] = n; } // 对索引表进行排序,交换索引表中指针的指向 for(int i=0; i<(*len_i)-1; i++) { int min = i; for(int j=i+1; j<*len_i; j++) { if(indexs[j]->data < indexs[min]->data) min = j; } if(min != i) { // 交换索引表中指针的指向 不修改链表 ListNode* temp = indexs[min]; indexs[min] = indexs[i]; indexs[i] = temp; } } } // 链表的二分查找,本质上是对顺序的索引表进行二分 int binary_list_index_search(ListNode** indexs,size_t len,int key) { int l = 0, r = len - 1; while(l <= r) { int p = (l + r)/2; if(indexs[p]->data == key) return p; if(key < indexs[p]) r = p-1; if(key > indexs[p]) l = p+1; } return -1; }
索引查找的优点:
-
对于顺序表的顺序查找,索引查找可以缩短数据的查找跳转范围
-
对于顺序表的二分查找,通过排序索引表也能提高排序的速度
-
对于链式表,可以先建立顺序的索引表后,进行之前无法实现的二分查找了
索引查找的缺点:
-
使用了额外的存储空间来创建索引表,是一种典型的以空间换时间的算法策略
索引查找的使用场景:
-
如果针对的是内存中的顺序表中的数据,通过索引查找提升的速度和效率其实并不是很明显,毕竟内存的运算速度很快
-
但是对于存储在机械硬盘上的数据,通过在内存中建立对应硬盘数据的索引表,访问起来提升的效率就很多了,因此在一些常用的数据库中的索引查找使用非常多