数据结构:线性表
2016-02-08 21:28
681 查看
声明:本文为学习数据结构与算法分析(第三版) Clifford A.Shaffer 著的学习笔记,代码有参考该书的示例代码。
线性表应该能够在任意位置插入和删除。读取任意位置的值,并且允许做出更改。
对于线性表的实现有两种方法,数组或者链表。
对于线性表的实现有数组和链表两种。
在数组的实现方面,我将它设计为动态的数组。
当数组满时,怎么办?
如果数组空时,容量减少一半;
链表本身就是动态的。
链表的实现方面就有一些技巧了。在线性表的实现中,保存了head指针、tail指针和curr指针(当前指针)
为了操作的便利有如下两个方法:
设计一个表头结点
让curr指针指向当前元素的前一个元素
设计一个表头结点,就不再需要考虑空链表的情况,节省了源代码。
让curr的指针指向当前元素的前一个元素,好处是
执行插入操作便利不少
和表头节点配合使用,味道更佳(其实是空链表的情况比较便利)
还可以建立一个可利用空闲链表,重载结点的new和delete操作符,进行程序的空间管理。
结点的实现代码如下:
链表的实现过程中,为了知道什么时候回收可空闲链表,实现了一个计数类:
当类的对象个数只有1个时,在它的析构函数里对可利用空闲链表回收空间。
一般情况下,当线性表中的元素相对较少时,链表的实现比顺序表的更省空间。反之,当数组几乎被填满时,顺序表的实现空间效率更高。
为此,可以求出 n 的临界值:
n>DE/(P+E)
满足上述条件是,在任何实际情况下,顺序表的空间效率都更高。
代码将会放到github上: xiaosa233
线性表应该能够在任意位置插入和删除。读取任意位置的值,并且允许做出更改。
对于线性表的实现有两种方法,数组或者链表。
ADT
首先定义线性表的ADTtemplate<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
相关文章推荐
- C#数据结构之顺序表(SeqList)实例详解
- Lua教程(七):数据结构详解
- 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解
- C#数据结构之队列(Quene)实例详解
- C#数据结构揭秘一
- C#数据结构之单链表(LinkList)实例详解
- 数据结构之Treap详解
- C#数据结构之堆栈(Stack)实例详解
- C#数据结构之双向链表(DbLinkList)实例详解
- JavaScript数据结构和算法之图和图算法
- Java数据结构及算法实例:冒泡排序 Bubble Sort
- Java数据结构及算法实例:插入排序 Insertion Sort
- Java数据结构及算法实例:考拉兹猜想 Collatz Conjecture
- java数据结构之java实现栈
- java数据结构之实现双向链表的示例
- Java数据结构及算法实例:选择排序 Selection Sort
- Java数据结构及算法实例:朴素字符匹配 Brute Force
- Java数据结构及算法实例:汉诺塔问题 Hanoi
- Java数据结构及算法实例:快速计算二进制数中1的个数(Fast Bit Counting)
- java线性表排序示例分享