单链表的一个缺陷

  • 触发条件

    • 长时间使用单链表对象频繁增加和删除元素
  • 可能的结果

    • 堆空间产生内存碎片,导致系统运行缓慢

新的线性表

设计思路

在“单链表”的内部增加一片预留的空间,所有的Node对象都在这片空间中动态创建和动态销毁

image.png

静态单链表的继承层次结构

image.png

静态单链表的实现思路

  • 通过类模板定义静态单链表(StaticLinkList)
  • 在类中定义固定大小的空间(unsigned char[])
  • 重写 create 和 destroy 函数,改变内存的分配和归还方式
  • 在 Node 类中重载 operator new,用于在指定内存上创建对象

编程实验:静态单链表的实现

文件:StaticLinkList.h

#ifndef STATICLINKLIST_H
#define STATICLINKLIST_H

#include "LinkList.h"

#include <iostream>

using namespace std;

namespace DTLib
{

template <typename T, int N>
class StaticLinkList : public LinkList<T>
{
public:
    StaticLinkList()  // O(n)
    {
        for (int i=0; i<N; ++i)
        {
            m_used[i] = 0;
        }
    }

    int capacity()  // O(1)
    {
        return N;
    }

protected:
    //typedef typename LinkList<T>::Node Node;
    using Node = typename LinkList<T>::Node;

    struct SNode : public Node
    {
        void *operator new (unsigned int size, void *loc)
        {
            (void)size;

            return loc;
        }
    };

    unsigned char m_space[N * sizeof(SNode)];
    char m_used[N];

    Node *create() override  // O(n)
    {
        SNode *ret = nullptr;

        for (int i=0; i<N; ++i)
        {
            if (m_used[i] == 0)
            {
                ret = reinterpret_cast<SNode*>(m_space) + i;
                ret = new(ret)SNode;
                m_used[i] = 1;
                break;
            }
        }

        return ret;
    }

    void destroy(Node *pn) override  // O(n)
    {
        SNode *space = reinterpret_cast<SNode*>(m_space);
        SNode *psn = dynamic_cast<SNode*>(pn);

        for (int i=0; i<N; ++i)
        {
            if (psn == (space + i))
            {
                m_used[i] = 0;
                pn->~Node();
                break;
            }
        }
    }
};

}

#endif // STATICLINKLIST_H

文件:main.cpp

#include <iostream>
#include "StaticLinkList.h"

using namespace std;
using namespace DTLib;

int main()
{
    cout << "main begin" << endl;

    StaticLinkList<int, 5> list;

    for (int i=0; i<5; ++i)
    {
        list.insert(0, i);
    }

    for (list.move(0); !list.end(); list.next())
    {
        cout << list.current() << endl;
    }

    cout << "main end" << endl;

    return 0;
}

输出:

main begin
4
3
2
1
0
main end

答疑

LinkList 中封装 create 和 destroy 函数的意义是什么呢?

为静态单链表(StaticLinkList)的实现准备。
StaticLinkList 与 LinkList 的不同仅在于链表结点内存分配上的不同;因此将仅有的不同封装于父类和子类的虚函数中。

小结

  • 顺序表与单链表相结合后衍生出静态单链表
  • 静态单链表是LinkList的子类,拥有单链表的所有操作
  • 静态单链表在预留的空间中创建结点对象
  • 静态单链表适合于频繁增删元素的场合(最大元素个数固定)

以上内容整理于狄泰软件学院系列课程,请大家保护原创!


TianSong
734 声望138 粉丝

阿里山神木的种子在3000年前已经埋下,今天不过是看到当年注定的结果,为了未来的自己,今天就埋下一颗好种子吧