Chinaunix首页 | 论坛 | 博客
  • 博客访问: 45824
  • 博文数量: 15
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 176
  • 用 户 组: 普通用户
  • 注册时间: 2014-01-31 01:57
个人简介

资深码农

文章分类

全部博文(15)

文章存档

2016年(3)

2014年(12)

我的朋友

分类: C/C++

2014-03-14 17:37:09

最近在学习Mark Weiss的《数据结构与算法分析(C++描述)》一书,里面介绍了list的实现,非常值得学习(在这里顺便也推荐一下这本书 ,Mark Weiss的另一本数据结构与算法分析C语言描述是我大学时候数据结构课的教材,该书是我大学课本中个人认为最为出色的一本教材,《数据结构与算法分析(C++描述)》是他关于数据结构与算法分析的另一本著作,我认为这本书同样非常的棒,值得一读)。list其实就是链表,链表算是数据结构中最常见的一种了,不过由于不能随机存取元素,因此用的场合并不是很多。不过作为一个C++程序员,了解一下它的实现无论如何都是非常必要的,对于深入理解C++的模板,STL设计思想以及迭代器模式都是很有好处的。
老规矩,先上代码

点击(此处)折叠或打开

  1. #include "stdafx.h"
  2. #include <iostream>

  3. using namespace std;

  4. template<class T>
  5. class LinkedList
  6. {
  7. public:
  8.     struct Node
  9.     {
  10.         T t;
  11.         Node * next;
  12.         Node * prev;
  13.         Node() : next(NULL), prev(NULL) {}
  14.     };

  15.     void init()
  16.     {
  17.         tail = head = new Node;
  18.         head->next = new Node;
  19.         head->next->prev = head;
  20.     }

  21.     LinkedList() : head(NULL), tail(NULL), Size(0)
  22.     {
  23.         init();
  24.     }

  25.     LinkedList(LinkedList &aList) : head(NULL), tail(NULL), Size(0)
  26.     {
  27.         init();
  28.         for (LinkedList<T>::iterator iter = aList.begin();
  29.             iter != aList.end();
  30.             iter++)
  31.         {
  32.             push_back(*iter);
  33.         }
  34.     }

  35.     const LinkedList & operator=(LinkedList<T> &aList)
  36.     {
  37.         if (&aList != this)
  38.         {
  39.             clear();
  40.             for (LinkedList<T>::iterator iter = aList.begin();
  41.                 iter != aList.end();
  42.                 iter++)
  43.             {
  44.                 push_back(*iter);
  45.             }
  46.         }
  47.         return *this;
  48.     }


  49.     void push_back(T t)
  50.     {
  51.         Node * endNode = tail->next;
  52.         tail->next = new Node;
  53.         tail->next->prev = tail;
  54.         tail = tail->next;
  55.         tail->t = t;
  56.         tail->next = endNode;
  57.         endNode->prev = tail;
  58.         Size++;
  59.     }
  60.     void pop_back()
  61.     {
  62.         if (empty())
  63.             return;
  64.         Node * newTail = tail->prev;
  65.         Node * endNode = tail->next;
  66.         newTail->next = endNode;
  67.         endNode->prev = newTail;
  68.         delete tail;
  69.         tail = newTail;
  70.         Size--;
  71.     }
  72.     bool empty() {return size() == 0;}
  73.     int size() {return Size;}

  74.     // inner class iterator
  75.     class iterator
  76.     {
  77.     public:
  78.         iterator() {}
  79.         iterator & operator++()
  80.         {
  81.             current = current->next;
  82.             return *this;
  83.         }
  84.         iterator operator++(int)
  85.         {
  86.             iterator oldIter = *this;
  87.             ++(*this);
  88.             return oldIter;
  89.         }
  90.         const T & operator*() const
  91.         {
  92.             return retrieve();
  93.         }
  94.         bool operator!=(iterator& rhs) const
  95.         {
  96.             return this->current != rhs.current;
  97.         }
  98.     protected:
  99.         LinkedList<T> *theList;
  100.         Node *current;

  101.         iterator(LinkedList<T> & lst, Node * p) : theList(&lst), current(p) {}
  102.         T &retrieve() const
  103.         {
  104.             return current->t;
  105.         }
  106.         friend class LinkedList<T>;
  107.     };

  108.     iterator begin()
  109.     {
  110.         iterator iter(*this, head->next);
  111.         return iter;
  112.     }
  113.     iterator end()
  114.     {
  115.         iterator iter(*this, tail->next);
  116.         return iter;
  117.     }

  118.     void clear()
  119.     {
  120.         while (!empty())
  121.             pop_back();
  122.     }

  123.     virtual ~LinkedList()
  124.     {
  125.         while (!empty())
  126.             pop_back();
  127.     }
  128. private:
  129.     Node * head;
  130.     Node * tail;
  131.     int Size;
  132. };

  133. template <typename Iterator, typename Object>
  134. Iterator find(Iterator start, Iterator end, const Object & x)
  135. {
  136.     Iterator iter = start;
  137.     for (; iter != end; ++iter)
  138.     {
  139.         if (*iter == x)
  140.             break;
  141.     }
  142.     return iter;
  143. }

  144. int _tmain(int argc, _TCHAR* argv[])
  145. {
  146.     // initialize two linkedlist L
  147.     LinkedList<int> L;
  148.     for (int i = 1; i <= 30; i++)
  149.         L.push_back(i);

  150.      LinkedList<int>::iterator iter = find(L.begin(), L.end(), 30);
  151.     if (iter != L.end())
  152.         cout << "Iterator found, the value is " << *iter << endl;
  153.     else
  154.         cout << "Iterator not found" << endl;

  155.     return 0;
  156. }

  157. //eof
      不到两百行的代码,基本涵盖了一个简易的list(为避免和stl标准库的混淆,我们将链表定义为LinkedList)的基本操作,麻雀虽小,五脏俱全,尤其是里面对迭代器的定义,了解它是非常有好处的。初看这些代码有些头大,不过我保证比你看微软标准库的STL中list的实现要舒服多了。分析代码要一步一步来,先看第10行Node的定义,这里使用了struct关键字而非class,struct和class的区别是C++面试常考的一道问题,它们的区别是什么?区别是class默认成员是私有的,struct默认成员是公有的,除此之外没有任何区别!OK,我们看这个Node是一个双向链表的结点,其中定义了next和prev指针,分别指向后继结点和前驱结点。接下来我们来看LinkedList的构造函数,默认构造函数调用init()成员函数,init函数里面new了两个node,这两个node叫做哨兵,本身是不存储任何信息的,但是定义他们对于简化链表的一系列操作的代码是极其有好处的,不信你可以写一个初始化head和tail为空的链表,再实现push_back,pop_back等操作看看,哈哈。我们的链表同样支持拷贝构造函数,细节请看30-39行,实现了拷贝构造函数请同时实现赋值操作符,代码43行判断了传入对象是否为this,不是的话才进行赋值操作,记得操作之前clear()一下,清空链表元素,同时释放内存,clear()代码简洁明了,参见130行。同时我们定义size函数,该函数返回Size,Size是私有成员变量,在push_back时自增,在pop_back时自减。push_back和pop_back就是一些指针交换操作,但很容易写错,这里要注意操作顺序和正确性。
      OK,现在基本的函数和功能介绍好了,下面介绍迭代器的实现,这块我个人认为是STL的精髓,你随便叫一个C++的programmer写一个list的iterator,他还真不一定能写出来,一般人也就停留在会用,不过真正了解其中含义的还真不多。首先请注意的是,iterator在LinkedList内部是一个inner class,也就是子LinkedList内部定义的类,在116行还声明了friend class LinkedList; 即LinkedList是iterator的友元,不光可以操作器公有成员,也可以操作其保护成员。接下来请看iterator的构造函数即代码第111行,这里它保存了LinkedList的指针,和一个Node结点,retrieve函数负责iterator当前指向的Node的内容。iterator不难理解,它的行为和C的指针极其相似,既然像指针,那就少不了自增,自减,取内容*操作符。第88行的自增操作(前缀形式++iter),就是把current->next赋给current自己,93行的自增操作为自增的后缀形式,也就是iter++的形式,C++为了区分自增的前缀和后缀,规定在后缀形式的自增操作符重载中加入一个int型参数,这个参数没有实际用处,仅为区分后缀操作而已。这里我们看到,后缀操作要定义一个iterator,所以相对 操作要慢于前缀形式的自增操作符,所以平时使用时尽量使用前缀形式的,像这样"for (list::iterator iter = List.begin(); iter != List.end(); ++iter)". 取地址操作符的实现是直接调用retrieve函数并返回。

      测试程序比较简单,我们定义一个查找函数find,输入参数是start, end的迭代器,同时输入一个待查找的对象Object,然后我们用1到30初始化一个LinkedList,然后查找30是否在这个表里,输出为
Iterator found, the value is 30
阅读(2269) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~