1,StaticQueue 的对象在创建的时候,对于数据元素为类类型时,会多次调用元素类型的构造函数,影响效率,所以要实现链式队列;
2,队列的链式存储实现:
3,链式队列的设计要点:
1,类模板,抽象父类 Queue 的直接子类;
2,在内部使用链式结构实现元素的存储;
3,只在链表的头部和尾部进行操作;
4,基于 LinkList 的队列 LinkQueue 的实现:
1 #include "Queue.h" 2 #include "Exception.h" 3 #include "LinkList.h" 4 5 namespace DTLib 6 { 7 8 template < typename T > 9 class LinkQueue : public Queue<T> 10 { 11 protected: 12 LinkList<T> m_list; 13 14 public: 15 LinkQueue() 16 { 17 } 18 19 void add(const T& e) // O(n) 这里需要改进 每次插入都要从头到尾的插入,很耗时。用linuxList改进。 20 { 21 m_list.insert(e); 22 } 23 24 void remove() // O(1) 25 { 26 if( m_list.length() > 0 ) 27 { 28 m_list.remove(0); 29 } 30 else 31 { 32 THROW_EXCEPTION(InvalidOperationException, "No LinkQueue element to remove ..."); 33 } 34 } 35 36 T front() const // O(1) 37 { 38 if( m_list.length() > 0 ) 39 { 40 return m_list.get(0); // 直接取,不对队列做操作 41 } 42 else 43 { 44 THROW_EXCEPTION(InvalidOperationException, "No element in current LinkQueue ..."); 45 } 46 } 47 48 int length() const // O(1) 49 { 50 return m_list.length(); 51 } 52 53 void clear() // O(n) 54 { 55 m_list.clear(); 56 } 57 58 ~LinkQueue() 59 { 60 clear(); 61 } 62 }; 63 64 }
5,基于 LinkList 实现链式队列:
1,基于 LinkList 实现的链式队列插入和清空操作中时间复杂度为 O(n),不高效;
6,队列链式存储实现的优化:
1,基于双向循环链表实现,要基于头结点的前驱和后继指针,很经典,直接指向队头和队尾;
7,基于 Linux 内核链表实现链式队列:
1,内核链表本身就是双向链表且能够通过结点指针相互连接,仅需要给结点指针加入值的存储空间构成新的结点就可以来作为真真的节点,但是头结点不用加值的存储空间;
2,形象的来说就是除了头结点其它结点长出来了一部分;
8,基于 Linux 内核链表的队列 LinkQueue 实现:
1 #ifndef LINKQUEUE_H 2 #define LINKQUEUE_H 3 4 #include "Queue.h" 5 #include "Exception.h" 6 #include "LinuxList.h" 7 8 namespace DTLib 9 { 10 11 template < typename T > 12 class LinkQueue : public Queue<T> 13 { 14 protected: 15 struct Node : public Object // 定义内部使用的结点对象,不是结构体 16 { 17 list_head head; // 链表头成员 18 T value; 19 }; 20 21 list_head m_header; 22 int m_length; 23 24 public: 25 LinkQueue() // O(1) 26 { 27 m_length = 0; 28 INIT_LIST_HEAD(&m_header); 29 } 30 31 void add(const T& e) // O(1) 32 { 33 Node* node = new Node(); 34 35 if( node != NULL ) 36 { 37 node->value = e; 38 list_add_tail(&node->head, &m_header); // O(1),将 node 插入链表尾部; 39 m_length++; 40 } 41 else 42 { 43 THROW_EXCEPTION(InvalidOperationException, "No memory to add new element ..."); 44 } 45 } 46 47 void remove() // O(1) 48 { 49 if( m_length > 0 ) 50 { 51 list_head* toDel = m_header.next; 52 list_del(toDel); // 调用内核链表函数删除内核链表结点,但是这里仅仅是断了指针的连接,还有对象实体没有销毁 O(1) 53 m_length--; 54 //delete toDel; 55 delete list_entry(toDel, Node, head); 56 } 57 else 58 { 59 THROW_EXCEPTION(InvalidOperationException, "No LinkQueue element to remove ..."); 60 } 61 } 62 63 T front() const // O(1) 64 { 65 if( m_length > 0 ) 66 { 67 return list_entry(m_header.next, Node, head)->value; // 直接取,不对队列做操作;去头结点下一个结点的值 68 } 69 else 70 { 71 THROW_EXCEPTION(InvalidOperationException, "No element in current LinkQueue ..."); 72 } 73 } 74 75 int length() const // O(1) 76 { 77 return m_length; 78 } 79 80 void clear() // O(n) 81 { 82 while( m_length > 0 ) // 队列中的每一个结点都出队列; 83 { 84 remove(); 85 } 86 } 87 88 ~LinkQueue() // O(n) 89 { 90 clear(); 91 } 92 }; 93 94 } 95 96 #endif // LINKQUEUE_H
9,LinkQueue 的测试代码:
1 #include <iostream> 2 #include "LinkQueue.h" 3 #include "StaticQueue.h" 4 5 using namespace std; 6 using namespace DTLib; 7 8 class Test : public Object 9 { 10 public: 11 Test() 12 { 13 cout << "Test()" << endl; 14 } 15 16 ~Test() 17 { 18 cout << "~Test()" << endl; 19 } 20 }; 21 22 int main() 23 { 24 LinkQueue<Test> lq; 25 StaticQueue<Test, 10> sq; 26 27 for(int i=0; i<10; i++) 28 { 29 //lq.add(i); 30 } 31 32 while( lq.length() >0 ) 33 { 34 //cout << lq.front() << endl; 35 lq.remove(); 36 } 37 38 return 0; 39 }
10,小结:
1,StaticQueue 在初始化时可能多次调用元素类型的构造函数;
2,LinkList 的组合使用能够实现队列的功能,但是不够高效;
3,LinkQueue 的最终实现组合使用了 Linux 内核链表;
4,LinkQueue 中入队和出队操作可以再常量时间内完成;