C++实现线性表(顺序表和链表)

线性表

线性表示是由称为元素的数据项组成的一种有限且有序的序列。
下面代码显示了线性表的ADT的定义。

//声明List基类
template <typename E>
class List
{
	private:
		void operator = (const List &){		}//protect assignment 
		List(const List&){}
	public:
		List(){		}//构造函数
		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 moveToStart()=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 pos)=0;
	
	//返回当前元素的值
	virtual const E& getValue() const=0;
	 
} ; 

根据上述ADT的定义,可以按照如下方式遍历顺序表

for(L.moveToStart();L.currPos()<L.lengeth();L.next())
{
	it=L.getValue();
	dosomething(it);
}

对于某些参数检查和约束检查,是利用调用Assert函数的形式完成的。以下是Assert的实现代码。

//定义Assert
void Assert(bool val,string s)//if "val" is false, pient "s"
{
	if(!val)
	{
		cout<<"Assertion Failed:"<<s<<endl;
		exit(-1);
	}
} 

顺序表实现线性表(array based list)

如下代码显示了顺序表实现的类说明。AList类继承了抽象类List,实现了List中所有的成员函数。

//顺序表实现
template <typename E> 
class AList :public List<E>
{
	private:
		int maxSize;//列表的最大size
		int listSize;//列表元素的个数
		int curr; //当前元素的位置
		E* listArray; // 
		
	public:
		AList(int size=1000) //构造函数   默认1000个(自己随便设) 
		{
			maxSize=size;
			listSize=curr=0;
			listArray=new E[maxSize];
			
		}
		~AList()//析构函数 
		{
			delete [] listArray;
		}
		
		void clear()
		{
			delete [] listArray; //删除数组 
			listSize=curr=0;   
			listArray=new E[maxSize];//新建数组 
		}
		
		void insert(const E& it)
		{
			Assert(listSize<maxSize,"List capacity exceeded");
			for(int i=listSize;i>curr;i--)
				listArray[i]=listArray[i-1]; //当前位置留出空间,当前位置的后面的元素依次向后挪一 
			listArray[curr]=it;
			listSize++;  
		}
		
		void append(const E& it)
		{
			Assert(listSize<maxSize,"List capacity exceeded");
			listArray[listSize++]=it;
		}
		
		E remove()
		{
			Assert((curr>=0)&&(curr<listSize),"No element");
			E it=listArray[curr]; 
			for(int i=curr;i<listSize;i++)
				listArray[i]=listArray[i+1];
			listSize--;
			
			return it;
		}
		
		void moveToStart()
		{
			curr=0;
		}
		
		void moveToEnd()
		{
			curr=listSize;
		}
		
		void prev()
		{
			if(curr!=0)
			curr--;
		}
		
		void next()
		{
			if(curr<listSize)
			curr++;
		}
		
		int length() const //此只能使用于成员函数,意思为此函数里不能函数所在类中的所有成员变量进行修改,他相当于对默认传递到length函数里的this指针加上const的限定,const OneClass * this;
		{
			return listSize;
		}
		
		int currPos() const
		{
			return curr;
		}
		
		void moveToPos(int pos)
		{
			Assert((pos>=0)&&(pos<=listSize),"Pos out of range"); //pos是可以在listSize位置的 
			curr=pos;
		}
		
		const E& getValue() const
		{
			Assert((curr>=0)&&(curr<listSize),"No element");
			return listArray[curr];
		}
} ;

链表实现线性表(link list)

另外一种实现线性表的传统方法是使用指针,即链表。链表是由一系类称为表的结点(node)的对象组成的。其实现代码如下。(此处是单链表,即结点中只能访问后继结点,而没有前驱结点)

//定义Link类
template <typename E> //单链表节点 
class Link
{
	public:
		E element;  //该节点的值
		Link *next; //指向列表下一个节点的指针
		
		Link(const E& elemval,Link * nextval =NULL)
		{
			element=elemval;
			next=nextval;
		};
		Link(Link* nextval=NULL)
		{
			next=nextval;
		}; 
};

如下代码显示了链表实现线性表。

//继承List基类,实现LList类

template <typename E> 
class LList : public List<E>
{
	private:
		Link<E>* head; //指向表头的指针
		Link<E>* tail; //指向最后一个元素的指针
		Link<E>* curr; //指向当前元素的指针 
		
		int cnt;  		//列表的大小
		
		void init()
		{
			curr=tail=head=new Link<E>;
			cnt=0;
		} 
		
		void removeall()//清除列表中所有的元素 
		{
			while(head!=NULL)
			{
				curr=head;
				head=head->next;
				delete curr;
			}
		}
		
	public:
		LList(int size=1000)
		{
			init();
		}
		~LList()
		{
			removeall();
		}
		
		void print() const;
		void clear()  //清空列表 
		{
			removeall();
			init();
		}
		
		void insert(const E& it)
		{
			curr->next=new Link<E>(it,curr->next);  //
			if (tail==curr) tail=curr->next;
			cnt++;
		}
		
		void append(const E& it)
		{
			tail=tail->next=new Link<E>(it,NULL);
			cnt++;
		} 
		
		E remove()
		{
			Assert(curr->next!=NULL,"No element!");
			E it=curr->next->element;  //当前元素
			Link<E>* ltemp=curr->next;
			if (tail==curr->next)
				tail=curr;
			curr->next=curr->next->next;//删除节点 
			delete ltemp;//释放空间 
			cnt-- ;
			return it;
		}
		
		void moveToStart()
		{
			curr=head;
		}
		
		void moveToEnd()
		{
			curr=tail;
		}
		
		void prev()  //只能从头开始找 
		{
			if (curr==head) return;
			Link<E>* temp=head;
			while(temp->next!=curr)
				temp=temp->next;
			curr=temp;
		}
		
		void next()
		{
			if (curr==tail) return;
			curr=curr->next;
		}
		
		int length() const
		{
			return cnt;
		}
		
		int currPos()const//返回当前位置的索引 
		{
			Link<E>* temp=head;
			int i;
			for(i=0;curr!=temp;i++)
				temp=temp->next;
			return i;
		}
		
		void moveToPos(int pos)
		{
			Assert((pos>=0)&&(pos<=cnt),"out of range");
			curr=head;
			for(int i=0;i<pos;i++)
				curr=curr->next;
		}
		
		const E& getValue() const
		{
			Assert(curr->next!=NULL,"no elenment");
			return curr->next->element;
		}
} ;

两种实现方法的比较

  • 顺序表的缺点是大小事先固定,这样虽然便于为数组分配空间,但是它不仅不能超过预定的长度,而且当线性表中的元素过少时,浪费了较多空间;链表的优点是只有实际在链表中的对象需要空间,只要存在可用的内存空间分配,链表中的元素个数就没有限制。
  • 顺序表的优点是对表中的每一个元素都没有浪费空间,而链表需要在每个结点上附件一个指针。如果element域占据空间较小,则链表的结构性开销就占去了整个存储空间的一大部分。
  • 11
    点赞
  • 77
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值