编程珠玑 第十三章 搜索

从上一章的问题:生成[0, maxval]范围内m个随机整数的有序序列,不允许重复。实现伪代码:

initialize set S to empty
size = 0
while size < m do
  t = bigrand() %d maxval
  if t is not in S
    insert t into S
    size ++
print the elements of S in sorted order

将生成的数据结构称为IntSet,指整数集合S。接口定义如下:

 1 //Intset.h 接口定义头文件
 2 typedef int bool;
 3 #define true 0
 4 #define false 1
 5 void intset_init(int maxelements, int maxval);
 6 void insert(int t);
 7 bool isinset(int t);
 8 int getsize();
 9 void report(int *v);
10 void destory_set();

int_set:初始化函数函数,参数 maxelements和maxval分别表示集合元素最大个数和集合中元素值的最大值,特定实现中可以忽略其中之一或两个都忽略;

insert:向集合中添加一个整数(前提需要先判断集合是否有已存在该值);

isinset:判断集合中是否存在元素t;

getsize:获得集合中元素数目;

report:把集合中的元素拷贝到v指向的内存区域;

destory_set: 销毁集合,释放集合所占用的空间;

2. 集合实现

以下是采用不同的数据结构实现的集合操作:

(1)使用整数数组实现集合。

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <assert.h>
 4 #include <malloc.h>
 5 #include "Intset.h"
 6 
 7 
 8 static int *set = NULL;
 9 static int size;
10 //集合初始化,
11 //maxelements 为集合最大元素数目 
12 //maxval为元素最大值
13 void intset_init(int maxelements, int maxval)
14 {
15     set = (int *)malloc((maxelements + 1)*sizeof(int));
16     assert(set != NULL);
17     size = 0;
18     //哨兵元素,比元素最大值大1
19     set[0] = maxval + 1;
20 }
21 
22 //二分查找,集合set已包含t,则返回位置;否则,返回-1;
23 int search(int t)
24 {
25     int l = 0, u = size - 1, p = -1,  m;
26     m = (l + u) / 2;
27     //printf("l=%d, u=%d\n", l, u);
28     while(l < u)
29     {
30         //printf("l=%d, u=%d\n", l, u);
31         if( set[m] > t)
32             u = m - 1;
33         else if ( set[m] < t)
34             l = m + 1;
35         else 
36         {
37             p = m;
38             break;
39         }
40         m = (l + u) / 2;
41     }
42     return p;
43 }
44 //判断元素t是否在集合t
45 bool isinset(int t)
46 {
47     if( search(t) == -1)
48     {
49         //printf("%d doesn't exist in set\n", t);
50         return false;
51     }
52     else    
53     {
54         //printf("%d exist in set\n", t);
55         return true;
56         
57     }
58 }
59 //把元素t插入到集合set中
60 void insert(int t)
61 {
62     assert(set != NULL);
63     int i, j;
64     for(i= 0; set[i] < t; i ++)
65         ;
66     if(set[i] == t)
67         return ;
68     for(j = size; j > i; j --)
69         set[j] = set[j - 1];
70     set[i] = t;
71     size ++;
72     return ;
73 }
74 
75 //获得集合set的大小
76 int getsize()
77 {
78     return size;
79 }
80 
81 //集合set拷贝到数组v
82 void report(int *v)
83 {
84     int i;
85     for(i = 0; i < size; i ++)
86     {
87         v[i] = set[i];
88     }
89 }
90 
91 //删除集合set
92 void destory_set()
93 {
94     if(set != NULL)
95     {
96         free(set);
97         set = NULL;
98     }
99 }

(2)链表实现

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <assert.h>
  4 #include <malloc.h>
  5 #include "Intset.h"
  6 
  7 
  8 static int *set = NULL;
  9 static int size;
 10 typedef struct node *pnode; 
 11 typedef struct node{
 12     int val;
 13     pnode next;
 14     
 15 }node; 
 16 
 17 static pnode head = NULL;//头部节点
 18 static pnode sentinel = NULL;//哨兵节点
 19 
 20 //集合初始化,
 21 //maxelements 为集合最大元素数目 
 22 //maxval为元素最大值
 23 void intset_init(int maxelements, int maxval)
 24 {
 25     
 26     if((head = sentinel = (pnode)malloc(sizeof(node))) != NULL)
 27     {
 28         sentinel->val = maxval;
 29         sentinel->next = NULL;
 30         size = 0;
 31     }
 32     else 
 33     {
 34         printf("intset_init failed to malloc\n");
 35         exit(1);
 36     }
 37 }
 38 
 39 //判断元素t是否在集合t
 40 bool isinset(int t)
 41 {
 42     pnode p = head;
 43     while(p != sentinel)
 44     {
 45         if(p->val == t)
 46             return true;
 47         else
 48             p = p->next;
 49     }
 50     return false;
 51 }
 52 
 53 //递归插入元素t
 54 pnode rinsert(pnode p, int t)
 55 {
 56     pnode pt;
 57     if(p->val < t)
 58     {
 59         p->next = rinsert(p->next, t);
 60     }
 61     else
 62     {
 63         pt = (pnode)malloc(sizeof(node));
 64         pt->val = t;
 65         pt->next = p;
 66         size ++;
 67         p = pt;
 68     }
 69     return p;
 70 }
 71 
 72 //把元素t插入到集合set中
 73 void insert(int t)
 74 {
 75     head = rinsert(head, t);
 76     return;
 77     
 78 }
 79 
 80 //非递归方法把元素t插入到集合set中
 81 void insert_norec(int t)
 82 {
 83     pnode p = head, q = NULL, new;
 84     
 85     while(p->val < t)//最后遇到大于t的元素或遇到哨兵节点
 86     {
 87         q = p;
 88         p = q->next;
 89     }
 90     
 91     new = (pnode)malloc(sizeof(node));
 92     new->val = t;
 93     new->next = p;
 94     
 95     if(q == NULL)//插入到head头部节点
 96     {
 97         head = new;
 98     }
 99     else
100     {
101         q->next = new;
102     }
103     size ++;
104     return;
105 }
106 
107 
108 //获得集合set的大小
109 int getsize()
110 {
111     return size;
112 }
113 
114 //集合set拷贝到数组v
115 void report(int *v)
116 {
117     int i=0;
118     pnode p = head;
119     while(p != sentinel)
120     {
121         v[i ++] = p->val;
122         p = p->next;
123     }
124 }
125 
126 //删除集合set
127 void destory_set()
128 {
129     pnode p = head, q;
130     if(p != NULL)
131     {
132         q = p->next;
133         free(p);
134         p = q;
135     }
136 }

(4)二叉树实现

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <assert.h>
  4 #include <malloc.h>
  5 #include "Intset.h"
  6 typedef struct node *pnode; 
  7 typedef struct node{
  8     int val;
  9     pnode left;//左子树指针
 10     pnode right;//右子树指针
 11 }node; 
 12 //二叉树根节点
 13 static pnode root;
 14 static size;
 15 
 16 
 17 //集合初始化,
 18 //maxelements 为集合最大元素数目 
 19 //maxval为元素最大值
 20 void intset_init(int maxelements, int maxval)
 21 {
 22     root = NULL;
 23     size = 0;
 24 }
 25 //递归插入节点t
 26 pnode rinsert(pnode p, int t)
 27 {
 28     if(p == NULL)
 29     {
 30         p = (pnode)malloc(sizeof(node));
 31         p->val = t;
 32         p->left = p->right = NULL;
 33         size ++;
 34     }
 35     else if( p->val > t)
 36     {
 37         p->left = rinsert(p->left, t);
 38     }
 39     else if(p->val < t)
 40     {
 41         p->right = rinsert(p->right, t);
 42     }
 43     return p;
 44 }
 45 //把元素t插入到集合set中
 46 void insert(int t)
 47 {
 48     root = rinsert(root, t);
 49 }
 50 
 51 //判断元素t是否在集合t
 52 bool isinset(int t)
 53 {
 54     pnode p = root;
 55     while(p != NULL)
 56     {
 57         if(p->val > t)
 58             p = p->left;
 59         else if(p->val < t)
 60             p = p->right;
 61         else
 62             return true;
 63     }
 64     return false;
 65 }
 66 int getsize()
 67 {
 68     return size;
 69 }
 70 //中序遍历二叉树
 71 void mid_traverse(pnode p, int *v)
 72 {
 73     static int index = 0;
 74     if(p == NULL)
 75         return;
 76     if(p == root)
 77         index = 0;
 78     mid_traverse(p->left, v);
 79     v[index ++] = p->val;
 80     mid_traverse(p->right, v);
 81     
 82 }
 83 
 84 //集合set拷贝到数组v
 85 void report(int *v)
 86 {
 87     mid_traverse(root, v);
 88 }
 89 
 90 //递归删除节点
 91 void destory_node(pnode p)
 92 {
 93     if(p == NULL)
 94         return;
 95     else if(p->left != NULL)
 96     {
 97         destory_node(p->left);
 98     }
 99     else if(p->right != NULL)
100     {
101         destory_node(p->right);
102     }
103     free(p);
104 }
105 
106 //删除集合set
107 void destory_set()
108 {
109     destory_node(root);
110     root = NULL;
111 }

(4)位图实现(参考编程珠玑第1章)

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <memory.h>
 4 #include "Intset.h"
 5 #define BITSPERWORD 32
 6 #define SHIFT 5
 7 #define MASK 0x1F
 8 static unsigned int *bitmap;//位图指针
 9 static unsigned int size = 0;//集合元素数目
10 static unsigned int max = 0;//集合中元素的最大值+1
11 // 数字k所对应位图的位置置为1
12 void set_bit(unsigned int k)
13 {
14     unsigned int byte_position;
15     unsigned short bit_position;
16     byte_position = k >> 5;//byte_position表示k在位图中的字节
17     bit_position = k & 0x1F;//bit_position表示k所在字节的位
18     /*printf("byte = %d    bit = %d\n", byte_position, bit_position);*/
19     bitmap[byte_position] = bitmap[byte_position] | (1 << bit_position);
20 }
21 
22 //数字k所对应位图的位置清零
23 void clear_bit(unsigned int k)
24 {
25     unsigned int byte_position;
26     unsigned short bit_position;
27     byte_position = k >> 5;
28     bit_position = k & 0x1F;
29     /*printf("byte = %d    bit = %d\n", byte_position, bit_position);*/
30     bitmap[byte_position] = bitmap[byte_position] & ~(1 << bit_position);
31 }
32 
33 //测试数字k所对应位图的位置为1或为0
34 bool test_bit(unsigned int k)
35 {
36     unsigned int byte_position;
37     unsigned short bit_position;
38     byte_position = k >> 5;
39     bit_position = k & 0x1F;
40     /*printf("byte = %d    bit = %d\n", byte_position, bit_position);*/
41     return bitmap[byte_position] & (1 << bit_position);
42 }
43 
44 
45 //初始化位图
46 void intset_init(int maxelements, int maxval)
47 {
48     unsigned int size = 1 + maxval/BITSPERWORD, i;
49     max = maxval;
50     printf("maxval = %u    size = %u\n", maxval, size);
51     bitmap = (unsigned int *)malloc(size*sizeof(unsigned int));
52     if(bitmap == NULL)
53     {
54         printf("Failed to malloc\n");
55         return ;
56     }
57     memset(bitmap, 0, size*sizeof(unsigned int));
58     size = 0;
59     return ;
60 }
61 
62 void insert(int t)
63 {
64     set_bit(t);
65     size ++;
66 }
67 
68 bool isinset(int t)
69 {
70     if( test_bit(t) == 0)
71         return false;
72     else
73         return true;
74 }
75 
76 int getsize()
77 {
78     return size;
79 }
80 
81 void report(int *v)
82 {
83     int i = 0, j = 0;
84     for(i = 0; i < max; i ++)
85     {
86         if(isinset(i) == true)
87             v[j ++] = i;
88     }    
89     return;
90 }
91 
92 void destory_set()
93 {
94     if(bitmap != NULL)
95     {
96         free(bitmap);
97         bitmap = NULL;
98     }
99 }

3.测试主函数实现

 1 #include <stdlib.h>
 2 #include <stdio.h>
 3 #include <assert.h>
 4 #include "Intset.h"
 5 //产生区间[lower upper]内的随机数,参考编程珠玑第8章习题
 6 int randInt(lower, upper)
 7 {
 8     assert( lower < upper);
 9     return rand()%(upper - lower + 1) + lower;
10 }
11 //打印集合中的元素
12 void print(int arry[], int length)
13 {
14     int i;
15     printf("the set is \n");
16     for(i = 0; i < length; i ++)
17         printf("%d\t", arry[i]);
18     printf("\n");
19 }
20 //调用程序需要输入三个参数 count:集合数目 lower:集合下限 upper:集合上限
21 int main(int argc, char *argv[])
22 {
23 
24     int count = 0, i, lower, upper, *v;
25     if(argc != 4)
26     {
27         printf("USAGE:%s count lower upper \n", argv[0]);
28         return 1;
29     }
30     count = atoi(argv[1]);
31     lower = atoi(argv[2]);
32     upper = atoi(argv[3]);
33     printf("lower=%d upper=%d count=%d\n", lower, upper, count);
34     v = (int *)malloc(count * sizeof(int));
35     intset_init(count , upper + 1);
36     i = count;
37     while(i > 0)
38     {
39         int t = randInt(lower, upper);
40         printf("rand=%d\n", t);
41         if( isinset(t) == false)//判断集合是否已存在该元素
42         {
43             insert(t);
44             i --;
45         }
46     
47     }
48     report(v);
49     destory_set();
50     print(v, count);
51     return 0;
52 }

4.以上各种数据结构实现集合操作的复杂度

 
集合表示

    O(每个操作的时间)

初始化    insert      report

总时间  空间

有序数组

有序链表

二叉树

位向量

1      m        m

1      m        m

1      logm       m

m       1            1

n      1             n

O(m^2)  m

O(m^2)  2m

O(mlogm) 3m

O(m)   3m

O(n)    n/b

原文地址:https://www.cnblogs.com/bigrabbit/p/2671125.html