有些日子没有写文章了.
最近重新看了一下数据结构和算法设计.写了个双向循环链表,这玩意儿倒不难.
既然要适应多种类型的需求,当然要用类模板了...
- #ifndef LIST_H
- #define LIST_H
- template <typename T>
- class list
- {
- private:
- struct Node
- {
- Node(){prev = next = NULL;}
- Node(const T&e,Node *p = NULL,Node *n = NULL):prev(p),next(n),data(e){}
- Node *prev;
- Node *next;
- T data;
- };
- public: // 迭代器
- class iterator
- {
- /
- };
- public:
- list()
- {
- theSize = 0;
- head = new Node;
- head->next = head->prev = head; // 指向自身的指针
- }
- ~list();
- public:
- // 成员函数
- private:
- Node *head; // 头指针
- int theSize; // 元素个数
- };
- #endif
list类被声明为一个模板类.其节点数据结构嵌套在list种定义了.既然是双向,需要两个指针分别指向前和后,Node * prev,*next; 节点种存储的数据是类型T data;
list类有一个成员变量Node *head; 这个指针被初始化为指向自身.作为一个链表的头结点(同时它又是尾节点),这样做的好处是在插入和删除元素的时候可以省去判断很多特例.一旦这样被初始化,任何时候插入和删除都可以用一种形式逻辑完成....(自己在图纸上画画,很快会明白的......发现写stl的人就是牛叉..)
有了数据结构就应该写它的功能了..
一个链表,最基础的应该有插入,删除的功能吧.
- // 在itr处插入元素
- template <typename T>
- list<T>::iterator list<T>::insert(iterator itr,const T&e)
- {
- Node *pNewNode = new Node(e,itr->prev,itr.cur);
- itr->prev->next = pNewNode;
- itr->prev = pNewNode;
- theSize++;
- return iterator(pNewNode);
- }
- // 删除itr位置处的元素
- template<typename T>
- list<T>::iterator list<T>::erase(iterator &itr)
- {
- iterator itrTemp = iterator(itr->next);
- itr->prev->next = itr->next;
- itr->next->prev = itr->prev;
- delete itr.cur;
- theSize--;
- return itrTemp;
- }
在这两个函数中,用到了iterator类型的参数.
iterator也是一个list的嵌套类型定义. 它有一个变量成员,Node *cur; 是节点类型的指针,保存当前指向的节点的地址.....有很多成员函数,都是操作符的重载.
-
- class iterator
- {
- friend class list<T>;
- public:
- iterator(){ cur=NULL; };
- Node* operator->(){ return cur;}
- T &operator*(){ return cur->data; }
- iterator operator++()
- {
- cur = cur->next;
- return cur;
- }
- iterator operator++(int)
- {
- iterator d = cur->next;
- cur = cur->next;
- return d;
- }
- bool operator==(const iterator&itr){ return itr.cur == cur; }
- bool operator!=(const iterator&itr){ return itr.cur != cur; }
- void operator=(const iterator&itr){ cur = itr.cur; }
- protected:
- iterator(Node *p){ cur = p; }
- Node *cur;
- };
我们要用它来遍历链表,实现插入和删除等操作
有了这些重载的操作符,对链表的操作就相当简单了.比如要取得 某个元素 *itr就ok了.要向后遍历,itr++,或者++itr.还有其他的向前遍历--itr.itr--.....itr是类对象,但是有了操作符的重载,用起来就像是一个指针一样,特爽.
对于其他的函数 push_front() push_back begin,back的实现.有了上面的基础就相当简单了.
- // 将节点加入链表头部,head的后面
- template<typename T>
- void list<T>::push_front(const T&e)
- {
- insert(begin(),e);
- }
- //将一个元素加入到链表的尾部
- template<typename T>
- void list<T>::push_back(const T&e)
- {
- insert(end(),e);
- }
- iterator begin()const { return iterator(head->next);} // 返回头结点指针
- iterator end()const { return iterator(head); } // 返回尾结点后面的指针
- T &front(){ return head->next->data; } // 返回第一个节点的元素
- T &back(){ return head->prev->data; } // 返回最后一个节点的元素
具体就不分析了...
编程群C,C++,MFC 58698324