SingleList单链表学习代码

//*****************************************************************
//
//      SingleList单链表学习代码
//    FreedomKnight_Duzhi 转载并添加注释
//
//*****************************************************************/
// 本文件为SingleList全文件
// 附注: 遍历时间复杂度为O(N),查询结点时间复杂度为O(N/2)

#ifndef __SINGLE_LIST_H__
#define __SINGLE_LIST_H__

#include <assert.h>          // 用以支持断言
#include <crtdbg.h>          // 用以支持内存泄露检测

// DEBUG模式下,定义获得错误信息,文件名,行数
#ifdef _DEBUG
      #define DEBUG_NEW new ( _NORMAL_BLOCK, THIS_FILE, __LINE__ )
#endif

#ifdef _DEBUG
      #define new DEBUG_NEW
#undef THIS_FILE
// 节省空间,没必要生成大量的错误信息,仅保存一个常量,
// 出现错误其他指针指向这里则可
static char THIS_FILE[] = __FILE__;
#endif

// 若是DEBUG模式,则断言有效,否则使其无效
#ifdef _DEBUG
      #ifndef ASSERT
      #define ASSERT assert
      #endif
#else
      #ifndef ASSERT
      #define ASSERT
      #endif
#endif

// 单链表每一个结点结构
template < typename T >
class CNode
{
public:
      T data;
      // 因为是单链表,结点中除了数据,就仅有一个next指针了
      CNode< T > *next;
      // 构造函数初始化表
      CNode() : data( T() ), next(NULL){};
      // 重载构造函数
      CNode(const T &initdata) : data(initdata), next(NULL){};
      // 重载构造函数2
      CNode(const T &initdata, CNode< T > *p) : data(initdata), next(p)){};
};

// 整个单链表结构
template < typename T >
class CSList
{
protected:
      // 因为是单链表,仅仅需要知道链表长度和首指针就可以了
      int m_nCount;
      CNode< T > *m_pNodeHead;
public:
      CSList();
      CSList(const T &initdata);
      ~CSList();
public:          // 常用方法对外接口
      /* 注意该部分的const用法,修饰参数,修饰成员函数
      修饰成员函数时,此函数中不允许调用非const函数,且不许修改成员变量
      所以,这部分函数中都自定义了一套临时变量nCount而没有使用m_nCount
      */
      /* 关于T与T&返回值的说明.该部分的所有获取函数都实现了两次,
      目的是,返回值为T的,允许修改,即读写操作均允许.
      返回值为T&的,是其内存地址所写的常量值,不允许进行写操作,进行了保护
      const的目的在于进行函数重载,若不加的话,编译器认为是相同的函数
      */

      // 判断单链表是否为空
      bool      IsEmpty() const;
      // 获取单链表结点个数
      int          GetCount() const;
      // 在指定位置之前添加结点
      int          InsertBefore(const int pos, const T data);
      // 在指定位置之后添加结点
      int          InsertAfter(const int pos, const T data);
      // 在链表头部添加结点
      int          AddHead(const T data);
      // 在链表尾部添加结点
      int          AddTail(const T data);
      // 删除指定位置结点
      void      RemoveAt(const int pos);
      // 删除头结点
      void      RemoveHead();
      // 删除尾结点
      void      RemoveTail();
      // 删除所有结点
      void      RemoveAll();
      // 获取头结点数据
      T&          GetHead();
      // 获取头结点数据
      T          GetHead() const;
      // 获取尾结点数据
      T&          GetTail();
      // 获取尾结点数据
      T          GetTail() const;
      // 获取指定位置结点
      T&          GetAt(const int pos);
      // 获取指定位置结点
      T          GetAt(const int pos) const;
      // 更改指定位置结点数据
      void      SetAt(const int pos, T data);
      // 查询指定数据位置
      int          Find(const T data) const;
};

/* 由于模版类不支持.h和.cpp分离,故实现也写在头文件中
除非编译器支持export关键字,而实际上基本没有编译器支持\(-_-)/ */
/* 代码短小常用的函数使用内联比较迅速 */
//-----------------------------------

template< typename T>
inline CSList< T >::CSList()
      : m_nCount              (0),
       m_pNodeHead          (NULL)
{
}

//-----------------------------------

template< typename T >
inline CSList< T >::CSList(const T &initdata)
      : m_nCount              (0),
       m_pNodeHead          (NULL)
{
      AddHead(initdata);
}

//-----------------------------------

template< typename T >
inline CSList< T >::~CSList()
{
      RemoveAll();
}

//-----------------------------------

template< typename T >
inline bool CSList< T >::IsEmpty() const
{
      if( m_nCount == 0 )
      {
          return true;
      }
      else
      {
          return false;
      }
}

//-----------------------------------

template< typename T >
inline int CSList< T >::AddHead(const T data)
{
      CNode< T > *pNewNode;
      pNewNode = new CNode< T >;

      if( pNewNode == NULL )
      {
          return 0;
      }

      pNewNode->data = data;
      pNewNode->next = m_pNodeHead;

      m_pNodeHead = pNewNode;
      ++m_nCount;

      return 1;
}

//-----------------------------------

template< typename T >
inline int CSList< T >::AddTail(const T data)
{
      if( InsertAfter(GetCount(), data) )
      {
          return 1;
      }
      else
      {
          return 0;
      }
}

//-----------------------------------

// 返回值说明:若返回0,则代表插入失败.若返回数字大于0,则记录插入结点的当前位置
template< typename T >
inline int CSList< T >::InsertBefore(const int pos, const T data)
{
      /* 将结点插入指定位置之前的操作要分为三种可能
      1:      空链表插入,头指针需要添加,新结点的指针无效
      2:    插入链表头部,头指针需要改变,新结点的指针赋值
      3:    插入链表中间,头指针不改变,改变位置前结点指针,自身指针赋值
      */
      int i;
      int nRetPos;                  // 结点实际所在位,0代表插入失败
      CNode< T > *pTmpNode;          // 目标位置前结点
      CNode< T > *pNewNode;          // 想插入的目标结点

      // 若创建结点失败
      pNewNode = new CNode< T >;
      if( pNewNode == NULL )
      {
          return 0;            
      }

      pNewNode->data = data;

      // 若表为空,则相当于将数据结点设置为首结点
      if(m_pNodeHead == NULL)
      {
          pNewNode->next = NULL;
          m_pNodeHead = pNewNode;
          nRetPos = 1;
          ++m_nCount;
          return nRetPos;
      }
    
      // 断言:传的参数必须在合理范围内
      ASSERT((1 <= pos) && (pos <= m_nCount));

      // 若插入的是链表头位置
      if(pos == 1)
      {
          pNewNode->next = m_pNodeHead;
          m_pNodeHead = pNewNode;
          nRetPos = 1;
          ++m_nCount;
          return nRetPos;
      }

      // 一般情况的处理
      pTmpNode = m_pNodeHead;
      for(i = 1; i < pos - 1; ++i)
      {    
          pTmpNode = pTmpNode->next;
      }
      // 通过循环获得的指定位置原来的结点的前一个结点pTmpNode
      pNewNode->next = pTmpNode->next;
      pTmpNode->next = pNewNode;

      nRetPos = pos;
      ++m_nCount;
      return nRetPos;
}

//-----------------------------------

template< typename T >
inline int CSList< T >::InsertAfter(const int pos, const T data)
{
      /* 比起InsertBefore来说, InsertAfter少了一种特殊情况处理
      1: 链表为空时依旧需要处理
      2: 插入点不可能为最前,若为最后的话,也可按照一般情况,所以仅
         需两种处理方式.
      */
      int i;
      int nRetPos;
      CNode< T > *pTmpNode;
      CNode< T > *pNewNode;

      pNewNode = new CNode< T >;
      if (pNewNode == NULL)
      {
          return 0;
      }

      pNewNode->data = data;

      if (m_pNodeHead == NULL)
      {
          m_pNodeHead = pNewNode;
          pNewNode->next = NULL;
          m_nCount++;
          nRetPos = 1;
          return nRetPos;
      }

      ASSERT((pos >= 1) && (pos <= m_nCount));

      pTmpNode = m_pNodeHead;
      /* 注意! 和InsertInsertBefore不同,此处循环多了一次
      InsertInsertBefore获得的是目标位置原结点的前一个结点
      InsertInsertAfter获得的是目标位置原结点
      */
      for (i = 1; i < pos; i++)
      {
          pTmpNode = pTmpNode->next;
      }
      // 通过循环获得的指定位置原结点pTmpNode
      pNewNode->next = pTmpNode->next;
      pTmpNode->next = pNewNode;
      ++m_nCount;
      nRetPos = pos + 1;      /* 注意: 此处加1 */
      return nRetPos;
}

//-----------------------------------

template < typename T >
inline int CSList< T >::GetCount() const
{
      return m_nCount;
}

//-----------------------------------

template < typename T >
inline void CSList< T >::RemoveAt(const int pos)
{
      // 判断合法性
      ASSERT(pos >= 1 && pos <= m_nCount);

      int i;
      CNode< T > *pTmpNode;          // 目标结点的前一个结点
      CNode< T > *pDestNode;          // 目标位置结点    
      pTmpNode = m_pNodeHead;

      // 如果删除的是头结点
      if (pos == 1)
      {
          m_pNodeHead = m_pNodeHead->next;
          delete pTmpNode;
          --m_nCount;
          return ;
      }

      // 一般情况
      for (i = 1; i < pos - 1; ++i)
      {
          pTmpNode = pTmpNode->next;
      }
      pDestNode = pTmpNode->next;
      pTmpNode->next = pDestNode->next;
      delete pDestNode;
      --m_nCount;
}

//-----------------------------------

template < typename T >
inline void CSList< T >::RemoveHead()
{
      ASSERT(m_nCount != 0);
      RemoveAt(1);
}

//-----------------------------------

template < typename T >
inline void CSList< T >::RemoveTail()
{
      ASSERT(m_nCount != 0);
      RemoveAt(m_nCount);
}

//-----------------------------------

template < typename T >
inline void CSList< T >::RemoveAll()
{
      int i;
      int nCount;
      CNode < T > *pTmpNode;

      nCount = m_nCount;
      for (i = 0; i < nCount; ++i)
      {
          /* 从链表头部开始删除 */
          pTmpNode = m_pNodeHead->next;
          delete m_pNodeHead;
          m_pNodeHead = pTmpNode;
      }
      m_nCount = 0;
}

//-----------------------------------

template < typename T >
inline T& CSList< T >::GetTail()
{
      ASSERT(m_nCount != 0);

      int i;
      int nCount;
      CNode < T > *pTmpNode;

      pTmpNode = m_pNodeHead;
      nCount = m_nCount;
      for (i = 0; i < nCount; ++i)
      {
          pTmpNode = pTmpNode->next;
      }
      return pTmpNode->data;
}

//-----------------------------------

template < typename T >
inline T CSList< T >::GetTail() const
{
      ASSERT(m_nCount != 0);

      int i;
      int nCount;
      CNode < T > *pTmpNode;

      pTmpNode = m_pNodeHead;
      nCount = m_nCount;
      for (i = 0; i < nCount; ++i)
      {
          pTmpNode = pTmpNode->next;
      }
      return pTmpNode->data;
}

//-----------------------------------

template < typename T >
inline T& CSList< T >::GetHead()
{
      ASSERT(m_nCount != 0);
      return m_pNodeHead->data;
}

//-----------------------------------

template < typename T >
inline T CSList< T >::GetHead() const
{
      ASSERT(m_nCount != 0);
      return m_pNodeHead->data;    
}

//-----------------------------------

template < typename T >
inline T& CSList< T >::GetAt(const int pos)
{
      ASSERT(pos >= 1 && pos <= m_nCount);

      int i;
      CNode< T > *pTmpNode;
      pTmpNode = m_pNodeHead;

      for (i = 1; i < pos; i++)
      {
          pTmpNode = pTmpNode->next;
      }
      return pTmpNode->data;
}

//-----------------------------------

template < typename T >
inline T CSList< T >::GetAt(const int pos) const
{
      ASSERT(pos >= 1 && pos <= m_nCount);

      int i;
      CNode< T > *pTmpNode;
      pTmpNode = m_pNodeHead;

      for (i = 1; i < pos; i++)
      {
          pTmpNode = pTmpNode->next;
      }
      return pTmpNode->data;
}

//-----------------------------------

template<typename T>
inline void CSList<T>::SetAt(const int pos, T data)
{
      ASSERT(1 <= pos && pos <= m_nCount);

      int i;
      CNode<T> *pTmpNode = m_pNodeHead;

      for (i = 1; i < pos; ++i)
      {
          pTmpNode = pTmpNode->next;
      }
      pTmpNode->data = data;
}

//-----------------------------------

template < typename T >
inline int CSList< T >::Find(const T data) const
{
      int i;
      int nCount;
      CNode< T >    *pTmpNode;

      pTmpNode = m_pNodeHead;
      nCount = m_nCount;

      // 遍历所有的结点
      for (i = 0; i < nCount; ++i)
      {
          if (data = pTmpNode->data)
          {
              return i + 1;      // 结点号和索引号差1
          }
          pTmpNode = pTmpNode->next;
      }
      // 若未找到则返回空
      return 0;
}

//-----------------------------------

#endif





//*****************************************************************
//
//      SingleList单链表学习代码

//    FreedomKnight_Duzhi 转载并添加注释
//
//*****************************************************************/
// 本文件用以测试SinList


#include <iostream>
#include "SinList.h"
using namespace std;

int main(void)
{
      int i;
      int nCount;
      CSList< int > sList;

#ifdef _DEBUG
      /* 如果是DEBUG模式下,若main函数运行完毕,还有内存没有释放,则会将其信息反馈
      到编译器下方的调试窗口有提示(注:但是这句不进行错误位置的提示,所以若需要错误定位
      依旧需要依靠头文件中的__FILE__那一行) */
      _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif

      sList.InsertAfter(sList.InsertAfter(sList.AddHead(1), 2), 3);      /* 1,2,3 */
      sList.InsertAfter(sList.InsertAfter(sList.GetCount(), 4), 5);      /* 1,2,3,4,5 */
      sList.InsertAfter(sList.GetCount(), 6);                              /* 1,2,3,4,5,6 */
      sList.AddTail(10);                                                  /* 1,2,3,4,5,6,10 */
      sList.InsertAfter(sList.InsertBefore(sList.GetCount(), 7), 8);    /* 1,2,3,4,5,6,7,8,10 */
      sList.SetAt(sList.GetCount(), 9);                                  /* 1,2,3,4,5,6,7,8,9    */
      sList.RemoveHead();                                                  /* 2,3,4,5,6,7,8,9    */
      sList.RemoveTail();                                                  /* 2,3,4,5,6,7,8    */

      nCount = sList.GetCount();
      for (i = 0; i < nCount; ++i)
      {
          cout << sList.GetAt(i + 1) << endl;
      }
      // 等待输入才退出
      getchar();
}