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

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

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

  

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

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

/*
作为线性表的基类,规定了线性表要实现的一些方法
*/
#ifndef LIST_H
#define LIST_H
template<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;
		}
		else
			return 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不是最后一个元素之后才有next
		if (fence != tail){
			fence = fence->next;
			++leftCount;
			--rightCount;
		}
	}

	void prev(){
		//当fence不是在第一个元素之前时才可以有prev
		if (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: 实际元素的个数也就是链表的元素节点数


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




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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值