重学数据结构之链表

鉴于我个人顺序存储时不使用顺序表,而常常用数组替代(并非好习惯),直接来说链表。

链表,即使用链式存储结构的线性表。可以想象用一根链条将不同位置处的信息连接起来,则链表中相邻元素在内存中可能不相邻。链表的连接,则由元素的指针域完成。根据指针域,可分为单向链表、双向链表、循环链表等等等。

一、单链表(SList)

每个元素(也称之为结点)包含两部分,值与指针,图上默认的是数值型数据,头指针指向第一个结点,结点的指针指向它的下一个结点,则最后一个结点指向空(NULL)。

关于单链表的代码表示,是基于结点的表示的。由于结点元素类型不确定,故采用C++模板机制编写,如下:

// C++模板机制
template <class T>
struct SListNode
{
	T data; 		//值
	SListNode<T> *next;//指向下一结点
};

结点已经表示完毕,接下来就是单链表类的属性与方法的代码,同样是用C++实现的,如下:

我设置了一个额外的头指针始终指向第一个结点,若无结点,则指向NULL。

//单链表类
template <class T>
class SList
{
	public:
		SList();	//无参构造
		SList(T a[], int n);//有参构造
		~SList();	//析构函数 
		void output();	//遍历整个链表并打印
		bool isEmpty();	//判断链表是否为空
		T get(int i);	//按位查找 
		SListNode<T> *find(T x);//查找第一个值为x的结点
		int location(T x);	//查找第一个值为x的结点的序数
		void deleteNode(int i);	//删除序数为i的结点
		void deleteNode(T x);	//删除第一个值为x的结点 
		void insert(int i, T x);//在第i个位置插入元素
		int length();	//返回链表长度
	private:
		SListNode<T> *head;	//头结点
};

关于构造函数的编写如下:

template <class T>
SList<T>::SList()
{
	head = new SListNode<T>;
	head -> next = NULL;
}
/*
//采用头插法 
template <class T>
SList<T>::SList(T a[], int n)
{	//与无参构造相同
	head = new SListNode<T>;
	head -> next = NULL;
	
	for(int i = 0; i < n; i++)
	{
		SListNode<T> *temp = new SListNode<T>;//临时结点
		temp -> data = a[i];//将数组值赋给该结点
		temp -> next = head -> next;//第一步
		head -> next = temp;//第二步
	}
}*/

其中有参构造部分借助图示可能更好理解(以第一个数据为例)。

-------------------------19.5.20 待续----------------------------

关于头插法,顾名思义,每次将一个数据插到头指针的后面,所以从头指针向后看,顺序是与数组顺序相悖的,即 head ----> a[n-1] ---> a[n-2] ---> ...... --->  a[1] ---> a[0],因而这里注释掉,采用尾插法,即每次插到末指针后。如下:

//采用尾插法 
template <class T>
SList<T>::SList(T a[], int n)
{	//与无参构造相同
	head = new SListNode<T>;
	SListNode<T> *p = head; 
	
	for(int i = 0; i < n; i++)
	{
		SListNode<T> *q = new SListNode<T>;
		q -> data = a[i];
		p -> next = q;	//插到末结点后 
		p = q;		
	}
	p -> next = NULL;	//末结点指向空 
} 

关于析构操作,当用new生成结点时,用delete来释放它,这是一个较好的习惯。

//析构函数
template <class T>
SList<T>::~SList()
{
	while(head -> next != NULL)
	{
		SListNode <T>*p = head;
		head = head -> next;
		delete p;
	}
}	

以及其他方法函数

//遍历整个链表并打印
template <class T>
void SList<T>::output()
{
	SListNode<T> *p = head -> next;
	cout << "head->";
	//不是尾结点点的下一结点
	while(p != NULL)
	{
		cout <<  p -> data << "->";
		p = p -> next;
	}
	cout << "NULL\n";
}
//判断链表是否为空
template <class T>
bool SList<T>::isEmpty()
{
	//不空返回1 
	return (head -> next != NULL); 
}
//按位查找 
template <class T>
T SList<T>::get(int i)
{
	SListNode<T> *p = head -> next;
	int count = 1;
	while(p != NULL && count < i)
	{
		p = p -> next;
		++ count;
	}
	if(p == NULL) throw "位置";	//i超过长度 
	else return p -> data; 
}
//查找第一个值为x的结点
template <class T>
SListNode<T>* SList<T>::find(T x)
{
	SListNode<T> *p = head -> next;
	while(p != NULL)
	{
		if(p ->data == x) return p;
		p = p -> next;
	}
	return NULL;	//未找到
}

//查找第一个值为x的结点的序数
template <class T>
int SList<T>::location(T x)
{
	SListNode<T> *p = head -> next;
	int count = 1;
	while(p != NULL)
	{
		if(p -> data == x) return count;	//找到即可返回 
		p = p -> next;
		++ count;
	}
	return 0;
}

-------------------------19.5.21 待续----------------------------

//删除序数为i的结点
template <class T> 
void SList<T>::deleteNode(int i)
{
	SListNode<T> *p = head; 
	int count = 0;
	while(p != NULL && count < i-1)
	{
		++ count;
		p = p -> next;
	}
	if(p == NULL || p -> next == NULL )throw "位置"; 
	else
	{
		SListNode<T> *q = p ->next;
		p -> next = q -> next;
		delete q;
	}
}	
//删除第一个值为x的结点
template <class T> 
void SList<T>::deleteNode(T x)
{
	SListNode<T> *p = head; 
	while(p != NULL)
	{
		SListNode<T> *q = p -> next;
		if(q -> data == x)
		{
			p -> next = q -> next;
			delete q;
		}
		p = p -> next;
	}
}

//在第i个位置插入元素
template <class T> 
void SList<T>::insert(int i, T x)
{
	SListNode<T> *p = head; 
	int count = 0;
	while(p != NULL && count < i-1)
	{
		++ count;
		p = p -> next;
	}
	if(p == NULL || i < 1)throw "位置"; 
	else
	{
		SListNode<T> *q = new SListNode<T>;
		q -> data = x;
		q -> next = p -> next;
		p -> next = q;
	}
}

//返回链表长度
template <class T>
int SList<T>::length()
{
	SListNode<T> *p = head -> next;
	int count = 0;	//计数器
	 
	//不是最后一个结点 
	while(p != NULL)
	{
		p = p -> next;
		++ count;
	}
	return count;
}

而后,对于链表进行测试使用。

-------------------------19.5.22 待续----------------------------

int main()
{
	//无参构造 
	SList<double> sl;	
	string str = ((sl.isEmpty()==0)? "空" : "非空");
	cout << "sl为空吗?\t" << str << endl;
	
	//sl.insert(0, 0.1);	//错误 
	//sl.insert(4, 4.1);	//错误 
	sl.insert(1, 1.1);
	sl.insert(2, 2.1);
	sl.output();
	
	str = ((sl.isEmpty()==0)? "空" : "非空");
	cout << "插入数据后sl为空吗?\t" << str << endl;
	cout << "插入数据后sl的长度是:\t" << sl.length() << endl;
	
	//有参构造 
	double a[6] = {1.1, 2.1, 3.1, 4.1, 0, 6.1};
	SList<double> SL(a,6);
	SL.output(); 
	
	//按位查找
	cout << "第" << 4 << "个结点值为:\t" << SL.get(4) << endl;
	//查找第一个值为x的结点的序数
	cout << "值为1.1的结点序数为:\t" << SL.location(1.1) << endl;
	cout << "值为2.7的结点序数为:\t" << SL.location(2.7) << endl;
	//删除序数为i的结点
	SL.deleteNode(5);
	cout << "删除第5个结点后\t";
	SL.output(); 
	//删除第一个值为x的结点 
	SL.deleteNode(6.1);
	cout << "删除值为6.1的结点后\t";
	SL.output(); 
}

实验成功!

二、循环链表(CList)

即把尾结点指向第一个结点。形成一个包括所有结点在内的环路。

 
这样的话,找到终端结点同单链表的时间复杂度相同,都为O(n),并没有什么方便可言。但如果设置尾指针,会方便得多。

------------------------19.5.24 待续----------------------------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值