单链表学完后,理所当然的就是轮到双链表了。

18 阅读5分钟

双链表 单链表学完后,理所当然的就是轮到双链表了。

3.1 代码实现 双链表的实现如下:

/// // // FileName : dlist.h // Version : 0.10 // Author : Luo Cong // Date : 2005-1-4 10:33:21 // Comment :
// ///

#ifndef DOUBLE_LIST_H #define DOUBLE_LIST_H

#include <assert.h> #include <crtdbg.h>

#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

#ifdef _DEBUG #ifndef ASSERT #define ASSERT assert #endif #else // not _DEBUG #ifndef ASSERT #define ASSERT #endif #endif // _DEBUG

template class CNode { public: T data; CNode *prior; CNode *next; CNode() : data(T()), prior(NULL), next(NULL) {} CNode(const T &initdata) : data(initdata), prior(NULL), next(NULL) {} };

template class CDList { protected: int m_nCount; CNode *m_pNodeHead; CNode *m_pNodeTail;

public: CDList(); CDList(const T &initdata); ~CDList();

public: int 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& GetTail(); T GetTail() const; T& GetHead(); T GetHead() 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; T& GetPrev(int &pos); T& GetNext(int &pos); };

template inline CDList::CDList() : m_nCount(0), m_pNodeHead(NULL), m_pNodeTail(NULL) { }

template inline CDList::CDList(const T &initdata) : m_nCount(0), m_pNodeHead(NULL), m_pNodeTail(NULL) { AddHead(initdata); }

template inline CDList::~CDList() { RemoveAll(); }

template inline T& CDList::GetNext(int &pos) { ASSERT(0 != m_nCount); ASSERT(1 <= pos && pos <= m_nCount);

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

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

++pos;

return pTmpNode->data;

}

template inline T& CDList::GetPrev(int &pos) { ASSERT(0 != m_nCount); ASSERT(1 <= pos && pos <= m_nCount);

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

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

--pos;

return pTmpNode->data;

}

template inline int CDList::InsertBefore(const int pos, const T data) { int i; int nRetPos; CNode *pTmpNode; CNode *pNewNode;

pNewNode = new CNode<T>;
if (NULL == pNewNode)
{
    nRetPos = 0;
    goto Exit0;
}

pNewNode->data = data;

// if the list is empty, replace the head node with the new node.
if (NULL == m_pNodeHead)
{
    pNewNode->prior = NULL;
    pNewNode->next = NULL;
    m_pNodeHead = pNewNode;
    m_pNodeTail = pNewNode;
    nRetPos = 1;
    goto Exit1;
}

// is pos range valid?
ASSERT(1 <= pos && pos <= m_nCount);

// insert before head node?
if (1 == pos)
{
    pNewNode->prior = NULL;
    pNewNode->next = m_pNodeHead;
    m_pNodeHead->prior = pNewNode;
    m_pNodeHead = pNewNode;
    nRetPos = 1;
    goto Exit1;
}

// if the list is not empty and is not inserted before head node,
// seek to the pos of the list and insert the new node before it.
pTmpNode = m_pNodeHead;
for (i = 1; i < pos; ++i)
{
    pTmpNode = pTmpNode->next;
}
pNewNode->next = pTmpNode;
pNewNode->prior = pTmpNode->prior;

pTmpNode->prior->next = pNewNode;
pTmpNode->prior = pNewNode;

// if tail node, must update m_pNodeTail
if (NULL == pNewNode->next)
{
    m_pNodeTail = pNewNode;
}

nRetPos = pos;

Exit1: ++m_nCount; Exit0: return nRetPos; }

template inline int CDList::InsertAfter(const int pos, const T data) { int i; int nRetPos; CNode *pNewNode; CNode *pTmpNode;

pNewNode = new CNode<T>;
if (NULL == pNewNode)
{
    nRetPos = 0;
    goto Exit0;
}

pNewNode->data = data;

// if the list is empty, replace the head node with the new node.
if (NULL == m_pNodeHead)
{
    pNewNode->prior = NULL;
    pNewNode->next = NULL;
    m_pNodeHead = pNewNode;
    m_pNodeTail = pNewNode;
    nRetPos = 1;
    goto Exit1;
}

// is pos range valid?
ASSERT(1 <= pos && pos <= m_nCount);

// if the list is not empty,
// seek to the pos of the list and insert the new node after it.
pTmpNode = m_pNodeHead;
for (i = 1; i < pos; ++i)
{
    pTmpNode = pTmpNode->next;
}

pNewNode->next = pTmpNode->next;
pNewNode->prior = pTmpNode;

// if NewNode's position is m_pNodeTail, update m_pNodeTail
if (pTmpNode->next == m_pNodeTail)
{
    m_pNodeTail->prior = pNewNode;
}

pTmpNode->next = pNewNode;

// if tail node, must update m_pNodeTail
if (NULL == pNewNode->next)
{
    m_pNodeTail = pNewNode;
}

nRetPos = pos + 1;

Exit1: ++m_nCount; Exit0: return nRetPos; }

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

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

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

return pTmpNode->data;

}

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

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

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

return pTmpNode->data;

}

template inline int CDList::AddHead(const T data) { return InsertBefore(1, data); }

template inline int CDList::AddTail(const T data) { return InsertAfter(GetCount(), data); }

template inline CDList::IsEmpty() const { return 0 == m_nCount; }

template inline CDList::GetCount() const { return m_nCount; }

template inline T& CDList::GetTail() { ASSERT(0 != m_nCount); return m_pNodeTail->data; }

template inline T CDList::GetTail() const { ASSERT(0 != m_nCount); return m_pNodeTail->data; }

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

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

template inline void CDList::RemoveAt(const int pos) { ASSERT(1 <= pos && pos <= m_nCount);

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

// head node?
if (1 == pos)
{
    m_pNodeHead = m_pNodeHead->next;
    goto Exit1;
}

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

Exit1: delete pTmpNode; --m_nCount; if (0 == m_nCount) { m_pNodeTail = NULL; } }

template inline void CDList::RemoveHead() { ASSERT(0 != m_nCount); RemoveAt(1); }

template inline void CDList::RemoveTail() { ASSERT(0 != m_nCount); RemoveAt(m_nCount); }

template inline void CDList::RemoveAll() { int i; int nCount; CNode *pTmpNode;

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

m_nCount = 0;

}

template inline void CDList::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 inline int CDList::Find(const T data) const { int i; int nCount; CNode *pTmpNode = m_pNodeHead;

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

return 0;

}

#endif // DOUBLE_LIST_H 调用如下:

/// // // FileName : dlist.cpp // Version : 0.10 // Author : Luo Cong // Date : 2005-1-4 10:58:22 // Comment :
// ///

#include #include "dlist.h" using namespace std;

int main() { int i; int nCount; CDList dlist;

#ifdef _DEBUG _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif

dlist.AddTail(1);
dlist.AddTail(3);
dlist.InsertBefore(2, 2);
dlist.AddHead(4);
dlist.RemoveTail();

nCount = dlist.GetCount();
for (i = 1; i <= nCount;)
{
    cout << dlist.GetNext(i) << endl;
}

}

3.2 说明 单链表的结点中只有一个指向直接后继结点的指针,所以,从某个结点出发只能顺着指针往后查询其他的结点。靠,那如果我想访问某个结点的前一个结点,岂不只能重新从表头结点开始了?效率真低啊!换句话说,在单链表中,GetNext()的时间复杂度为O(1),而GetPrev()的时间复杂度则为O(N)。为克服单链表这种单向性的缺点,我们可以利用——“当当当当”,Only you,就是——双链表。

顾名思义,在双链表的结点中有两个指针,一个指向直接后继,另一个指向直接前驱,在C++语言中表示如下:

struct Node { struct Node *prior; struct Node *next; T data; }; 大部分对双链表的操作(只涉及到向后方向的指针的操作)都与单链表的相同,但在插入、删除时有很大的不同,在双链表中需同时修改两个方向上的指针。因此,可以直接继承单链表的类来完成双链表,然后改改不一样的函数就行了。但我没有这样做,别问为什么,人品问题而已。

如果你已经熟练掌握了单链表的指针域,那么双链表的这部分应该难不倒你了。不多说了,请看代码吧。如果有bug,请告诉我。^_^ ————————————————

                        版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                    

原文链接:blog.csdn.net/qq_24700495…