C++使用模板类创建线性表(顺序、链式)并应用

在讨论线性表的顺序及链式存储映像,并在此基础上给出其基本操作的实现之前,先以模板的形式为ADT List建立一个抽象类。

//List.h
#pragma once

template <class ElemType>
class List
{
public:
	//后面加个const 表明该函数不会改变成员函数的值!该函数可以被常量对象访问(const)。
	//注意 加了const 与不加const的函数是两个不同的函数!
	virtual bool IsEmpty() const = 0;//判断是否为空表
	virtual int Length() const = 0;//求表长
	virtual void Clear() = 0;//置空表
	virtual bool GetElem(ElemType&, int)const = 0;//取值
	virtual bool SetElem(const ElemType&, int) = 0;//表中存值
	virtual int LocateElem(const ElemType&)const = 0;//查找符合条件的数据元素
	virtual int LocatePrior(const ElemType&)const = 0;//查前驱
	virtual int LocateNext(const ElemType&)const = 0;//查后继
	virtual bool Insert(const ElemType&, int) = 0;//在指定位置插入值
	virtual bool Append(const ElemType&) = 0;//尾插
	virtual bool Delete(ElemType&, int i) = 0;//删值
	virtual void Traverse(void(*visit)(const ElemType&))const = 0;//遍历表
};

顺序表

顺序表的特点是存储地址连续,便于取值,但不便于插入、删除

//SqList.h
#pragma once
#include "List.h"

template <class ElemType>
class SqList :public List<ElemType>
{
public:
	SqList(int m = 0);
	SqList(const SqList<ElemType>&);
	~SqList();
	bool IsEmpty()const { return len <= 0; }//len<=0时返回true
	int Length()const { return len; }
	void Clear() { len = 0; }//置线性表为空表
	bool GetElem(ElemType&, int)const;
	bool SetElem(const ElemType&, int);
	int LocateElem(const ElemType&)const;
	int LocatePrior(const ElemType&)const;
	int LocateNext(const ElemType&)const;
	bool Insert(const ElemType&, int);
	bool Append(const ElemType&, int);
	bool Delete(ElemType&, int);
	void Traverse(void(*visit)(const ElemType&))const;
	SqList<ElemType>& operator=(const SqList<ElemType>&);
private:
	int len;
	int size;
	ElemType* elem;//基地址指针
	void CopyFrom(const SqList<ElemType>&);
};

//初始化顺序表
template<class ElemType>
SqList<ElemType>::SqList(int m)
{
	len = 0;
	if (0 == m)
		elem = NULL;
	else
		elem = new ElemType[m];
	size = m;
}

//复制构造函数
template<class ElemType>
SqList<ElemType>::SqList(const SqList<ElemType>& r)
{
	len = 0;
	size = 0;
	elem = NULL;
	CopyFrom(r);
}

//析构函数
template<class ElemType>
SqList<ElemType>::~SqList()
{
	delete[] elem;
}

//表中取值
template<class ElemType>
bool SqList<ElemType>::GetElem(ElemType& e, int i) const
{
	if (i<1 || i>len)
		return false;
	e = elem[i - 1];
	return true;
}

//表中存值
template<class ElemType>
bool SqList<ElemType>::SetElem(const ElemType& e, int i)
{
	if (i<1 || i>len)
		return false;
	elem[i - 1] = e;
	return true;
}

//按数据元素查值
template<class ElemType>
int SqList<ElemType>::LocateElem(const ElemType& e) const
{
	ElemType* p = elem;
	int i = 1;
	while (i < len && *p != e)
	{
		p++;
		i++;
	}
	if (i <= len)
		return i;
	return 0;
}

//查某数据元素前驱
template<class ElemType>
int SqList<ElemType>::LocatePrior(const ElemType& e) const
{
	ElemType* p = elem;
	int i = 1;
	while (i < len && *p != e)
	{
		p++;
		i++;
	}
	if (i > 1 && i <= len)
		return i - 1;
	return 0;
}

//查某数据元素后继
template<class ElemType>
int SqList<ElemType>::LocateNext(const ElemType& e) const
{
	ElemType* p = elem;
	int i = 1;
	while (i < len && *p != e)
	{
		p++;
		i++;
	}
	if (i >= 1 && i < len)
		return i + 1;
	return 0;
}

//在指定位置插入数据元素
template<class ElemType>
bool SqList<ElemType>::Insert(const ElemType& e, int i)
{
	if (i<1 || i>len)
		return false;
	if (len >= size) {
		ElemType* newbase;
		newbase = new ElemType[size + 10];
		if (!newbase)
			return false;
		for (int j = 0; j < len; j++)
			newbase[j] = elem[j];
		delete[] elem;
		elem = newbase;
		size += 10;
	}

	ElemType* p, * q;
	q = &elem[i - 1];//将elem[i-1]的地址赋给q
	for (p = &elem[len - 1]; p >= q; --p)
		*(p + 1) = *p;
	*q = e;
	++len;
	return true;
}

//在顺序表尾插入值
template<class ElemType>
bool SqList<ElemType>::Append(const ElemType& e, int i)
{
	ElemType* newbase;
	if (len >= size) {
		newbase = new ElemType[size + 10];
		if (!newbase)
			return false;
		for (int j = 0; j < len; j++)
			newbase[j] = elem[j];
		delete[] elem;
		elem = newbase;
		size += 10;
	}
	elem[len++] = e;
	return true;
}

//删除指定位置数据元素,并返回其值
template<class ElemType>
bool SqList<ElemType>::Delete(ElemType& e, int i)
{
	if (i<1 || i>len)
		return false;
	ElemType* p;
	p = &elem[i - 1];
	e = *p;
	for (p + 1; p <= &elem[len - 1]; ++p)
		*(p - 1) = *p;
	len--;
	return true;
}

//重载“=”
template<class ElemType>
SqList<ElemType>& SqList<ElemType>::operator=(const SqList<ElemType>& r)
{
	Clear();
	CopyFrom(r);
	return *this;
}

//从现有一个顺序表中复制所有元素
template<class ElemType>
void SqList<ElemType>::CopyFrom(const SqList<ElemType>& r)
{
	ElemType* p = r.elem;
	for (int i = 0; i < r.len; i++)
		Append(*p++);
}

//依次对顺序表每个元素调用一次visit()函数
template<class ElemType>
void SqList<ElemType>::Traverse(void(*visit)(const ElemType& e)) const
{
	ElemType* p = elem;
	for (int i = 0; i < len; i++)
		visit(*p++);
}

顺序表的应用:合并两个有序表

已知两个数据元素按值非递减排列的线性表La和Lb,合并La、Lb得到新表Lc,Lc也按值非递减排列。
算法描述:

  1. 建立线性表La、Lb,表长分别为n、m。
  2. i=1, j=1, k=1。
  3. 当i<=n&&j<=m,重复4、5。
  4. 依序取出La表中第i个元素和lb表中第j个元素,分别赋给sa、sb。
  5. if sa<=sb,将sa插入到线性表Lc第k的位置(表尾),i++, k++;
    否则,将sb插入到线性表Lc第k的位置(表尾),j++, k++。
  6. 若i<=n, 将La剩余元素复制到Lc表尾;
    否则,将Lb剩余元素复制到Lc表尾。
//MergeList.cpp
#include<iostream>
#include "List.h"
#include "Sqlist.h"

using namespace std;

//建立线性表
void Creat(List<int>* lx, int k, char c) {
	int e;
	if (k > 0) {
		cout << "请顺序输入" << k << "个" << c << "表中的数据:" << endl;
		for (int i = 0; i < k; i++)
		{
			cin >> e;
			lx->Append(e);
		}
	}
}

void MergeList(List<int>* la, List<int>* lb, List<int>* lc) {
	int i = 1, j = 1;
	int sa, sb;
	int lalen = la->Length();
	int lblen = lb->Length();
	while (i <= lalen && j <= lblen) {
		la->GetElem(sa, i);
		lb->GetElem(sb, j);
		if (la <= lb) {
			lc->Append(sa);
			i++;
		}
		else
		{
			lc->Append(sb);
			j++;
		}
	}
	while (i <= lalen)
	{
		la->GetElem(sa, i);
		lc->Append(sa);
		i++;
	}
	while (j<=lblen)
	{
		lb->GetElem(sb, j);
		lc->Append(sb);
		j++;
	}
}

void Print(const int& c) {
	cout << c << " ";
}

int main()
{
	void Print(const int&);
	void MergeList(List<int>*, List<int>*, List<int>*);
	void Create(List<int>*, int, char);
	int n, m;
	cout << "---此程序用于合并两个有序表---" << endl;
	cout << "请输入有序表A的长度: ";
	cin >> n;
	cout << "请输入有序表B的长度: ";
	cin >> m;
	cout << "抽象类模板连接的具体类是单链表类模板,数据元素是整型" << endl << endl;
	SqList<int> lal(n), lbl(m), lcl(n + m);
	Creat(&lal, n, 'A');
	cout << endl;
	Creat(&lbl, m, 'B');
	cout << endl;
	MergeList(&lal, &lbl, &lcl);
	cout << "合并后的c表为:" << endl;
	lcl.Traverse(Print);
	system("pause");
	return 0;
}

链表

先定义链表节点

//LinkNode.h
#pragma once
template<class ElemType>

struct LinkNode
{
	ElemType data;
	LinkNode<ElemType>* next;
};

链表的特点是便于插值、删除,但不便于遍历取值
下面创建链表类,并给出其基本操作的实现

//LinkList.h
#pragma once
#include "List.h"
#include "LinkNode.h"

template <class ElemType>
class LinkList:public List<ElemType>
{
public:
	LinkList();
	LinkList(const LinkList<ElemType>&);
	~LinkList();
	bool IsEmpty()const { return len <= 0; }
	int Length()const { return len; }
	void Clear();
	bool GetElem(ElemType&, int)const;
	bool SetElem(const ElemType&, int);
	int LocateElem(const ElemType&)const;
	int LocatePrior(const ElemType&)const;
	int LocateNext(const ElemType&)const;
	bool Insert(const ElemType&, int);
	bool Append(const ElemType&);
	bool Delete(ElemType&, int);
	void Traverse(void(*visit)(const ElemType&))const;
	LinkList<ElemType>& operator=(const LinkList<ElemType>&);

private:
	int len;
	LinkNode<ElemType>* head;
	LinkNode<ElemType>* tail;
	void CopyFrom(const LinkList<ElemType>&);
};

template<class ElemType>
LinkList<ElemType>::LinkList()
{
	len = 0;
	head = tail = new LinkNode<ElemType>;
	head->next = NULL;
}

template<class ElemType>
LinkList<ElemType>::LinkList(const LinkList<ElemType>& r)
{
	CopyFrom(r);
}

//从现有单链表拷贝构造出一个单链表
template<class ElemType>
LinkList<ElemType>::~LinkList()
{
	Clear();
	delete head; //释放头结点
}

//释放所有数据节点
template<class ElemType>
void LinkList<ElemType>::Clear()
{
	LinkNode<ElemType>* p = head->next, * q;
	while (p)
	{
		q = p->next;
		delete p;
		p = q;
	}
	tail = head;
	head->next = NULL;
	len = 0;
}

template<class ElemType>
bool LinkList<ElemType>::GetElem(ElemType& e, int i) const
{
	if (i<1 || i>len)
		return false;
	LinkNode<ElemType>* p = head->next;
	int k = 1;
	while (k<i)
	{
		p = p->next;
		k++;
	}
	e = p->data;
	return true;
}

template<class ElemType>
bool LinkList<ElemType>::SetElem(const ElemType& e, int i)
{
	if (i<1 || i>len)
		return false;
	LinkNode<ElemType>* p = head->next;
	int k = 1;
	while (k<i)
	{
		p = p->next;
		k++;
	}
	p->data = e;
	return true;
}

//要考虑表中无p的情况
template<class ElemType>
int LinkList<ElemType>::LocateElem(const ElemType& e) const
{
	LinkNode<ElemType>* p = head->next;
	int i = 1;
	while (p && p -> data != e)
	{
		p = p->next;
		i++;
	}
	if (p)
		return i;
	else
		return 0;
}

template<class ElemType>
int LinkList<ElemType>::LocatePrior(const ElemType& e) const
{
	int i = LocateElem(e);
	if (i > 1)
		return i - 1;
	return 0;
}

template<class ElemType>
int LinkList<ElemType>::LocateNext(const ElemType& e) const
{
	int i = LocateElem(e);
	if (i && i < len)
		return i + 1;
	return 0;
}

//前插,共len+1个空位
template<class ElemType>
bool LinkList<ElemType>::Insert(const ElemType& e, int i)
{
	if (i < 1 || i > len + 1)
		return false;
	LinkNode<ElemType>* p, * q;
	int k = 1;
	q = new LinkNode<ElemType>;
	q->data = e;
	//将p指针定位在i-1的位置上
	p = head;
	while (k<i)
	{
		p = p->next;
		k++;
	}
	q->next = p->next;
	p->next = q;

	if (i == len + 1)
		tail = q;
	++len;
	return true;
}

template<class ElemType>
bool LinkList<ElemType>::Append(const ElemType& e)
{
	LinkNode<ElemType>* q;
	q = new LinkNode<ElemType>;
	q->data = e;
	tail->next = q;
	tail = q;
	tail->next = NULL;
	++len;
	return true;
}

//删除第i个数据,并用e返回其值
template<class ElemType>
bool LinkList<ElemType>::Delete(ElemType& e, int i)
{
	if (i<1 || i>len)
		return false;
	LinkNode<ElemType>* p, * q;
	p = head;
	int k = 1;
	while (k<i)
	{
		p = p->next;
		k++;
	}
	q = p->next;
	p->next = q->next;
	
	if (q == tail)
		tail = p;
	e = q->data;
	delete q;
	--len;
	return true;
}

template<class ElemType>
LinkList<ElemType>& LinkList<ElemType>::operator=(const LinkList<ElemType>& r)
{
	Clear();
	delete head;
	CopyFrom(r);
	return *this;//返回当前对象本身
}

template<class ElemType>
void LinkList<ElemType>::CopyFrom(const LinkList<ElemType>& r)
{
	len = 0;
	head = tail = new LinkNode<ElemType>;
	head->next = NULL;
	LinkNode<ElemType>* p = r.head->next;
	while (p)
	{
		Append(p->data);
		p = p->next;
	}
}

template<class ElemType>
void LinkList<ElemType>::Traverse(void(*visit)(const ElemType& r)) const
{
	LinkNode<ElemType>* p = head->next;
	while (p)
	{
		visit(p->data);
		p = p->next;
	}
}

链表的应用:集合运算

利用单链表类模板实现集合运算A=(A-B)∪(B-A)

// SetOperation.cpp
#include <iostream>
#include "LinkList.h"

using namespace std;

void print(const char& c) {
	cout << c << " ";
}

//使用线性表实现集合运算(A-B)U(B-A),即找出两个集合中所有不同元素
void Difference(LinkList<char>& la, LinkList<char>& lb) {
	int i, lblen;
	lblen = lb.Length();
	//逐一读入B表数据到A表查找,若存在则从A表删除,否则,插入到A表
	for (i = 1; i <= lblen; i++)
	{
		char e;
		lb.GetElem(e, i);//单链表中取值效率较顺序表低,o(n)
		int k = la.LocateElem(e);
		if (k)
			la.Delete(e, k);//o(1)
		else
			la.Append(e);//尾插,o(1)
	}
}

//建立以线性表存储表示的集合
void Create(LinkList<char>& la, const int& k) {
	char e;
	for (int i = 0; i < k; i++)
	{
		cin >> e;
		la.Append(e);
	}
}

int main()
{
	int n, m;
	cout << "---此程序实现集合运算(A-B)∪(B-A)---" << endl;
	cout << "请输入集合A中元素个数:" << endl;
	cin >> n;
	cout << "请输入集合B中元素个数:" << endl;
	cin >> m;
    
	cout << "解题模板是单链表类,数据元素类型是字符型" << endl << endl;
	LinkList<char> lal, lbl;
	cout << "请输入" << n << "个数据元素至集合A" << endl;
	Create(lal, n);
	cout << "请输入" << m<< "个数据元素至集合A" << endl;
	Create(lbl, m);

	Difference(lal, lbl);

	cout << "运算后的集合A是:" << endl;
	lal.Traverse(print);
	cout << endl << endl;

	system("pause");
	return 0;
}
  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
线性表是一种常见的数据结构,它包含了一组有序的元素,元素之间具有前驱和后继关系。线性表链式表示是其中一种实现方式。 线性表链式表示通过使用节点和指针来表示表中元素之间的关系。具体来说,每个节点中都包含了一个元素和一个指向下一个节点的指针。通过不断地遍历指针,就可以顺着链表找到表中的所有元素。 链式表示的优点是可以动态地插入和删除元素,而不需要移动其他元素。这是因为链表中的节点通过指针相互连接,可以在任意位置插入和删除元素。同时,链式表示可以节省内存空间,因为节点不需要连续的存储空间,可以灵活地分配内存。 实现链式表示的方式是使用结构体来定义节点和指针。节点结构体中包含了一个元素和一个指向下一个节点的指针。使用指针的方式可以实现链表节点之间的相互连接。 在C语言中,可以通过定义一个节点结构体来表示链表的节点,然后使用指针指向链表的头节点。通过遍历指针,可以遍历整个链表,并对链表进行插入、删除等操作。 总之,线性表链式表示通过节点和指针的方式来实现表中元素之间的关系。链式表示可以动态地插入和删除元素,并且节省内存空间。在C语言中,可以通过定义节点结构体和指针来实现链式表示,实现链表的各种操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

banjitino

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值