基于数组和基于链表线性表C++实现

来源:互联网 发布:基德 知乎 编辑:程序博客网 时间:2024/06/08 10:54

<span style="background-color: rgb(255, 255, 255); font-family: Arial, Helvetica, sans-serif; font-size: 12px;">  数据结构是软件工程中很重要的一门课程,里面包含了数据的储存和处理,介绍了各种各样的数据结构,他们各有各的优缺点,各有各的适用范围。下面就来介绍一下最简单的数据结构--- ---线性表。</span>

  首先,简单介绍下什么是线性表。线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的。线性表的逻辑结构简单,便于实现和操作。因此,线性表这种数据结构在实际应用中是广泛采用的一种数据结构。

  然后让我们来看看线性表的两种实现方式基于数组(Arraybase list)和基于链表(Linked list)。

  

一:基于数组的线性表实现

  类List是虚基类,用于列出线性表要实现的相关功能,比如查找删除等等... ...该类会被AList(即基于数组的线性表)继承。其中由于线性表元素位置,故采用了模板参数。

/*作为线性表的基类,规定了线性表要实现的一些方法*/#ifndef LIST_H#define LIST_Htemplate<typename Elem>class List{public://清空线性表virtual void clear() = 0;//在fence位置插入元素(fence == position缩写)virtual bool insert(const Elem&) = 0;//在线性表尾增添元素virtual bool append(const Elem&) = 0;//移除特定元素virtual bool remove(Elem&) = 0;//把fence位置移到线性表最前面元素virtual void setStart() = 0;//把fence位置移到线性表末端,最后一个元素后面virtual void setEnd() = 0;//把fence位置向前移一位virtual void prev() = 0;//把fence位置向后移一位virtual void next() = 0;//获取fence位置左边元素个数virtual int leftLength() const = 0;//获取fence位置右边元素个数virtual int rightLength() const = 0;//设置fence的位置virtual bool setFence(int fence) = 0;//获取fence位置元素的值virtual bool getValue(Elem&) const = 0;//打印线性表virtual void show() const = 0;};#endif


  上面List已经给出了要实现的方法,现在就该为各个方法提供具体的实现了,我们这里采用继承的方法来写AList类,具体的逻辑均有注释,不做太多的解释。

/* *这是基于数组实现的线性表---ArraybaseList */#ifndef ALIST_H#define ALIST_H#include"List.h"#include<iomanip>#include<iostream>using namespace std;template<typename Elem>class AList :public List<Elem>{public:AList(int ms){maxSize = ms;listSize = fence = 0;listArray = new Elem[maxSize];}~AList(){delete [] listArray;}//清空线性表void clear(){delete[] listArray;listSize = fence = 0;listArray = new Elem[maxSize];}//在fence位置插入bool insert(const Elem & insertElem){if (fence == maxSize)return false;else{//先将要插入位置后面的所有元素向后移一位,腾出空间for (int i = listSize; i > fence; --i)listArray[i] = listArray[i - 1];listArray[fence] = insertElem;listSize++;return true;}}//在线性表末端插入bool append(const Elem & appendElem){if (listSize == maxSize)return false;else{listArray[listSize++] = appendElem;return true;}}//移除fence位置的元素bool remove(Elem & removeElem){if (rightLength() == 0)return false;else{removeElem = listArray[fence];for (int i = fence; i < listSize - 1; ++i)listArray[i] = listArray[i + 1];listSize--;return true;}}//把fence设置在第一个元素之前void setStart(){fence = 0;}//把fence设置在最后一个元素后面void setEnd(){fence = listSize;}//把fence向前移一位void prev(){if (fence != 0)fence--;}//把fence向后移一位void next(){if (fence != listSize)fence++;}int leftLength() const{return fence;}int rightLength() const{return listSize - fence;}//设置fence的位置bool setFence(int f){if (f >= 0 && f <= listSize){fence = f;return true;}elsereturn false;}//获取fence位置元素的值bool getValue(Elem& value) const{if (fence == listSize)return false;else{value = listArray[fence];return true;}}//打印线性表void show() const{for (int i = 0; i < listSize; i++){cout << setw(7) << listArray[i];if ((i + 1) % 10 == 0)cout << endl;}cout << endl;}private://最大储存元素个数int maxSize;//实际储存元素int listSize;//标记位置,用于插入和删除//若fence=1,则表明此刻插入和删除的位置在第一个元素的后面int fence;//用于储存元素的数组Elem * listArray;};#endif


上面详细介绍了基于数组的线性表实现,下面也介绍下基于链表的线性表实现。


二:基于链表的线性表实现
  为了后面实现更加简单,操作更加容易,我们采取增加一个头节点的方法来实现,头结点不存储数据,只指向第一个节点。下面一张图片简单介绍了头指针head,fence和tail的位置。注意fence是指向栅的前一个元素,你也可以考虑为什么不是后面(原因是插入和删除的复杂性问题)。





先简单地把节点的实现给大家,节点包含数据值和指针值,一个构造函数用于生成普通节点,另一个构造函数用于生成头结点。

template<typename Elem>class Node{public:Elem value;Node * next;Node(const Elem & v, Node * n = NULL){value = v;next = n;}Node(Node * n = NULL){next = n;}};


下面简单介绍下插入和删除操作,

上图是插入操作的示意图,插入步奏如下:

1:新建一个节点,把它的value值设置为要插入元素的值(节点实现等下给出)

2:把新建节点的next指针指向fence指向的next元素(此处是元素12)

3:把fence的next指向新建节点



上图是删除操作,操作步奏如下:

1:先让一个it指针指向要删除的节点,保存it的value值

        2:将fence指针指向it的next指向的节点

        3:删除掉it节点


下面给出与基于数组类似的线性表的各种操作,

#include<iostream>#include<iomanip>#include"Node.h"using namespace std;template<typename Elem>class LList :public Node<Elem>{private://头结点Node<Elem> * head;//尾节点Node<Elem> * tail;//fence后面的第一个节点Node<Elem> * fence;//fence左边的元素个数int leftCount;//fence右边的元素个数int rightCount;//初始化void init(){head = tail = fence = new Node<Elem>();leftCount = rightCount = 0;}//移除所有节点void removeAll(){while (head != NULL){fence = head;head = head->next;delete fence;}}public:LList(){init();}~LList(){removeAll();}void clear(){removeAll();init();}void setStart(){fence = head;rightCount += leftCount;leftCount = 0;}void setEnd(){fence = tail;leftCount += rightCount;rightCount = 0;}void next(){//当fence不是最后一个元素之后才有nextif (fence != tail){fence = fence->next;++leftCount;--rightCount;}}void prev(){//当fence不是在第一个元素之前时才可以有previf (fence != head){//从头开始遍历,查找fence的前一个元素Node<Elem> * temp = head;while (temp->next != fence)temp = temp->next;fence = temp;--leftCount;++rightCount;}}int leftLength() const{return leftCount;}int rightLength() const{return rightCount;}//获取fence位置的元素值bool getValue(Elem & v){if (rightCount == 0)returnf false;else{v = fence->next->value;return true;}}bool insert(const Elem & v){fence->next = new Node<Elem>(v, fence->next);if (tail == fence)tail = fence->next;rightCount++;return true;}bool append(const Elem & v){tail->next = new Node<Elem>(v, NULL);tail = tail->next;rightCount++;return true;}bool remove(Elem & it){if (fence == tail)return false;Node<Elem> * temp = fence->next;it = temp->value;fence->next = temp->next;if (fence->next == NULL)  //删掉最后一个时tail = fence;delete temp;rightCount--;return true;}bool setFence(const int pos){if (pos < 0 || pos >(leftCount + rightCount))return false;else{fence = head;for (int i = 1; i <= pos; i++)fence = fence->next;rightCount = (leftCount + rightCount) - pos;  leftCount = pos;return true;}}void show() const{Node<Elem> * temp = head;temp = temp->next;   //先把指针移到头结点的后一位,头结点不存储数据int count = 0;while (temp != NULL){cout << setw(7) << temp->value;temp = temp->next;if (++count % 10 == 0)cout << endl;}cout << endl;}};


总结:

两者的对比

1.时间开销

Array-Based Lists:
插入和删除的时间复杂度 (n).
Prev和直接访问的时间复杂度 (1).
数组的大小必须先被确定
当数组满时没有额外开销


Linked Lists:
插入和删除的时间复杂度 are (1).
Prev和直接访问的时间复杂性(n).
可以插入任意个元素,不用开始时设定最大元素个数
每个节点都需要额外开销


2.空间开销



D*E (Array-based List) 
 vs.   
n*(P + E) (Linked List)

当 n>D*E/(P+E) 时,n(P + E) > DE;
   
E: 存储元素的大小
P: 指针的大小
D: 数组的大小
n: 实际元素的个数也就是链表的元素节点数


故当数组元素不是很满时,链表实现更有优势,当数组元素比较满时,数组的实现更有优势




这就是本人对线性表的理解,如果有不正确或者不深刻之处,还望大家指出。


0 0
原创粉丝点击