使用迭代器如何实现指针前移或后移

电子说

1.2w人已加入

描述

近日周立功教授公开了数年的心血之作《程序设计与数据结构》,电子版已无偿性分享到电子工程师与高校群体下载,经周立功教授授权,特对本书内容进行连载。

>>>> 1.1.1 算法的接口

由于使用迭代器可以轻松地实现指针的前移或后移因此可以使用迭代器接口实现冒泡排序算法。其函数原型为

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;

7        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  }

26

27  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;

33 

34      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);

41

42     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  }

21

22  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个函数即可,从而达到复用代码的目的。显然,迭代器是一种更灵活的遍历行为,它可以按任意顺序访问容器中的元素,而且不会暴露容器的内部结构。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分