双向链表的基本操作

双向链表,在进行数据的存储时不光存储数据和后继节点,还要有一块区域存储前驱节点。在插入和删除时不需要额外的代码来寻找第i-1个节点,直接使用此节点的前驱节点就可以找到它的上一个节点。具体结构如下图所示。

双向链表的节点

双向链表的节点包含三个部分,分别是数据域,前驱域和后继域。

//双向链表的节点
template <typename datatype>
struct two_way_listnode
{
	datatype value;//存储的值
	two_way_listnode* prior;//前驱节点
	two_way_listnode* next;//后继节点
};

双向链表类

双向链表的私有成员有一个指向头结点的头指针head,成员函数有无参构造函数,有参构造函数(头插或尾插),打印双向链表的函数(遍历),在第i个节点插入的函数,求双向链表的长度的函数,按位序查找的函数,按值查找的函数,判空函数,删除双向链表的节点的函数,析构双向链表的函数。

//双向链表类
template <typename datatype>
class two_way_list
{
public:
	two_way_list();//无参构造函数,建立空链表
	two_way_list(datatype [],int,int);//有参构造函数,用(数组+头插)的形式来构建链表
	two_way_list(datatype[], int);//有参构造函数,用(数组+尾插)的形式来构建链表
	void printlist();//打印双向链表
	void insert_two_way_list(int,datatype);//双向链表在第i个节点插入(不需要提前找第i个节点)
	int length();//求双向链表的长度
	datatype get(int);//按位序查找
	int locate(datatype);//按值查找
	bool empty();//判空函数
	void delete_two_way_list(int);//删除双向链表的节点
	~two_way_list();//析构双向链表
private:
	two_way_listnode<datatype>* head;
};

无参构造函数

创建一个只有头结点的空链表,即将头结点的前驱和后继都指向空。

//无参构造函数,建立空链表
template <typename datatype>
two_way_list<datatype>::two_way_list()
{
	head = new two_way_listnode<datatype>;
	head->prior = NULL;
	head->next = NULL;
}

有参构造函数(数组+头插)

每次从链表的头部进行插入,在进行插入时有四条链的变换。值得注意的是,如果待插入的元素是第一个元素,那么不需要将它的下一个元素的前驱指向被插入的这个元素(因为被插入的这个元素的下一个元素是空指针,不存在前驱),如果不是第一个元素,则进行头插即可。

//有参构造函数,用(数组+头插)的形式来构建空表
template<typename datatype>
two_way_list<datatype>::two_way_list(datatype a[], int n,int x)
{
	head = new two_way_listnode<datatype>;
	head->prior = NULL;
	head->next = NULL;
	for (int i = 0; i < n; i++)
	{
		two_way_listnode<datatype>* p = new two_way_listnode<datatype>;
		p->value = a[i];
		p->next = head->next;
		if(head->next!=NULL)//判断是否是第一个新的节点
           head->next->prior=p;
        p->prior = head;
        head->next = p;
	}
}

有参构造函数(数组+尾插)

在尾部进行插入时,每次待插入元素的下一个元素都是空指针,因此不需要像头插那样有一步单独判断的过程,直接在尾部进行插入即可。值得注意的是,这里需要定义一个尾指针,并不断将其进行更新,以确保每次都是从尾部插入。

//有参构造函数,用(数组+尾插)的形式来构建链表
template<typename datatype>
two_way_list<datatype>::two_way_list(datatype a[], int n )
{
	head = new two_way_listnode<datatype>;
	head->prior = NULL;
	head->next = NULL;
	two_way_listnode<datatype>* tail = head;
	for (int i = 0; i < n; i++)
	{
		two_way_listnode<datatype>* p = new two_way_listnode<datatype>;
		p->value = a[i];
		p->next = tail->next;
		p->prior = tail;
		tail->next = p;
		tail = p;
	}
}

打印双向链表

对双向链表进行遍历。

//打印双向链表
template <typename datatype>
void two_way_list<datatype>::printlist()
{
	two_way_listnode<datatype>* cur = head->next;
	while (cur != NULL)
	{
		cout << cur->value << "->";
		cur = cur->next;
	}
	cout << "NULL" << endl;
}

在第i个节点插入

第i个节点插入的时候,要留意这个节点是不是最后一个节点,如果是最后一个节点,则有三条链需要被改变,如果不是最后一个元素,则有四条链会发生改变。也就是说,尾插的话需要单独处理。使用双向链表进行插入时,不需要提前找第i-1个节点,只需要找到第i个节点然后对其前驱和后继节点进行改变即可。

//双向链表在第i个节点插入,不需要提前找第i个节点(尾插需要单独处理)
template<typename datatype>
void two_way_list<datatype>::insert_two_way_list(int i, datatype e)
{
	if (i < 1) throw"插入位置错误";
	two_way_listnode<datatype>* cur = head->next;
	int count = 1;
	while (count < i && cur->next!= NULL)
	{
		cur= cur->next;
		count++;
	}
	two_way_listnode<datatype>* p = new two_way_listnode<datatype>;
	p->value = e;
	if (cur->next == NULL)
	{
		p->next = NULL;
		cur->next = p;
		p->prior = cur;
	}
	else
	{
      p->next = cur;
	  p->prior = cur->prior;
	  cur->prior->next = p;
	  cur->prior = p;
	}
}

求双向链表的长度

即对双向链表进行遍历,并记录链表的长度

//求双向链表的长度
template<typename datatype>
int two_way_list<datatype>::length()
{
	int length = 0;
	two_way_listnode<datatype>* cur = head->next;
	while (cur != NULL)
	{
		cur = cur->next;
		length++;
	}
	return length;
}

按位序查找

按照位置查找链表中相应的元素,如果找到,返回其元素值。

//按位序查找
template<typename datatype>
datatype two_way_list<datatype>::get(int i)
{
	two_way_listnode<datatype>* cur = head->next;
	int count = 1;
	while (cur != NULL && count < i)
	{
		cur = cur->next;
		count++;
	}
	if (cur == NULL) return 0x3f3f3f3f;
	else return cur->value;
}

按值查找

在链表中查找相应的值所在的位置。

//按值查找
template<typename datatype>
int two_way_list<datatype>::locate(datatype x)
{
	two_way_listnode<datatype>* cur = head->next;
	int count = 1;
	while (cur != NULL)
	{
		if (cur->value == x)
		{
			return count;
		}
       
          count++;
		  cur = cur->next;
	}
      if (cur->next == NULL) return -1;
}

判空

如果头指针的前驱和后继都是空指针,则链表为空,只有头指针。

//判空函数
template<typename datatype>
bool two_way_list<datatype>::empty()
{
	if (head->next == NULL && head->prior == NULL) return false;
	else return true;
}

删除双向链表的第i个节点

删除双向链表的第i个节点同样需要考虑是否是最后一个元素的情况,如果是最后一个元素,则无需考虑前驱链的指向,直接让它的上一个元素的next直接指向空指针即可,如果不是最后一个元素,那么需要有前驱和后继两条链的改变,让其正好跳过待删除的元素。

//删除双向链表的第i个节点
template<typename datatype>
void two_way_list<datatype>::delete_two_way_list(int i)
{
	two_way_listnode<datatype>* cur = head->next;
	int count = 1;
	while (cur != NULL && count < i)
	{
		cur = cur->next;
		count++;
	}
	if (cur == NULL) throw "位置有误";
	if (cur->next != NULL)
	{
      cur->next->prior = cur->prior;
	  cur->prior->next = cur->next;
	  delete cur;
	}
	else if (cur->next == NULL)
	{
		cur->prior->next = NULL;
		delete cur;
	}
}

双向链表的析构函数

从头节点开始析构每一个节点,直到只有空指针,则析构完毕。

template<typename datatype>
two_way_list<datatype>::~two_way_list()
{
	two_way_listnode<datatype>* cur = head;
	while (cur != NULL)
	{
		head=head->next;  
		delete cur;
		cur = head;
	}
	head = NULL;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值