幻想森林

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 2333|回复: 1

[通用编程] [砖头]我也来个链表

[复制链接]

7

主题

190

帖子

1766

积分

⑥精研

....

积分
1766
发表于 2007-6-9 03:43:51 | 显示全部楼层 |阅读模式
以前学数据结构时候,忘记在哪儿弄的的一份单向链表代码,如下,当时可真是加了不少注释[s:5]
  1. //*****************************************************************
  2. //
  3. //    SingleList单链表学习代码(2004.5.12日)
  4. //  FreedomKnight_Duzhi 转载并添加注释
  5. //
  6. //*****************************************************************/
  7. // 本文件为SingleList全文件
  8. // 附注: 遍历时间复杂度为O(N),查询结点时间复杂度为O(N/2)
  9. #ifndef __SINGLE_LIST_H__
  10. #define __SINGLE_LIST_H__
  11. #include <assert.h>        // 用以支持断言
  12. #include <crtdbg.h>        // 用以支持内存泄露检测
  13. // DEBUG模式下,定义获得错误信息,文件名,行数
  14. #ifdef _DEBUG
  15.     #define DEBUG_NEW new ( _NORMAL_BLOCK, THIS_FILE, __LINE__ )
  16. #endif
  17. #ifdef _DEBUG
  18.     #define new DEBUG_NEW
  19. #undef THIS_FILE
  20. // 节省空间,没必要生成大量的错误信息,仅保存一个常量,
  21. // 出现错误其他指针指向这里则可
  22. static char THIS_FILE[] = __FILE__;
  23. #endif
  24. // 若是DEBUG模式,则断言有效,否则使其无效
  25. #ifdef _DEBUG
  26.     #ifndef ASSERT
  27.     #define ASSERT assert
  28.     #endif
  29. #else
  30.     #ifndef ASSERT
  31.     #define ASSERT
  32.     #endif
  33. #endif
  34. // 单链表每一个结点结构
  35. template < typename T >
  36. class CNode
  37. {
  38. public:
  39.     T data;
  40.     // 因为是单链表,结点中除了数据,就仅有一个next指针了
  41.     CNode< T > *next;
  42.     // 构造函数初始化表
  43.     CNode() : data( T() ), next(NULL){};
  44.     // 重载构造函数
  45.     CNode(const T &initdata) : data(initdata), next(NULL){};
  46.     // 重载构造函数2
  47.     CNode(const T &initdata, CNode< T > *p) : data(initdata), next(p)){};
  48. };
  49. // 整个单链表结构
  50. template < typename T >
  51. class CSList
  52. {
  53. protected:
  54.     // 因为是单链表,仅仅需要知道链表长度和首指针就可以了
  55.     int m_nCount;
  56.     CNode< T > *m_pNodeHead;
  57. public:
  58.     CSList();
  59.     CSList(const T &initdata);
  60.     ~CSList();
  61. public:        // 常用方法对外接口
  62.     /* 注意该部分的const用法,修饰参数,修饰成员函数
  63.     修饰成员函数时,此函数中不允许调用非const函数,且不许修改成员变量
  64.     所以,这部分函数中都自定义了一套临时变量nCount而没有使用m_nCount
  65.     */
  66.     /* 关于T与T&返回值的说明.该部分的所有获取函数都实现了两次,
  67.     目的是,返回值为T的,允许修改,即读写操作均允许.
  68.     返回值为T&的,是其内存地址所写的常量值,不允许进行写操作,进行了保护
  69.     const的目的在于进行函数重载,若不加的话,编译器认为是相同的函数
  70.     */
  71.     // 判断单链表是否为空
  72.     bool    IsEmpty() const;
  73.     // 获取单链表结点个数
  74.     int        GetCount() const;
  75.     // 在指定位置之前添加结点
  76.     int        InsertBefore(const int pos, const T data);
  77.     // 在指定位置之后添加结点
  78.     int        InsertAfter(const int pos, const T data);
  79.     // 在链表头部添加结点
  80.     int        AddHead(const T data);
  81.     // 在链表尾部添加结点
  82.     int        AddTail(const T data);
  83.     // 删除指定位置结点
  84.     void    RemoveAt(const int pos);
  85.     // 删除头结点
  86.     void    RemoveHead();
  87.     // 删除尾结点
  88.     void    RemoveTail();
  89.     // 删除所有结点
  90.     void    RemoveAll();
  91.     // 获取头结点数据
  92.     T&        GetHead();
  93.     // 获取头结点数据
  94.     T        GetHead() const;
  95.     // 获取尾结点数据
  96.     T&        GetTail();
  97.     // 获取尾结点数据
  98.     T        GetTail() const;
  99.     // 获取指定位置结点
  100.     T&        GetAt(const int pos);
  101.     // 获取指定位置结点
  102.     T        GetAt(const int pos) const;
  103.     // 更改指定位置结点数据
  104.     void    SetAt(const int pos, T data);
  105.     // 查询指定数据位置
  106.     int        Find(const T data) const;
  107. };
  108. /* 由于模版类不支持.h和.cpp分离,故实现也写在头文件中
  109. 除非编译器支持export关键字,而实际上基本没有编译器支持\(-_-)/ */
  110. /* 代码短小常用的函数使用内联比较迅速 */
  111. //-----------------------------------
  112. template< typename T>
  113. inline CSList< T >::CSList()
  114.     : m_nCount            (0),
  115.       m_pNodeHead        (NULL)
  116. {
  117. }
  118. //-----------------------------------
  119. template< typename T >
  120. inline CSList< T >::CSList(const T &initdata)
  121.     : m_nCount            (0),
  122.       m_pNodeHead        (NULL)
  123. {
  124.     AddHead(initdata);
  125. }
  126. //-----------------------------------
  127. template< typename T >
  128. inline CSList< T >::~CSList()
  129. {
  130.     RemoveAll();
  131. }
  132. //-----------------------------------
  133. template< typename T >
  134. inline bool CSList< T >::IsEmpty() const
  135. {
  136.     if( m_nCount == 0 )
  137.     {
  138.         return true;
  139.     }
  140.     else
  141.     {
  142.         return false;
  143.     }
  144. }
  145. //-----------------------------------
  146. template< typename T >
  147. inline int CSList< T >::AddHead(const T data)
  148. {
  149.     CNode< T > *pNewNode;
  150.     pNewNode = new CNode< T >;
  151.     if( pNewNode == NULL )
  152.     {
  153.         return 0;
  154.     }
  155.     pNewNode->data = data;
  156.     pNewNode->next = m_pNodeHead;
  157.     m_pNodeHead = pNewNode;
  158.     ++m_nCount;
  159.     return 1;
  160. }
  161. //-----------------------------------
  162. template< typename T >
  163. inline int CSList< T >::AddTail(const T data)
  164. {
  165.     if( InsertAfter(GetCount(), data) )
  166.     {
  167.         return 1;
  168.     }
  169.     else
  170.     {
  171.         return 0;
  172.     }
  173. }
  174. //-----------------------------------
  175. // 返回值说明:若返回0,则代表插入失败.若返回数字大于0,则记录插入结点的当前位置
  176. template< typename T >
  177. inline int CSList< T >::InsertBefore(const int pos, const T data)
  178. {
  179.     /* 将结点插入指定位置之前的操作要分为三种可能
  180.     1:    空链表插入,头指针需要添加,新结点的指针无效
  181.     2:  插入链表头部,头指针需要改变,新结点的指针赋值
  182.     3:  插入链表中间,头指针不改变,改变位置前结点指针,自身指针赋值
  183.     */
  184.     int i;
  185.     int nRetPos;                // 结点实际所在位,0代表插入失败
  186.     CNode< T > *pTmpNode;        // 目标位置前结点
  187.     CNode< T > *pNewNode;        // 想插入的目标结点
  188.     // 若创建结点失败
  189.     pNewNode = new CNode< T >;
  190.     if( pNewNode == NULL )
  191.     {
  192.         return 0;            
  193.     }
  194.     pNewNode->data = data;
  195.     // 若表为空,则相当于将数据结点设置为首结点
  196.     if(m_pNodeHead == NULL)
  197.     {
  198.         pNewNode->next = NULL;
  199.         m_pNodeHead = pNewNode;
  200.         nRetPos = 1;
  201.         ++m_nCount;
  202.         return nRetPos;
  203.     }
  204.    
  205.     // 断言:传的参数必须在合理范围内
  206.     ASSERT((1 <= pos) && (pos <= m_nCount));
  207.     // 若插入的是链表头位置
  208.     if(pos == 1)
  209.     {
  210.         pNewNode->next = m_pNodeHead;
  211.         m_pNodeHead = pNewNode;
  212.         nRetPos = 1;
  213.         ++m_nCount;
  214.         return nRetPos;
  215.     }
  216.     // 一般情况的处理
  217.     pTmpNode = m_pNodeHead;
  218.     for(i = 1; i < pos - 1; ++i)
  219.     {   
  220.         pTmpNode = pTmpNode->next;
  221.     }
  222.     // 通过循环获得的指定位置原来的结点的前一个结点pTmpNode
  223.     pNewNode->next = pTmpNode->next;
  224.     pTmpNode->next = pNewNode;
  225.     nRetPos = pos;
  226.     ++m_nCount;
  227.     return nRetPos;
  228. }
  229. //-----------------------------------
  230. template< typename T >
  231. inline int CSList< T >::InsertAfter(const int pos, const T data)
  232. {
  233.     /* 比起InsertBefore来说, InsertAfter少了一种特殊情况处理
  234.     1: 链表为空时依旧需要处理
  235.     2: 插入点不可能为最前,若为最后的话,也可按照一般情况,所以仅
  236.        需两种处理方式.
  237.     */
  238.     int i;
  239.     int nRetPos;
  240.     CNode< T > *pTmpNode;
  241.     CNode< T > *pNewNode;
  242.     pNewNode = new CNode< T >;
  243.     if (pNewNode == NULL)
  244.     {
  245.         return 0;
  246.     }
  247.     pNewNode->data = data;
  248.     if (m_pNodeHead == NULL)
  249.     {
  250.         m_pNodeHead = pNewNode;
  251.         pNewNode->next = NULL;
  252.         m_nCount++;
  253.         nRetPos = 1;
  254.         return nRetPos;
  255.     }
  256.     ASSERT((pos >= 1) && (pos <= m_nCount));
  257.     pTmpNode = m_pNodeHead;
  258.     /* 注意! 和InsertInsertBefore不同,此处循环多了一次
  259.     InsertInsertBefore获得的是目标位置原结点的前一个结点
  260.     InsertInsertAfter获得的是目标位置原结点
  261.     */
  262.     for (i = 1; i < pos; i++)
  263.     {
  264.         pTmpNode = pTmpNode->next;
  265.     }
  266.     // 通过循环获得的指定位置原结点pTmpNode
  267.     pNewNode->next = pTmpNode->next;
  268.     pTmpNode->next = pNewNode;
  269.     ++m_nCount;
  270.     nRetPos = pos + 1;    /* 注意: 此处加1 */
  271.     return nRetPos;
  272. }
  273. //-----------------------------------
  274. template < typename T >
  275. inline int CSList< T >::GetCount() const
  276. {
  277.     return m_nCount;
  278. }
  279. //-----------------------------------
  280. template < typename T >
  281. inline void CSList< T >::RemoveAt(const int pos)
  282. {
  283.     // 判断合法性
  284.     ASSERT(pos >= 1 && pos <= m_nCount);
  285.     int i;
  286.     CNode< T > *pTmpNode;        // 目标结点的前一个结点
  287.     CNode< T > *pDestNode;        // 目标位置结点   
  288.     pTmpNode = m_pNodeHead;
  289.     // 如果删除的是头结点
  290.     if (pos == 1)
  291.     {
  292.         m_pNodeHead = m_pNodeHead->next;
  293.         delete pTmpNode;
  294.         --m_nCount;
  295.         return ;
  296.     }
  297.     // 一般情况
  298.     for (i = 1; i < pos - 1; ++i)
  299.     {
  300.         pTmpNode = pTmpNode->next;
  301.     }
  302.     pDestNode = pTmpNode->next;
  303.     pTmpNode->next = pDestNode->next;
  304.     delete pDestNode;
  305.     --m_nCount;
  306. }
  307. //-----------------------------------
  308. template < typename T >
  309. inline void CSList< T >::RemoveHead()
  310. {
  311.     ASSERT(m_nCount != 0);
  312.     RemoveAt(1);
  313. }
  314. //-----------------------------------
  315. template < typename T >
  316. inline void CSList< T >::RemoveTail()
  317. {
  318.     ASSERT(m_nCount != 0);
  319.     RemoveAt(m_nCount);
  320. }
  321. //-----------------------------------
  322. template < typename T >
  323. inline void CSList< T >::RemoveAll()
  324. {
  325.     int i;
  326.     int nCount;
  327.     CNode < T > *pTmpNode;
  328.     nCount = m_nCount;
  329.     for (i = 0; i < nCount; ++i)
  330.     {
  331.         /* 从链表头部开始删除 */
  332.         pTmpNode = m_pNodeHead->next;
  333.         delete m_pNodeHead;
  334.         m_pNodeHead = pTmpNode;
  335.     }
  336.     m_nCount = 0;
  337. }
  338. //-----------------------------------
  339. template < typename T >
  340. inline T& CSList< T >::GetTail()
  341. {
  342.     ASSERT(m_nCount != 0);
  343.     int i;
  344.     int nCount;
  345.     CNode < T > *pTmpNode;
  346.     pTmpNode = m_pNodeHead;
  347.     nCount = m_nCount;
  348.     for (i = 0; i < nCount; ++i)
  349.     {
  350.         pTmpNode = pTmpNode->next;
  351.     }
  352.     return pTmpNode->data;
  353. }
  354. //-----------------------------------
  355. template < typename T >
  356. inline T CSList< T >::GetTail() const
  357. {
  358.     ASSERT(m_nCount != 0);
  359.     int i;
  360.     int nCount;
  361.     CNode < T > *pTmpNode;
  362.     pTmpNode = m_pNodeHead;
  363.     nCount = m_nCount;
  364.     for (i = 0; i < nCount; ++i)
  365.     {
  366.         pTmpNode = pTmpNode->next;
  367.     }
  368.     return pTmpNode->data;
  369. }
  370. //-----------------------------------
  371. template < typename T >
  372. inline T& CSList< T >::GetHead()
  373. {
  374.     ASSERT(m_nCount != 0);
  375.     return m_pNodeHead->data;
  376. }
  377. //-----------------------------------
  378. template < typename T >
  379. inline T CSList< T >::GetHead() const
  380. {
  381.     ASSERT(m_nCount != 0);
  382.     return m_pNodeHead->data;   
  383. }
  384. //-----------------------------------
  385. template < typename T >
  386. inline T& CSList< T >::GetAt(const int pos)
  387. {
  388.     ASSERT(pos >= 1 && pos <= m_nCount);
  389.     int i;
  390.     CNode< T > *pTmpNode;
  391.     pTmpNode = m_pNodeHead;
  392.     for (i = 1; i < pos; i++)
  393.     {
  394.         pTmpNode = pTmpNode->next;
  395.     }
  396.     return pTmpNode->data;
  397. }
  398. //-----------------------------------
  399. template < typename T >
  400. inline T CSList< T >::GetAt(const int pos) const
  401. {
  402.     ASSERT(pos >= 1 && pos <= m_nCount);
  403.     int i;
  404.     CNode< T > *pTmpNode;
  405.     pTmpNode = m_pNodeHead;
  406.     for (i = 1; i < pos; i++)
  407.     {
  408.         pTmpNode = pTmpNode->next;
  409.     }
  410.     return pTmpNode->data;
  411. }
  412. //-----------------------------------
  413. template<typename T>
  414. inline void CSList<T>::SetAt(const int pos, T data)
  415. {
  416.     ASSERT(1 <= pos && pos <= m_nCount);
  417.     int i;
  418.     CNode<T> *pTmpNode = m_pNodeHead;
  419.     for (i = 1; i < pos; ++i)
  420.     {
  421.         pTmpNode = pTmpNode->next;
  422.     }
  423.     pTmpNode->data = data;
  424. }
  425. //-----------------------------------
  426. template < typename T >
  427. inline int CSList< T >::Find(const T data) const
  428. {
  429.     int i;
  430.     int nCount;
  431.     CNode< T >  *pTmpNode;
  432.     pTmpNode = m_pNodeHead;
  433.     nCount = m_nCount;
  434.     // 遍历所有的结点
  435.     for (i = 0; i < nCount; ++i)
  436.     {
  437.         if (data = pTmpNode->data)
  438.         {
  439.             return i + 1;    // 结点号和索引号差1
  440.         }
  441.         pTmpNode = pTmpNode->next;
  442.     }
  443.     // 若未找到则返回空
  444.     return 0;
  445. }
  446. //-----------------------------------
  447. #endif
复制代码
  1. //*****************************************************************
  2. //
  3. //    SingleList单链表学习代码(2004.5.12日)
  4. //  FreedomKnight_Duzhi 转载并添加注释
  5. //
  6. //*****************************************************************/
  7. // 本文件用以测试SinList
  8. #include <iostream>
  9. #include "SinList.h"
  10. using namespace std;
  11. int main(void)
  12. {
  13.     int i;
  14.     int nCount;
  15.     CSList< int > sList;
  16. #ifdef _DEBUG
  17.     /* 如果是DEBUG模式下,若main函数运行完毕,还有内存没有释放,则会将其信息反馈
  18.     到编译器下方的调试窗口有提示(注:但是这句不进行错误位置的提示,所以若需要错误定位
  19.     依旧需要依靠头文件中的__FILE__那一行) */
  20.     _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
  21. #endif
  22.     sList.InsertAfter(sList.InsertAfter(sList.AddHead(1), 2), 3);    /* 1,2,3 */
  23.     sList.InsertAfter(sList.InsertAfter(sList.GetCount(), 4), 5);    /* 1,2,3,4,5 */
  24.     sList.InsertAfter(sList.GetCount(), 6);                            /* 1,2,3,4,5,6 */
  25.     sList.AddTail(10);                                                /* 1,2,3,4,5,6,10 */
  26.     sList.InsertAfter(sList.InsertBefore(sList.GetCount(), 7), 8);  /* 1,2,3,4,5,6,7,8,10 */
  27.     sList.SetAt(sList.GetCount(), 9);                                /* 1,2,3,4,5,6,7,8,9  */
  28.     sList.RemoveHead();                                                /* 2,3,4,5,6,7,8,9  */
  29.     sList.RemoveTail();                                                /* 2,3,4,5,6,7,8  */
  30.     nCount = sList.GetCount();
  31.     for (i = 0; i < nCount; ++i)
  32.     {
  33.         cout << sList.GetAt(i + 1) << endl;
  34.     }
  35.     // 等待输入才退出
  36.     getchar();
  37. }
复制代码
萝卜啊,白菜啊,土豆星啊,梦想有爱啊。
回复

使用道具 举报

7

主题

190

帖子

1766

积分

⑥精研

....

积分
1766
 楼主| 发表于 2007-6-9 03:45:34 | 显示全部楼层
[s:5] 其实实现的原理到是很基础的,不过这个格式值得学习,写引擎的专用方式.
萝卜啊,白菜啊,土豆星啊,梦想有爱啊。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|幻想森林

GMT+8, 2024-4-29 05:58 , Processed in 0.027261 second(s), 20 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表