我们知道在比较排序算法中,归并排序可以实现O(n*logn)的时间复杂度。对于数组来说,他不是原地排序,需要申请额外的空间。不过glib是用链表来表示一个GList的,所以在GList当中,只需要修改链表的前后指针即可。
归并排序的一般算法为:
void MergeSort(Node l[], int m , int n) {
if(m<n){
MergeSort(l,m,n/2);
MergeSort(l,n/2+1,n);
Merge(1,m,n);
}
}
我们先看看glib是如何对两个链表进行Merge的。
static GList *
g_list_sort_merge (GList *l1,
GList *l2,
GFunc compare_func, //用于比较的函数
gpointer user_data)
{
GList list, *l, *lprev;
gint cmp;
l = &list; //用于保存两个链表合并后的起始指针head = list.next
lprev = NULL; //用于保存链表的前驱,这里的链表是双循环链表
while (l1 && l2)
{
cmp = ((GCompareDataFunc) compare_func) (l1->data, l2->data, user_data); //比较两个數的大小
if (cmp <= 0)
{
l->next = l1; //这里的指针l会一直跟着移动,所以l->next = l1的操作实际上是在重新连接链表的各个节点
l1 = l1->next; //l1的指针向后移动
}
else
{
l->next = l2;
l2 = l2->next;
}
l = l->next; //l指针向后移动
l->prev = lprev;
lprev = l;
}
l->next = l1 ? l1 : l2;
l->next->prev = l;
return list.next;
}
从上面的代码可以看出,程序实现的过程有三个指针最重要l,l1,l2。l1和l2分别指向要合并的两条链表并且一直向后移动(只要l1所指的数据比l2所指的数据小,l1就向后移动)。l这个指针一直都向后移动,重新链接起每一个节点。
现在我们来看看GList是如何实现排序的。
static GList*
g_list_sort_real (GList *list,
GFunc compare_func,
gpointer user_data)
{
GList *l1, *l2;
if (!list)
return NULL;
if (!list->next)
return list;
l1 = list; //l1指向起点
l2 = list->next; //l2指向下一个位置
while ((l2 = l2->next) != NULL) //在这个循环当中,l2实际上是移动了两次,但是l1只是移动了一下,所以当循环结束时,l2已经到了链表的最后一个节点
{
if ((l2 = l2->next) == NULL) //而l1已经到了链表的中间节点了。
break;
l1 = l1->next;
}
l2 = l1->next;
l1->next = NULL; //这两句把l2移动到了中间节点,并把链表分为两条,分别由l2和list指向
return g_list_sort_merge (g_list_sort_real (list, compare_func, user_data),
g_list_sort_real (l2, compare_func, user_data),
compare_func,
user_data); //这一个函数的调用是用递归的方法来实现的。g_list_sort_merge可以显示两个有序链表的合并,而g_list_sort_real
} //又可以实现链表的排序