Pine Blog

GLIB用户指南-双向链表

1.概念

双向链表与单向链表非常类似,不过它们包含有另外的指针,以支持更多导航选项;给定双向链表中的一个节点,可以向前移动,也可以向后移动。 这使得它们比单向链表更灵活,但也使用了更多内存,所以,除非确实需要这种灵活性,否则不要使用这种双向链表。
GLib 包含有一个名为 GList 的双向链表实现。GList 中的大部分操作与 GSList 中的类似。我们将查看基本用法的一些示例, 然后是 GList 所支持的附加操作。
glib

2.基本操作

这里是使用 GList 可以进行的一些常见操作:

//ex-glist-1.c
#include <glib.h>
int main(int argc, char** argv) {
 GList* list = NULL;
 list = g_list_append(list, "Austin ");
 printf("The first item is '%s'\n", list->data);
 list = g_list_insert(list, "Baltimore ", 1);
 printf("The second item is '%s'\n", g_list_next(list)->data);
 list = g_list_remove(list, "Baltimore ");
 printf("After removal of 'Baltimore', the list length is %d\n", g_list_length(list));
 GList* other_list = g_list_append(NULL, "Baltimore ");
 list = g_list_concat(list, other_list);
 printf("After concatenation: ");
 g_list_foreach(list, (GFunc)printf, NULL);
 list = g_list_reverse(list);
 printf("\nAfter reversal: ");
 g_list_foreach(list, (GFunc)printf, NULL);
 g_list_free(list);
 return 0;
}

***** Output *****

The first item is 'Austin '
The second item is 'Baltimore '
After removal of 'Baltimore', the list length is 1
After concatenation: Austin Baltimore
After reversal: Baltimore Austin

上面的代码看起来非常熟悉!上面所有操作都在 GSList 中出现过;唯一的区别是 GList 的函数名是 g_list 开头,而 不是 g_slist。当然,它们全都要使用指向 GList 结构体而不是指向 GSList 结构体。

3.更好的导航

已经了解了一些基本的 GList 操作后,这里是一些可能的操作,唯一的原因就是 GList 中的每个节点都有一个指向前一个节点的链接:

//ex-glist-2.c
#include <glib.h>
int main(int argc, char** argv) {
 GList* list = g_list_append(NULL, "Austin ");
 list = g_list_append(list, "Bowie ");
 list = g_list_append(list, "Charleston ");
 printf("Here's the list: ");
 g_list_foreach(list, (GFunc)printf, NULL);
 GList* last = g_list_last(list);
 printf("\nThe first item (using g_list_first) is '%s'\n", g_list_first(last)->data);
 printf("The next-to-last item is '%s'\n", g_list_previous(last)->data);
 printf("The next-to-last item is '%s'\n", g_list_nth_prev(last, 1)->data);
 g_list_free(list);
 return 0;
}

***** Output *****

Here's the list: Austin Bowie Charleston
The first item (using g_list_first) is 'Austin '
The next-to-last item is 'Bowie '
The next-to-last item is 'Bowie '

没有太令人惊讶的事情,不过有一些注解:

  • g_list_nth_prev 的第二个参数是一个整数,表明需要向前导航多少个节点。如果传递的值超出了 GList 的
    范围,那么要准备好处理程序的崩溃。
  • g_list_first 是一个 O(n) 操作。它从一个给定的节点开始向后搜索,直到找到 GList 的头。
    上面的示例是一个最坏的情形,因为遍历是从列表的末尾开始的。同理,g_list_last 也是一个 O(n) 操作。

4.使用链接删除节点

已经了解了在拥有指向其容纳的数据的指针的前提下如何从列表中删除一个节点;g_list_remove 可以很好地完成。 如果拥有一个指向节点本身的指针,可以通过一个快速的 O(1) 操作直接删除那个节点。

//ex-glist-3.c
#include <glib.h>
int main(int argc, char** argv) {
 GList* list = g_list_append(NULL, "Austin ");
 list = g_list_append(list, "Bowie ");
 list = g_list_append(list, "Chicago ");
 printf("Here's the list: ");
 g_list_foreach(list, (GFunc)printf, NULL);
 GList* bowie = g_list_nth(list, 1);
 list = g_list_remove_link(list, bowie);
 g_list_free_1(bowie);
 printf("\nHere's the list after the remove_link call: ");
 g_list_foreach(list, (GFunc)printf, NULL);
 list = g_list_delete_link(list, g_list_nth(list, 1));
 printf("\nHere's the list after the delete_link call: ");
 g_list_foreach(list, (GFunc)printf, NULL);
 g_list_free(list);
 return 0;
 }

***** Output *****

Here's the list: Austin Bowie Chicago
Here's the list after the remove_link call: Austin Chicago
Here's the list after the delete_link call: Austin

所以如果拥有一个指向节点而不是数据的指针,那么可以使用 g_list_remove_link 删除那个节点。
删除它以后,还需要使用 g_list_free_1 显式地释放它,其所做的事情正如名字所暗示的: 它释放一个节点。照例,需要保存好 g_list_remove_link 的返回值,因为那里是 列表的新起始位置。
最后,如果想做的只是删除并释放某个节点,那么可以调用 g_list_delete_link 来一步完成。
GSList 也有相同的函数;只需要将 g_list 替换为 g_slist,上面的所有信息都适用。

5.索引和位置

如果只是想找出某个条目在 GList 中的位置,那么有两种选择。可以使用 g_list_index,它可以使用条目中的 数据找出它,或者可以使用 g_list_position,它使用的是指向那个节点的指针。 这个示例展示了这两种方法:

//ex-glist-4.c
#include <glib.h>
int main(int argc, char** argv) {
 GList* list = g_list_append(NULL, "Austin ");
 list = g_list_append(list, "Bowie ");
 list = g_list_append(list, "Bowie ");
 list = g_list_append(list, "Cheyenne ");
 printf("Here's the list: ");
 g_list_foreach(list, (GFunc)printf, NULL);
 printf("\nItem 'Bowie' is located at index %d\n", g_list_index(list, "Bowie "));
 printf("Item 'Dallas' is located at index %d\n", g_list_index(list, "Dallas"));
 GList* last = g_list_last(list);
 printf("Item 'Cheyenne' is located at index %d\n", g_list_position(list, last));
 g_list_free(list);
 return 0;
}

***** Output *****

Here's the list: Austin Bowie Bowie Cheyenne
Item 'Bowie' is located at index 1
Item 'Dallas' is located at index -1
Item 'Cheyenne' is located at index 3

注意,如果 g_list_index 找不到那个数据,则它返回的值为 -1。 如果两个节点具有相同的数据值,那么 g_list_index 会返回最先出现的索引。如果找不到指定的节点, g_list_position 也会返回 -1。
另外,在 GSList 也有这些方法,只是名字不同。

6.实际应用

让我们来研究在前面提到的开放源代码的应用程序中 GList 的应用。
Gaim 使用了很多 GList:
在关闭“add a buddy”对话框时,gaim-1.2.1/plugins/gevolution/add_buddy_dialog.c 使用 g_list_foreach 调用来释放对 每个联系人的引用。

  • gaim-1.2.1/src/account.c 使用一个 GList 来保存所有帐号;它使用 g_list_find
    来确保某个帐号在使用 g_list_append 进行添加之前并不存在。
  • Evolution 使用 GList: evolution-2.0.2/filter/filter-rule.c 使用 GList
    来保存邮件过滤规则的各部分(比如要检查的主题行);filter_rule_finalise 使用 g_list_foreach释放对那些部分的 引用。evolution-2.0.2/calendar/gui/alarm-notify/alarm.c 使用GList 保存警告; queue_alarm 借助一个定制的GCompareFunc 使用 g_list_insert_sorted将新的警告插入到适当的位置。
  • 在 GIMP 中的应用: gimp-2.2.4/app/file/gimprecentlist.c 使用 GList
    保存最近访问过的文件; gimp_recent_list_read 从某个 XML 文件描述符中读取文件名,并在返回 GList 之前调用g_list_reverse。gimp-2.2.4/app/vectors/gimpbezierstroke.c 使用 GList保存笔划连接点(stroke anchors); gimp_bezier_stroke_connect_stroke 使用g_list_concat 来帮助将一个笔划连接到另一个笔划。

文章转载自:https://blog.csdn.net/l197803/article/details/138244885?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EYuanLiJiHua%7EPosition-2-138244885-blog-52932358.235%5Ev43%5Epc_blog_bottom_relevance_base2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EYuanLiJiHua%7EPosition-2-138244885-blog-52932358.235%5Ev43%5Epc_blog_bottom_relevance_base2&utm_relevant_index=5,如有问题请联系删除。

未经允许不得转载:Pine Blog » GLIB用户指南-双向链表

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

Pine Blog
Anywhere, Anytime
E-mail:59054872@qq.com
苏ICP备15059480号-1