数据结构---nginx-1.7.12源码分析 (双向链表)
2015-04-18 15:00
429 查看
// 完美分析 请见: /article/1330219.html
Ngx_queue.h
ngx-queue.c
最后是我的 测试代码 现 贴上:
关键点:
其中q为ngx_queue_t* 类型,函数作用为根据q算出,算出链表元素的地址,其中linux接口offsetof是算出link在type中的偏移。
总结:
ngx_queue设计非常精巧,基本涵盖了双链表的所有操作,建议需要面试的童鞋看一看,很多链表的题目都迎刃而解。此外,ngx_queue与其它nginx 代码耦合度低,有需要这种双向链表的实现时不妨直接拿过来使用。
Ngx_queue.h
/* * ngx-queue.h * Copyright (C) Igor Sysoev * Copyright (C) Nginx, Inc. */ #include <ngx_config.h> #include <ngx_core.h> #ifndef _NGX_QUEUE_H_INCLUDED_ #define _NGX_QUEUE_H_INCLUDED_ // nginx 的所有的链表操作对象类型 typedef struct ngx_queue_s ngx_queue_t; struct ngx_queue_s { ngx_queue_t *prev; ngx_queue_t *next; }; // 链表初始化 #define ngx_queue_init(q) \ (q)->prev = q; \ (q)->next = q // 判断链表是否为空 #define ngx_queue_empty(h) \ (h == (h)->prev) //h 插入到x的前面 #define ngx_queue_insert_head(h, x) \ (x)->next = (h)->next; \ (x)->next->prev = x; \ (x)->prev = h; \ (h)->next = x // 这定义主要用于快速排序 #define ngx_queue_insert_after ngx_queue_insert_head //将h 插入到x的后面 #define ngx_queue_insert_tail(h, x) \ (x)->prev = (h)->prev; \ (x)->prev->next = x; \ (x)->next = h; \ (h)->prev = x //下一个节点 #define ngx_queue_head(h) \ (h)->next // 上一个节点 #define ngx_queue_last(h) \ (h)->prev // 当前节点 #define ngx_queue_sentinel(h) \ (h) #define ngx_queue_next(q) \ (q)->next #define ngx_queue_prev(q) \ (q)->prev #if (NGX_DEBUG) //删除该节点 在链表中的位置 #define ngx_queue_remove(x) \ (x)->next->prev = (x)->prev; \ (x)->prev->next = (x)->next; \ (x)->prev = NULL; \ (x)->next = NULL #else #define ngx_queue_remove(x) \ (x)->next->prev = (x)->prev; \ (x)->prev->next = (x)->next #endif // 分割队列 // h为链表容器,q为链表h中的一个元素,这个方法可以将链表h以元素q为界拆分为两个链表h和n,其中h由原链表的前半部分组成(不包含q),而n由后半部分组成,q为首元素,操作也很简单 #define ngx_queue_split(h, q, n) \ (n)->prev = (h)->prev; \ (n)->prev->next = n; \ (n)->next = q; \ (h)->prev = (q)->prev; \ (h)->prev->next = h; \ (q)->prev = n; // 链接 队列: 将链表n 合并到链表h的尾部 #define ngx_queue_add(h, n) \ (h)->prev->next = (n)->next; \ (n)->next->prev = (h)->prev; \ (h)->prev = (n)->prev; \ (h)->prev->next = h; //获取队列中的节点数据 // q是队列中的节点,type队列类型,link是队列类型中ngx_queue_t的元素名 #define ngx_queue_data(q, type, link) \ (type *) ((u_char *) q - offsetof(type, link)) //队列的中间节点 ngx_queue_t *ngx_queue_middle(ngx_queue_t *queue); //队列排序算法 只要更改cmp函数即可 void ngx_queue_sort(ngx_queue_t *queue, ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *)); #endif /* _NGX_QUEUE_H_INCLUDED_ */
ngx-queue.c
/* * Copyright (C) Igor Sysoev * Copyright (C) Nginx, Inc. */ #include <ngx_config.h> #include <ngx_core.h> /* * find the middle queue element if the queue has odd number of elements * or the first element of the queue's second part otherwise */ /*这里用到的技巧是每次middle向后移动一步, next向后移动两步,这样next指到队尾的时候, middle就指到了中间,时间复杂度就是O(N), 这是一道经典的面试题, 今天在这里看到了源码, 似成相识啊,果然经典面试题目都不是凭空而来*/ ngx_queue_t * ngx_queue_middle(ngx_queue_t *queue) { ngx_queue_t *middle, *next; middle = ngx_queue_head(queue); if (middle == ngx_queue_last(queue)) { return middle; } next = ngx_queue_head(queue); for ( ;; ) { middle = ngx_queue_next(middle); next = ngx_queue_next(next); if (next == ngx_queue_last(queue)) { return middle; } next = ngx_queue_next(next); if (next == ngx_queue_last(queue)) { return middle; } } } /* the stable insertion sort */ /*这边 就是从第个元素开始向前遍历 可以看到,这里采用的是插入排序算法, 时间复杂度为O(n),整个代码非常简洁。*/ void ngx_queue_sort(ngx_queue_t *queue, ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *)) { ngx_queue_t *q, *prev, *next; q = ngx_queue_head(queue); if (q == ngx_queue_last(queue)) { return; } for (q = ngx_queue_next(q); q != ngx_queue_sentinel(queue); q = next) { prev = ngx_queue_prev(q); next = ngx_queue_next(q); ngx_queue_remove(q); do { if (cmp(prev, q) <= 0) { break; } prev = ngx_queue_prev(prev); } while (prev != ngx_queue_sentinel(queue)); ngx_queue_insert_after(prev, q); } }
最后是我的 测试代码 现 贴上:
#include <iostream> #include <algorithm> #include <pthread.h> #include <time.h> #include <stdio.h> #include <errno.h> #include <string.h> #include "queue.h" #include <stddef.h> struct student_info { long stu_id; unsigned int age; unsigned int score; ngx_queue_t qEle; }; int compareStudent(const ngx_queue_t *a, const ngx_queue_t *b) { //分别取得a b 对象指针 student_info *ainfo = ngx_queue_data(a,student_info,qEle); student_info *binfo = ngx_queue_data(b,student_info,qEle); return ainfo->score <binfo->score; } void print_ngx_queue(ngx_queue_t *queue) { //遍历输出 for(ngx_queue_t *q = ngx_queue_head(queue);q != ngx_queue_sentinel(queue);q = ngx_queue_next(q)) { student_info *info = ngx_queue_data(q, student_info, qEle); if(info != NULL) { std::cout <<info->score << " "; } } std::cout << std::endl; } int main() { ngx_queue_t queue; ngx_queue_init(&queue); student_info info[5]; for(int i = 0;i < 5;i++) { info[i].stu_id = i; info[i].age = i; info[i].score = i; if(i%2) { ngx_queue_insert_tail(&queue,&info[i].qEle); } else { ngx_queue_insert_head(&queue,&info[i].qEle); } } print_ngx_queue(&queue); ngx_queue_sort(&queue,compareStudent); print_ngx_queue(&queue); return 0; }
关键点:
根据ngx_queue_t 找到链表元素
1: #define ngx_queue_data(q, type, link) \
2: (type *) ((u_char *) q - offsetof(type, link))
其中q为ngx_queue_t* 类型,函数作用为根据q算出,算出链表元素的地址,其中linux接口offsetof是算出link在type中的偏移。
总结:
ngx_queue设计非常精巧,基本涵盖了双链表的所有操作,建议需要面试的童鞋看一看,很多链表的题目都迎刃而解。此外,ngx_queue与其它nginx 代码耦合度低,有需要这种双向链表的实现时不妨直接拿过来使用。
相关文章推荐
- nginx高级数据结构源码分析(一)-----双向链表
- Nginx 源码分析-- ngx_array、ngx_list基本数据结构
- Nginx高级数据结构源码分析(二)-----动态数组
- nginx源码分析2———基础数据结构二(链表和双向链表)
- Nginx源码学习-双向链表(ngx_queue_t)实现及实例分析
- 【Nginx源码剖析-数据结构】双向链表(queue)【未完】
- Nginx源码分析之基本数据结构
- Nginx源码分析各个数据结构介绍链接
- Nginx高级数据结构源码分析(三)-----链表
- Nginx高级数据结构源码分析(四)-----内存池
- 文章2:Nginx源码分析-ngx_array_t动态数组
- 【Nginx源码剖析-数据结构】数据结构梳理
- Nginx源码分析-内存池
- [nginx源码分析]hash 和header 回调初始化
- Nginx源码分析 - 实战篇 - 编写一个自定义的模块
- nginx源码分析—数组结构ngx_array_t
- nginx 源码学习笔记(六)——nginx基本数据结构
- Nginx源码分析——日志处理
- Nginx源码分析-启动初始化过程(二)
- nginx源码初读(1)--让烦恼从数据结构开始(ngx_cdecl/ngx_int/ngx_log)