填一个大坑之C/C++实现线性表(利用泛型)下篇

今天我们用C++来实现顺序表和链表,
因为C和C++是两种在思维方式上不同,语法上很相同的语言,所以要求我们能够在两种思维中转换。
由于在上篇中我们都用int类型来做Elementype,本篇中我们利用泛型类来编写,使它们具有同用性

1.C++泛型顺序表
我们将顺序表封装成一个类,将数据指针,和长度,容量做为3个顺序表的成员,
把具体的操作函数写成一个个方法(有必要时重载以实现不同的功能)
下面给出顺序表的定义

template <class T>
class SQList{
private:
    T*elem;    //指向数据空间的指针
    int length;   //长度
    int listsize;   //容量
public:

    /*  具体的各种操作方法 */    
};

由于对象的生成会自动的调用构造函数,
生命周期结束时会自动调用析构函数,
所以我们把顺序表的初始化放在构造函数中,把表的释放放在析构函数中
代码如下

template <class T>     //声明模板T
SQList<T>::SQList(int listsize)
{
	elem = new T[listsize];      //在内存中开辟传入的listsize个空间
	
	length = 0;              //初始化表长为0
	
	this->listsize = listsize;
}
template <class T>
SQList<T>::~SQList()        //析构函数
{
	delete[] elem;         //释放开辟的内存空间
	elem = NULL;
}

现在就完成了构造和析构的步骤
现在我们就需要实现插入和删除操作

template <class T>
bool SQList<T>::insertlist(int pos,T e)    //插入函数
{
	if(pos>length||pos<1)return false;    //避免传入错误的插入位置
	T*weiba = &elem[length-1];           //得到尾元素的地址
	T*temp = &elem[pos-2];               //得到要插入元素得前一个元素地址
	for(temp++;weiba>=temp;weiba--)*(weiba+1) = *weiba;  //原来位置以后得元素向后一位
	T*need = &elem[pos-1];     //得到要插入位置的地址
	*need = e;                 //保存数据e
	length++;                  //长度加一
	return true;
	
}
template <class T>
bool SQList<T>::deletelist(int pos)
{
	if(pos>length||pos<1)return false;   //避免传入错误的删除位置
	T*weiba = &elem[length-1];      //得到表尾得地址
	T*temp = &elem[pos];            //得到要删除得后一个位置
	for(temp;weiba>=temp;temp++)*(temp-1) = *temp;   //从后向前覆盖
	length--;     //长度减一
	return true;
}

我们看过程中观察效果,我们写一个显示数据的方法

template <class T>
void SQList<T>::showdata()
{
     for(int i = 0;i<length;i++)
     {
     cout<<"|  "<<elem[i]<<"\n------------\n"<<endl;
     }
}

接下来是合并表方法

void SQList<T>::SQListcombine(SQList*s1,SQList*s2,SQList*newbase)
{
	T*p1 = s1->elem;             //得到表头
	T*p2 = s2->elem;              //得到表头
	newbase->elem = new T[s1->listsize+s2->listsize];   //新表开辟内存
	T*p1_last = s1->elem+s1->length-1;   //得到表尾
	T*p2_last = s2->elem+s2->length-1;   //得到表尾
	
	T*p3 = newbase->elem;       //得到新表的表头
	while(p1<=p1_last)*p3++ = *p1++;    //写入
	
	while(p2<=p2_last)*p3++ = *p2++;
	newbase->length = s1->length+s2->length;
	newbase->listsize = s1->listsize+s2->listsize; 
}

自此一个比较完整的顺序表就出来了。
下面给出我写的一个已经封装好的顺序表类的源码(使用泛型)

#ifndef _GENSQLIST_H_
#define _GENSQLIST_H_
#include <iostream>
#include <string>
using namespace std;
template <class T>
class SQList{
	
	private:
	
	T*elem;
	
	int length;
	
	int listsize;
	
	public:
	
	SQList(){
		
	};
	SQList(int listsize);
	~SQList();
	void writedata(int tohow);
	void showdata();
	bool insertlist(int pos,T e);
	bool deletelist(int pos);
    void SQListcombine(SQList*s1,SQList*s2,SQList*newbase);
	
};
template <class T>
SQList<T>::SQList(int listsize)
{
	elem = new T[listsize];
	
	length = 0;
	
	this->listsize = listsize;
}
template <class T>
SQList<T>::~SQList()
{
	delete[] elem;
	elem = NULL;
}
template <class T>
void SQList<T>::writedata(int tohow)
{
	T*temp = elem;
	
	for(int i = 0;i<tohow;i++,temp++)
	{
		T element;
		cin>>element;
		*temp = element;
		length++;
	}
}
template <class T>
void SQList<T>::showdata()
{
	for(int i = 0;i<length;i++)
	{
		cout<<" "<<elem[i]<<"\n---------\n"<<endl; 
	}
}
template <class T>
bool SQList<T>::insertlist(int pos,T e)
{
	if(pos>length||pos<1)return false;
	T*weiba = &elem[length-1];
	T*temp = &elem[pos-2];
	for(temp++;weiba>=temp;weiba--)*(weiba+1) = *weiba;
	T*need = &elem[pos-1];
	*need = e;
	length++;
	return true;
	
}
template <class T>
bool SQList<T>::deletelist(int pos)
{
	if(pos>length||pos<1)return false;
	T*weiba = &elem[length-1];
	T*temp = &elem[pos];
	for(temp;weiba>=temp;temp++)*(temp-1) = *temp;
	length--;
}
template <class T>
void SQList<T>::SQListcombine(SQList*s1,SQList*s2,SQList*newbase)
{
	T*p1 = s1->elem;
	T*p2 = s2->elem;
	newbase->elem = new T[s1->listsize+s2->listsize];
	T*p1_last = s1->elem+s1->length-1;
	T*p2_last = s2->elem+s2->length-1;
	
	T*p3 = newbase->elem;
	while(p1<=p1_last)*p3++ = *p1++;
	
	while(p2<=p2_last)*p3++ = *p2++;
	newbase->length = s1->length+s2->length;
	newbase->listsize = s1->listsize+s2->listsize; 
}
#endif

下面是我以前写的一个typedef以int为类型的顺序表

#pragma once
#ifndef _MYSQLIST_H_
#define _MYSQLIST_H_

#include <iostream>
#include <string>
typedef int Elemtype;
using namespace std;
class SQList {
private:
	Elemtype *elem;
	int length;
	int listsize;
public:
	SQList(int listsize);
	SQList(){}     //这个构造函数的重载使我们可以不对对象进行实体化
	~SQList();      //析构函数,释放申请的内存空间
	void appendsize(int howmuch);   //追加申请HOWMUCH内存空间
	void writedata(int times);      //从表得第一位输入数据times次
	bool writedata(int beg, int end);  //从表的第BEG到END位输入数据END-BEG次
	void showlist(int towhere);        //输入顺序表的值
	void showlist();
	bool insertlist(int pos, Elemtype e);   //在POS处插入元素E
	bool deletlist(int pos,Elemtype*e);//传入一个Elemtype类型得到要删除得数据,用来回滚
	void Listcombine(SQList*q, SQList*p,SQList*Lc);  //传入前面两个表的地址,合并到第三个表
    bool sortlist(bool flag);  //排序函数,T为 大到小的选择法排序 
    void toRerseve();          //元素倒置函数
    
};
SQList::SQList(int listsize) {          //顺序表构造函数,开辟空间,初始化顺序表
	elem = new Elemtype[listsize];
	length = 0;
	this->listsize = listsize;

}
void SQList::writedata(int times) {
	Elemtype *p = elem;
	for (int i = 0; i < times; i++,p++) {
		cin >> *p;
	}
	length += times;
}
void SQList::showlist(int towhere) {
	Elemtype *p = elem;
	for (int i = 0; i < towhere; i++, p++) {
		cout << "      " << *p << "\n---------\n" << endl;
	}
}
void SQList::showlist(){
   Elemtype*temp = elem;
   for(int i = 0;i<length;i++,temp++)
   {
       cout<<"        "<<*temp<<"\n-----------\n"<<endl; 	
   }
}
bool SQList::insertlist(int pos, Elemtype e) {       //顺序表插入方法
	if (pos<0 || pos>length - 1)return false;  //插入错误情况
	Elemtype *point = elem+length-1;      //表尾
	Elemtype *q = elem + pos-1;          //得到插入位置得地址
	for (int i = length - 1; i > pos-1; i--,point--)
	{
		*(point+1) = (*point);
	}
	*q = e;
	length += 1;
	return true;
}
bool SQList::deletlist(int pos, Elemtype*e) {    //删除方法
	if (pos<0 || pos>length - 1)return false;
	Elemtype *p1 = elem + pos-1;
	Elemtype *p2 = elem + length-1;
	e = p1;      //得到要删除得元素地址,给我们传入得地址
	for (int i = pos; i < length - 1; i++,p1++) {   //后一个去覆盖前一个
		*(p1 - 1) = *(p1);
	}
	*p2 = NULL;
	length--;
	return true;
}
SQList::~SQList() {        //析构函数
	delete[] elem;
	elem = NULL;
}
void SQList::Listcombine(SQList*q, SQList*p,SQList*Lc) {  //合并表函数
	Elemtype *pa = q->elem;
	Elemtype *pb = p->elem;
	Elemtype *pa_last = q->elem + q->length - 1;
	Elemtype *pb_last = p->elem + p->length - 1;
	Elemtype *pc = Lc->elem;
	while (pa <= pa_last)*pc++ = *pa++;
	while (pb <= pb_last)*pc++ = *pb++;
}
void SQList::appendsize(int howmuch) {     //追加空间函数
	Elemtype *newsize = new Elemtype[listsize + howmuch];
	Elemtype *tmp2 = newsize;
	Elemtype *fade = elem;
	int i = 0;
	while (i < length) {
		*tmp2++ = *elem++;
			i++;
	}
	length += howmuch;
	listsize += howmuch;
	
	elem = newsize;
	delete[] fade;
}
bool SQList::writedata(int beg, int end) {    //重载输入数据函数
	Elemtype *findpos = elem + beg-1;
	Elemtype *theend = elem + beg + end-2;
	for (int i = 0; i < end-beg+1 ; i++,findpos++)
	{
		cin >> *findpos;
	}
	return true;
}
bool SQList::sortlist(bool flag)    //flag控制排序方式 
{
	if(flag){
		Elemtype swap1=NULL;        //交换用变量 
		Elemtype*temp1 = elem;      //得到数组得地址 
		for(int i = 0;i<length-1;i++)
		{
			for(int j = i+1;j<length;j++)   //选择法大到小排序的操作 
			{
				if(*(temp1+i)<*(temp1+j))
				{
					swap1 = *(temp1+i);     //交换 
					
					*(temp1+i) = *(temp1+j);
					
					*(temp1+j)  = swap1;
				}
			}
		}
		return false;
		
	}
	else{
		Elemtype swap2;               //交换用变量 
		Elemtype*temp1 = elem;        //得到顺序表地址 
		for(int i = 0;i<length-1;i++)            //冒泡小到大 
		{
			for(int j =0;j<length-1-i;j++)
			{
				if(temp1[j]>temp1[j+1])
				{
					swap2 = *(temp1+j);
					
					*(temp1+j) = *(temp1+j+1);
					
					*(temp1+j+1)  = swap2;
				}
			}
		}
		return 0;
	}
}
void SQList::toRerseve()
{
	Elemtype*temp = elem;
	Elemtype tohuan;
	for(int i = 0;i<(length/2);i++)
	{
			tohuan = *(temp+i);
			
			*(temp+i) = *(temp+length-1-i);
			
			*(temp+length-1-i) = tohuan;
	}
}
#endif // !_MYSQLIST_H_

2.C++泛型链表
同样,按照我在上篇中的导图只要把Linkedlist结构体封装成一个类。
结构就像如下

template <class T>
struct Node{
	T data;
	
	Node<T> *next;
	
};
template <class T>
class linklist{
	private:
	
	Node<T> * Head;
	
	int length1;
	public:
	/*方法列表*/

同样是用构造函数和析构函数来实现初始化和内存释放
不过析构函数可要复杂一些

template <class T>   
linklist<T>::linklist(){     //构造函数
	
	Head = new Node<T>;   //开辟头节点
	
	Head->next = NULL;    //指向空
	
	length1 = 0;  //初始化长度为0
	
}
template <class T>
linklist<T>::~linklist()           //析构函数
{
	Node<T>*temp = NULL;      //定义一个临时的指针
	Node<T>*p = Head->next;   //p和temp形成循环释放头节点之后的节点
	while(p!=NULL){
		temp = p->next;
		delete p;
		p = temp;
	}
	delete Head;
	Head = NULL;
	cout<<"................."<<endl;
	length1 = 0;
}

有了初始化之后
就要有头插和尾插的方法

template <class T>
void linklist<T>::adddata(T Data)    //尾部插入
{
	Node<T> *p = new Node<T>;   //开辟新的节点
	p->data = Data;                        //保存数据
	p->next = NULL;                     //后指针指向空
	Node<T>*p1 = Head;             //得到头节点的地址
	
	while(p1->next!=NULL){
		p1 = p1->next;                 //循环指向尾节点
	}
	p1->next =p ;                    //链接
	length1++;
}
template <class T>
void linklist<T>::fadddata(T e)    //头插方法
{
    Node<T>*temp = new Node<T>;   //开辟新节点
    temp->data   =  e;            //保存数据
    Node<T>*p1 = Head;           //建立链接
    temp->next = p1->next;
    p1->next = temp;
    length++;
    
}

接下来是位插入函数

template <class T>
bool linklist<T>::insertdat(T Data,int pos)
{
	if(pos>length1-1||pos<1)return false;   //避免错误的输入
	Node<T>*temp = Head;                    //得到头节点地址
	int k = 0;
	while(k<pos-1){                    //找到要插入节点的前一个节点
		temp = temp->next;
		k++;
	}
	Node<T>*tempneichun = new Node<T>;   //开辟节点
	tempneichun->data = Data;          //保存数据
	tempneichun->next = temp->next;         //链接
	temp->next = tempneichun;
	length1++;
	return true;
}

在下面是定位删除方法

template <class T>
T linklist<T>::deletedat(int pos){
	if(pos>length1-1||pos<1)return 0;   //避免错误的输入
	Node<T>*temp = Head;
	int k = 0;
	while(k<pos-1)
	{
		temp = temp->next;    //找到要插入节点的前一个节点
		k++;
	}
	Node<T>*p = temp->next;   //得到要删除节点得地址
	Node<T>*t1 = temp->next;   //得到要删除节点得地址
	Node<T>*t2 = t1->next;     //得到要删除节点得下一个节点地址
	temp->next = t2;          //链接前后节点
	T temp2 = p->data;       //返回要删除得数据到主函数
	delete p;               //释放节点
	t1 = NULL;
	return temp2;      //返回要删除得数据到主函数
	length1--;           //长度减1
}

下面给出我之前写得源码

#include <iostream>
#include <string>
using namespace std;         //泛型链表 
template <class T>
struct Node{
	T data;
	
	Node<T> *next;
	
};
template <class T>
class linklist{
	private:
	
	Node<T> * Head;
	
	int length1;
	public:
	linklist();
	~linklist();
	void adddate(T Data);            //尾插函数 
	bool insertdat(T Data,int pos);   //插入函数 
	T deletedat(int pos);             //删除函数 ,会返回删除值 
	void displaylist();                //显示函数 
	int howlongitis();                 //返回链表得长度 
	void fadddata(T e);                //头插函数   
	void linkcombine(linklist*s1,linklist*s2);//合表函数 
};
template <class T>
linklist<T>::linklist(){
	
	Head = new Node<T>;
	
	Head->next = NULL;
	
	length1 = 0;
	
}
template <class T>
void linklist<T>::adddate(T Data)
{
	Node<T> *p = new Node<T>;
	p->data = Data;
	p->next = NULL;
	Node<T>*p1 = Head;
	
	while(p1->next!=NULL){
		p1 = p1->next;
	}
	
	
	p1->next =p ;
	length1++;
}
template <class T>
void linklist<T>::displaylist(){
	Node<T>*temp = Head->next;
	while(temp!=NULL)
	{
		cout<<temp->data<<'\n'<<endl;
		temp = temp->next;
	}
	
}
template <class T>
void linklist<T>::fadddata(T e)    //头插方法
{
    Node<T>*temp = new Node<T>;   //开辟新节点
    temp->data   =  e;            //保存数据
    Node<T>*p1 = Head;           //建立链接
    temp->next = p1->next;
    p1->next = temp;
    length1++;
    
}
template <class T>
void linklist<T>::linkcombine(linklist*s1,linklist*s2)
{
	Node<T>*p = s1->Head;
	Node<T>*p2 = s2->Head->next;
	while(p->next!=NULL)
	{
		p = p->next;
	}
	p->next = p2;
	s1->length1+=s2->length1; 
}
template <class T>
linklist<T>::~linklist()
{
	Node<T>*temp = NULL;
	Node<T>*p = Head->next;
	while(p!=NULL){
		temp = p->next;
		delete p;
		p = temp;
	}
	delete temp;
	temp = NULL;
	delete Head;
	Head = NULL;
	cout<<"................."<<endl;
	length1 = 0;
}
template <class T>
int linklist<T>::howlongitis()
{
	return length1;
}
template <class T>
T linklist<T>::deletedat(int pos){
	if(pos>length1-1||pos<1)return 0;
	Node<T>*temp = Head;
	int k = 0;
	while(k<pos-1)
	{
		temp = temp->next;
		k++;
	}
	Node<T>*p = temp->next;
	Node<T>*t1 = temp->next;
	Node<T>*t2 = t1->next;
	temp->next = t2;
	T temp2 = p->data;
	delete p;
	
	return temp2;
	length1--;
}
template <class T>
bool linklist<T>::insertdat(T Data,int pos)
{
	if(pos>length1-1||pos<1)return false;
	Node<T>*temp = Head;
	int k = 0;
	while(k<pos-1){
		temp = temp->next;
		k++;
	}
	Node<T>*tempneichun = new Node<T>;
	tempneichun->data = Data;
	tempneichun->next = temp->next;
	temp->next = tempneichun;
	length1++;
	return true;
}
int main()
{
	linklist<string>*l1 = new linklist<string>;
	l1->fadddata("456");
	l1->fadddata("789");
	l1->fadddata("123");
	l1->displaylist();
	linklist<int>*o2 = new linklist<int>;
	o2->adddate(9);
	o2->adddate(8);
	o2->adddate(7);
	o2->displaylist();
	int i = o2->deletedat(2);
	cout<<"...."<<i<<endl; 
	l1->insertdat("sadweq",2);
	l1->displaylist();
}

现在终于写完了,哈哈哈,对这些简单数据结构得了解可以加深对它得印象,是去了解其他更高级高层结构的基础,
希望这两篇文章对读者有帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

罗马苏丹默罕默德

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

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

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

打赏作者

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

抵扣说明:

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

余额充值