Fork me on GitHub

数据结构与算法——查找算法

查找

又称检索或查询,是指在查找表中找出满足一定条件的结点或记录对应的操作。

查找表

在计算机中,是指被查找的数据对象是由同一类型的记录构成的集合,如顺序表, 链表、二叉树和哈希表等

查找效率

查找算法中的基本运算是通过记录的关键字与给定值进行比较,所以查找的效率 同常取决于比较所花的时间,而时间取决于比较的次数。通常以关键字与给定值进行比较的记录 个数的平均值来计算。

查找操作及分类

操作

  1. 查找某个“特定的”数据元素是否存在在查找表中
  2. 某个“特定的”数据元素的各种属性
  3. 在查找表中插入一个数据元素
  4. 从查找表中删除某个数据元素

分类

  • 若对查找表只进行(1) 或(2)两种操作,则称此类查找表为静态查找表。
  • 若在查找过程中同时插入查找表中存在的数据元素,或者从查找表中删除已存在的 某个数据元素,则称此类查找表为动态查找表。

 

1. 数组和索引

日常生活中,经常会在电话号码簿中查阅“某人”的电话号码,按姓查询或者按字母排 序查询;

在字典中查阅“某个词”的读音和含义等等。

这里,“电话号码簿” 和 “字典” 都可 看作一张查找表, 而按 “姓” 或者 “字母” 查询则是按索引查询!

索引把线性表分成若干块,每一块中的元素存储顺序是任意的,但是块与块间必须按关键字 大小按顺序排列。即前一块中的最大关键字值小于后一块中的最小关键字值。

分块以后,为了快速定义块,还需要建立一个索引表,索引表中的一项对应于线性表中的一 块,索引项由键域和链域组成。键域存放相应关键字的键值,链域存放指向本块第一个节点和最 后一个节点的指针,索引表按关键字由小到大的顺序排列!

 

数组是特殊的块索引(一个块一个元素):

 

哈希表是非常经典的块索引:

 分块查找的算法分两步进行,首先确定所查找的节点属于哪一块,即在索引表中查找其所在的块, 然后在块内查找待查询的数据。由于索引表是递增有序的,可采用二分查找,而块内元素是无序 的,只能采用顺序查找。(块内元素较少,则不会对执行速度有太大的影响)

 

2. 二分查找

二分查找法实质上是不断地将有序数据集进行对半分割,并检查每个分区的中间元素。再重 复根据中间数确定目标范围并递归实行对半分割,直到中间数等于待查找的值或是目标数不在搜 索范围之内!

 1 #include <stdlib.h>
 2 #include <stdio.h>
 3 
 4 int int_compare(const void *key1, const void *key2)
 5 {
 6     const int *ch1 = (const int *)key1;
 7     const int *ch2 = (const int *)key2;
 8     return (*ch1-*ch2);
 9 }
10 
11 int char_compare(const void *key1, const void *key2)
12 {
13     const char *ch1 = (const char *)key1;
14     const char *ch2 = (const char *)key2;
15     return (*ch1-*ch2);
16 }
17 
18 int BinarySearch(void *sorted, int len, int elemSize, void *search, int(*compare)(const void *key1, const void *key2))
19 {
20     int left = 0, right = 0, middle = 0;
21     /*初始化 left 和 right 为边界值*/
22     left = 0;
23     right = len - 1;
24     /*循环查找,直到左右两个边界重合*/
25     
26     while(left <= right)
27     {
28         int ret = 0;
29         middle = (left + right) /2 ;
30         ret = compare((char *)sorted+(elemSize*middle), search);
31         
32         if(ret == 0)
33         {
34             /*middle 等于目标值*/
35             /*返回目标的索引值 middle*/
36             return middle;
37         }
38         else if( ret > 0)
39         {
40             /*middle 大于目标值*/
41             /*移动到 middle 的左半区查找*/
42             right = middle - 1;
43         }
44         else 
45         {
46             /*middle 小于目标值*/
47             /*移动到 middle 的右半区查找*/
48             left = middle + 1;
49         }
50     }
51     return -1;
52 }
53 
54 int main(void)
55 {
56     int arr[]={1, 3, 7, 9, 11};
57     int search[] = {-1, 0, 1, 7 , 2, 11, 12};
58     printf("整数查找测试开始。。。\n");
59     
60     for(int i=0; i<sizeof(search)/sizeof(search[0]); i++)
61     {
62         int index = BinarySearch(arr, sizeof(arr)/sizeof(arr[0]),
63         sizeof(int), &search[i], int_compare);
64         printf("searching %d, index: %d\n",search[i], index);
65     }
66     
67     char arr1[]={'a','c','d','f','j'};
68     char search1[] = {'0', 'a', 'e', 'j' , 'z'};
69     printf("\n 字符查找测试开始。。。\n");
70     
71     for(int i=0; i<sizeof(search1)/sizeof(search1[0]); i++)
72     {
73         int index = BinarySearch(arr1, sizeof(arr1)/sizeof(arr1[0]),
74         sizeof(char), &search1[i], char_compare);
75         printf("searching %c, index: %d\n",search1[i], index);
76     }
77     
78     system("pause");
79     
80     return 0;
81 }

 

 

3. 穷举搜索

有 20 枚硬币,可能包括 4 种类型:1 元、5 角、1 角和 5 分。

已知 20 枚硬币的总价值为 10 元,求各种硬币的数量。

例如:4、11、5、0 就是一种方案。而 8、2、10、 0 是另一个可能的方案,显然方案并不是 唯一的,请编写程序求出类似这样的不同的方案一共有多少种?

(1)编程思路。 直接对四种类型的硬币的个数进行穷举。其中,1 元最多 10 枚、5 角最多 20 枚、1 角最多 20 枚、5 分最多 20 枚。

如果以元为单位,则 5 角、1 角、5 分会化成浮点型数据,容易计算出错。可以将 1 元、5 角、1 角、5 分变成 100 分、50 分、10 分和 5 分,从而全部采用整型数据处理。

 1 #include <iostream>
 2 
 3 using namespace std;
 4 
 5 int main(void)
 6 {
 7     int a100 = 0;            //1 元的硬币数量
 8     int a50 = 0;            //5 角的硬币数量
 9     int a10 = 0;            //1 角的硬币数量
10     int a5 = 0;                //5 分的硬币数量
11     int cnt = 0;            //记录可行的方案的种数
12     for(a100=0; a100<=10; a100++)
13     {
14         for(a50=0; a50<=20; a50++)
15         {
16             for(a10=0; a10<=20; a10++)
17             {
18                 for(a5=0; a5<=20; a5++)
19                 {
20                     if((a100*100 + a50*50 + a10*10 + a5*5)==1000 && (a100 + a50 + a10 + a5)==20)
21                     {
22                         cout<<a100<<" , "<<a50<<" , "<<a10<<" ,    "<<a5<<endl;
23                         cnt++;
24                     }
25                 }//a5 end.
26             }//a10 end.
27         }//a50 end.
28     }//a100 end.
29     
30     cout<<"可行的解决方案总共有: "<<cnt<<endl;
31     
32     system("pause");
33     
34     return 0;
35 }

穷举法(枚举法)的基本思想是:列举出所有可能的情况,逐个判断有哪些是符合问题所要求 的条件,从而得到问题的全部解答。 它利用计算机运算速度快、精确度高的特点,对要解决问题的所有可能情况,一个不漏地进行检 查,从中找出符合要求的答案。

穷举算法解决问题,通常可以从两个方面进行分析:

(1)问题所涉及的情况:问题所涉及的情况有哪些,情况的种数必须可以确定。把它描述 出来。应用穷举时对问题所涉及的有限种情形必须一一列举,既不能重复,也不能遗漏。重复列 举直接引发增解,影响解的准确性;而列举的遗漏可能导致问题解的遗漏。 (2)答案需要满足的条件:分析出来的这些情况,需要满足什么条件,才成为问题的答案。 把这些条件描述出来。

 

 

4. 并行搜索

假设我们要从很大的一个无序的数据集中进行搜索,假设我们的机器可以一次性容纳这么多 数据。从理论上讲,对于无序数据,如果不考虑排序,已经很难从算法层面优化了。而利用并行处理思想,我们可以很轻松地将检索效率提升多倍。具体实现思路如下:

将数据分成 N 个块,每个块由一个 线程来并行搜索。

 

线程演示代码:

 1 #include <Windows.h>
 2 #include <stdio.h>
 3 #include <iostream>
 4 #include <time.h>
 5 
 6 #define TEST_SIZE (1024*1024*200)
 7 #define NUMBER 20
 8 
 9 DWORD WINAPI ThreadProc(void *lpParam)
10 {
11     for(int i=0; i<5; i++)
12     {
13         printf("进程老爸,我来了!\n");
14         Sleep(1000);
15     }
16     return 0;
17 }
18 
19 int main(void)
20 {
21     DWORD threadID1;//线程 1 的身份证
22     HANDLE hThread1;//线程 1 的句柄
23     DWORD threadID2;//线程 2 的身份证
24     HANDLE hThread2;//线程 2 的句柄
25     
26     printf("创建线程... ... \n");
27     
28     //创建线程 1
29     hThread1 = CreateThread(NULL, 0, ThreadProc, NULL, 0, &threadID1);
30     
31     //创建线程 2
32     hThread2 = CreateThread(NULL, 0, ThreadProc, NULL, 0, &threadID2);
33     
34     WaitForSingleObject(hThread1, INFINITE);
35     WaitForSingleObject(hThread2, INFINITE);
36     
37     printf("进程老爸欢迎线程归来!\n");
38     
39     system("pause");
40     
41     return 0;
42 }

 

完整代码:

  1 #include <Windows.h>
  2 #include <stdio.h>
  3 #include <iostream>
  4 #include <time.h>
  5 
  6 #define TEST_SIZE (1024*1024*200)
  7 #define NUMBER 20
  8 
  9 typedef struct _search
 10 {
 11     int *data;//搜索的数据集
 12     size_t start; //搜索的开始位置
 13     size_t end; //搜索的终止位置
 14     size_t count; //搜索结果
 15 }search;
 16 
 17 DWORD WINAPI ThreadProc(void *lpParam)
 18 {
 19     search *s = (search*)lpParam;
 20     time_t start, end;
 21     
 22     printf("新的线程开始执行...\n");
 23     
 24     time(&start);
 25     for(int j=0; j<10; j++)
 26     {
 27         for(size_t i=s->start; i<=s->end; i++)
 28         {
 29             if(s->data[i] == NUMBER)
 30             {
 31                 s->count++;
 32             }
 33         }
 34     }
 35     
 36     time(&end);
 37     
 38     printf("查找数据所花时间: %lld\n", end-start);
 39     
 40     return 0;
 41 }
 42 
 43 int main02(void)
 44 {
 45     int *data = NULL;
 46     int count = 0;                    //记录的数量
 47     int mid = 0;
 48     search s1, s2;
 49     data = new int[TEST_SIZE];
 50     
 51     for(int i=0; i<TEST_SIZE; i++)
 52     {
 53         data[i] = i;
 54     }
 55     
 56     mid = TEST_SIZE/2;
 57     s1.data = data;
 58     s1.start = 0;
 59     s1.end = mid;
 60     s1.count = 0;
 61     s2.data = data;
 62     s2.start = mid+1;
 63     s2.end = TEST_SIZE-1;
 64     s2.count = 0;
 65     
 66     DWORD threadID1;//线程 1 的身份证
 67     HANDLE hThread1;//线程 1 的句柄
 68     DWORD threadID2;//线程 2 的身份证
 69     HANDLE hThread2;//线程 2 的句柄
 70     
 71     printf("创建线程... ... \n");
 72     //创建线程 1
 73     hThread1 = CreateThread(NULL, 0, ThreadProc, &s1, 0, &threadID1);
 74     
 75     //创建线程 2
 76     hThread2 = CreateThread(NULL, 0, ThreadProc, &s2, 0, &threadID2);
 77     
 78     WaitForSingleObject(hThread1, INFINITE);
 79     WaitForSingleObject(hThread2, INFINITE);
 80     
 81     printf("进程老爸欢迎线程归来!count: %d\n", s1.count+s2.count);
 82     
 83     system("pause");
 84     
 85     return 0;
 86 }
 87 int main(void)
 88 {
 89     int *data = NULL;
 90     int count = 0;                    //记录的数量
 91     data = new int[TEST_SIZE];
 92     for(int i=0; i<TEST_SIZE; i++)
 93     {
 94         data[i] = i;
 95     }
 96     time_t start=0, end=0;            //记录开始和结束的时间戳
 97     time(&start);
 98     for(int j=0; j<10; j++)
 99     {
100         for(int i=0; i<TEST_SIZE; i++)
101         {
102             if(data[i] == NUMBER)
103             {
104                 count++;
105             }
106         }
107     }
108     
109     time(&end);
110     
111     printf("查找数据所花时间: %lld, count: %d\n", end-start, count);
112     
113     system("pause");
114     
115     return 0;
116 }

 

 

 

 

 

=====================================================================================================================

posted @ 2020-12-12 20:10  索智源  阅读(389)  评论(0)    收藏  举报