近日周立功教授公开了数年的心血之作《程序设计与数据结构》,电子版已无偿性分享到电子工程师与高校群体下载,经周立功教授授权,特对本书内容进行连载。
由于使用迭代器可以轻松地实现指针的前移或后移,因此可以使用迭代器接口实现冒泡排序算法。其函数原型为:
void iter_sort(iterator_if_t *p_if, iterator_t begin, iterator_t end, compare_t compare, swap_t swap) ;
其中,p_if表示算法使用的迭代器接口。begin与end是一对迭代器,表示算法的操作范围,但不一定是容器的首尾迭代器,因此算法可以处理任何范围的数据。为了判定范围的有效性,习惯采用前闭后开范围表示法,即使用begin和end表示的范围为[begin,end),表示范围涵盖bigen到end(不含end)之间的所有元素。当begin==end时,上述所表现的便是一个空范围。
compare同样也是比较函数,但比较的类型发生了变化,用于比较两个迭代器所对应的值。其类型compare_t定义如下:
typedef int (*compare_t)(iterator_t it1, iterator_t it2);
swap函数用于交换两个迭代器所对应的数据,其类型swap_t定义如下:
typedef void (*swap_t)(iterator_t it1, iterator_t it2);
由此可见,接口中只有迭代器,根本没有容器的踪影,从而做到了容器与冒泡排序算法彻底分离,基于迭代器的冒泡排序算法详见程序清单3.56。
程序清单3.56冒泡排序算法函数
1 void iter_sort(iterator_if_t *p_if, iterator_t begin, iterator_t end, compare_t compare, swap_t swap)
2 {
3 int flag = 1; // flag = 1,表示指针的内容未交换
4 iterator_t it1 = begin; // it1指向数组变量的首元素
5 iterator_t it2 = end;
67 iterator_t it_next; // pNext指向p1所指向的元素的下一个元素
8 if (begin == end) { //没有需要算法处理的迭代器
9 return;
10 }
11 iterator_prev(p_if, &it2); // it2指向需要排序的最后一个元素12 while (it2 != begin){
13 it1 = begin;
14 flag = 1;
15 while(it1 != it2){
16 it_next = it1; //暂存
17 iterator_next(p_if, &it_next); // it_next为 it1 的下一个元素
18 if(compare(it1, it_next) > 0){
19 swap(it1, it_next); //交换内容
20 flag = 0; // flag = 0,表示指针的内容已交换
21 }22 it1 = it_next; // it1的下一个元素
23 }
24 if(flag) return; //没有交换,表示已经有序,则直接返回
25 iterator_prev(p_if, &it2); // it2向前移
26 }
27 }下面以一个简单的例子来测试验证基于迭代器的冒泡排序算法,详见程序清单3.57。将整数存放到双向链表中,首先将5、4、3、2、1分别加在链表的尾部,接着调用dlist_foreach()遍历链表,看是否符合预期,然后再调用算法库的iter_sort()排序。当排序完毕后链表的元素应该是从小到大排列的,再次调用算法库的dilst_foreach()遍历链表,看是否符合预期。
程序清单3.57使用双向链表、算法和迭代器
1 #include
2 #include "iterator.h"
3
4 typedef struct _dlist_int{
5 dlist_node_t node; //包含链表结点
6 int data; // int类型数据
7 }dlist_int_t;
8
9 int list_node_process(void *p_arg, dlist_node_t *p_node)
10 {
11 printf("%d ", ((dlist_int_t *)p_node) -> data);12 return 0;
13 }
14
15 static int __compare(iterator_t it1, iterator_t it2)
16 {
17 return ((dlist_int_t *)it1) -> data - ((dlist_int_t *)it2) -> data;
18 }
19
20 static void __swap(iterator_t it1, iterator_t it2)
21 {
22 int data = ((dlist_int_t *)it2) -> data;
23 ((dlist_int_t *)it2) -> data = ((dlist_int_t *)it1) -> data;
24 ((dlist_int_t *)it1) -> data = data;
25 }
2627 int main(int argc, char *argv[])
28 {
29 iterator_if_t iterator_if;
30 dlist_head_t head; //定义链表头结点
31 dlist_int_t node[5]; //定义5个结点空间
32 int i;
3334 dlist_init(&head);
35
36 for (i = 0; i < 5; i++) { // 将5个结点添加至链表尾部
37 node[i].data = 5 - i; //使值的顺序为 5 ~ 1
38 dlist_add_tail(&head, &(node[i].node));
39 }
40 dlist_iterator_if_get(&iterator_if);
4142 printf("\nBefore bubble sort:\n");
43 dlist_foreach (&head, list_node_process, NULL); //打印排序前的情况
44
45 iter_sort(&iterator_if, dlist_begin_get(&head), dlist_end_get(&head),__compare, __swap);
46
47 printf("\nAfter bubble sort:\n");
48 dlist_foreach (&head, list_node_process, NULL); //打印排序后的情况
49 return 0;
50 }在这里,使用了dlist_foreach()遍历函数,既然通过迭代器能够实现冒泡排序,那么也能通过迭代器实现简单的遍历算法,此时遍历算法与具体容器无关。遍历函数的原型如下:
void iter_foreach(iterator_if_t *p_if, iterator_t begin, iterator_t end, visit_t visit, void *p_arg);
其中,p_if表示算法使用的迭代器接口,begin与end表示算法需要处理的迭代器范围,visit是用户自定义的遍历迭代器的函数。其类型visit_t定义如下:
typedef int (*visit_t)(void *p_arg, iterator_t it);
visit_t的参数是p_arg指针和it迭代器,其返回值为int类型的函数指针。每遍历一个结点均会调用visit指向的函数,传递给p_arg的值即为用户参数,其值为iter_foreach()函数的p_arg参数,p_arg的值完全是由用户决定的,传递给it迭代器的值即为指向当前遍历的迭代器,iter_foreach()函数的实现详见程序清单3.58。
程序清单3.58遍历算法函数
1 void iter_foreach(iterator_if_t *p_if, iterator_t begin, iterator_t end, visit_t visit, void *p_arg)
2 {
3 iterator_t it = begin;
4 while(it != end){
5 if (visit(p_arg, it) < 0) { // 若返回值为负值,表明用户终止了遍历6 return;
7 }
8 iterator_next(p_if, &it); //让迭代器向后移动
9 }
10 }现在可以将程序清单3.57中的第43行和第48行中的dlist_foreach()函数修改为使用iter_foreach()函数,看能否得到相同的效果?
如果将数据保存在数组变量中,那么将如何使用已有的冒泡排序算法呢?由于数组也是容器,因此只要实现基于数组的迭代器即可,详见程序清单3.59。
程序清单3.59使用数组实现迭代器接口
1 typedef int element_type_t;
2
3 static void __array_iterator_next(iterator_t *p_iter)
4 {
5 (*(element_type_t **)(p_iter))++; //让迭代器指向下一个数据
6 }
7
8 static void __array_iterator_prev(iterator_t *p_iter)
9 {10 (*(element_type_t **)(p_iter))--; //让迭代器指向前一个数据
11 }
12
13 void array_iterator_if_get(iterator_if_t *p_if)
14 {
15 iterator_if_init(p_if, __array_iterator_next, __array_iterator_prev);
16 }基于新的迭代器,同样可以直接使用冒泡排序算法实现排序,详见程序清单3.60。
程序清单3.60使用数组、算法和迭代器
1 #include
2 #include "iterator.h"
3
4 static int __visit(void *p_arg, iterator_t it)
5 {
6 printf("%d ", *(int *)it);
7 return 0;
8 }
9
10 static int __compare(iterator_t it1, iterator_t it2)
11 {12 return *(int *)it1 - *(int *)it2;
13 }
14
15 static void __swap(iterator_t it1, iterator_t it2)
16 {
17 int data = *(int *)it2;
18 *(int *)it2 = *(int *)it1;
19 *(int *)it1 = data;
20 }
2122 int main(int argc, char *argv[])
23 {
24 iterator_if_t iterator_if;
25 int a[] = {5, 3, 2, 4, 1};
26 array_iterator_if_get(&iterator_if);
27
28 printf("\nBefore bubble sort:\n");
29 iter_foreach(&iterator_if, a, a + 5, __visit, NULL);
30
31 iter_sort(&iterator_if, a, a + 5, __compare, __swap);
32
33 printf("\nAfter bubble sort:\n");34 iter_foreach(&iterator_if, a, a + 5, __visit, NULL);
35 return 0;
36 }由此可见,通过迭代器冒泡排序算法也得到了复用。如果算法库里有几百个函数,那么只要实现迭代器接口的2个函数即可,从而达到复用代码的目的。显然,迭代器是一种更灵活的遍历行为,它可以按任意顺序访问容器中的元素,而且不会暴露容器的内部结构。
-
算法
+关注
关注
23文章
4599浏览量
92618 -
指针
+关注
关注
1文章
479浏览量
70506 -
周立功
+关注
关注
38文章
130浏览量
37578 -
迭代器
+关注
关注
0文章
43浏览量
4302
原文标题:周立功:迭代器——算法的接口
文章出处:【微信号:Zlgmcu7890,微信公众号:周立功单片机】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论