您的位置:首页 > 理论基础 > 数据结构算法

数据结构:线性表

2016-02-08 21:28 681 查看
声明:本文为学习数据结构与算法分析(第三版) Clifford A.Shaffer 著的学习笔记,代码有参考该书的示例代码。

线性表应该能够在任意位置插入和删除。读取任意位置的值,并且允许做出更改。

对于线性表的实现有两种方法,数组或者链表。

ADT

首先定义线性表的ADT

template<typename E>
class List
{
List(const List& list) {}
void operator= (const List& list) {}
public:
virtual ~List() {}
virtual void clear() = 0;
virtual void insert(const E& item) = 0;
virtual void append(const E& item) = 0;
virtual E remove() = 0;
virtual void moveToStar() = 0;
virtual void moveToEnd() = 0;
virtual void prev() = 0;
virtual void next() = 0;
virtual int length() const = 0;
virtual int currPos() const = 0;
virtual void moveToPos(int) = 0;
virtual E& getValue() const = 0;
};


对于线性表的实现有数组和链表两种。

在数组的实现方面,我将它设计为动态的数组。

当数组满时,怎么办?

数组

如果数组容量满时,容量增加一倍;

如果数组空时,容量减少一半;

链表

线性表的另一种实现方法是用链表。

链表本身就是动态的。

链表的实现方面就有一些技巧了。在线性表的实现中,保存了head指针、tail指针和curr指针(当前指针)

为了操作的便利有如下两个方法:

设计一个表头结点

让curr指针指向当前元素的前一个元素

设计一个表头结点,就不再需要考虑空链表的情况,节省了源代码。

让curr的指针指向当前元素的前一个元素,好处是

执行插入操作便利不少

和表头节点配合使用,味道更佳(其实是空链表的情况比较便利)

还可以建立一个可利用空闲链表,重载结点的new和delete操作符,进行程序的空间管理。

结点的实现代码如下:

template<typename E>
struct Link
{
private:
static Link<E>* freeLink;
public:
E item;
Link* next;

//-------function
Link(Link* n = nullptr) : next(n) {}
Link(const E& it, Link* n = nullptr): item(it), next(n) {}
void* operator new(size_t t)
{
if(freeLink==nullptr) return ::new Link;
Link<E>* temp = freeLink;
freeLink = freeLink->next;
return reinterpret_cast<void*>(temp);
}
void operator delete (void* ptr)
{
Link<E>* t = reinterpret_cast<Link<E>* >(ptr);
t->next = freeLink;
freeLink = t;
}
static void clear()
{
Link<E>* t;
while(freeLink!=nullptr)
{
t = freeLink;
freeLink = freeLink->next;
::delete t;
}
}
};

template<typename E>
Link<E>* Link<E>::freeLink = nullptr;


链表的实现过程中,为了知道什么时候回收可空闲链表,实现了一个计数类:

template<typename E>
class Counter
{
static size_t count;
public:
Counter() { ++count; }
Counter(const Counter& c) { ++count; }
virtual ~Counter() { --count; }
size_t getCounter() const { return count; }
};
template<typename E>
size_t Counter<E>::count = 0;


当类的对象个数只有1个时,在它的析构函数里对可利用空闲链表回收空间。

两种方法的比较

设 n 表示线性表中当前元素的数目,P 表示指针的存储单元大小(一般为4),E 表示数据元素的存储单元大小,D 表示可以在数组中存储的线性表示元素的最大数目,则顺序表(数组的实现)的空间需求为 DE。如果不考虑任何给定时刻链表中实际村粗元素的数目,则链表的空间需求为 n(P+E) 。

一般情况下,当线性表中的元素相对较少时,链表的实现比顺序表的更省空间。反之,当数组几乎被填满时,顺序表的实现空间效率更高。

为此,可以求出 n 的临界值:

n>DE/(P+E)

满足上述条件是,在任何实际情况下,顺序表的空间效率都更高。

双链表

还可以使用双链表的方式实现,即结点中除了有元素的值、下一个结点的指针外,还有前驱结点的指针。这里并没有实现

代码将会放到github上: xiaosa233
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数据结构 线性表