双向链表

双向链表定义

双向链表一般有三个域,为一个数据域和两个指针域,两个指针域分别指向其前一个节点和后一个节点。结构图解如下:
在这里插入图片描述
可以看到,第一个节点没有上一个结点,我们将其last指针域指向NULL,最后一个节点没有下一个结点,我们将其next指针域指向NULL,剩下的节点都是last指针域指向上一个节点,而next指针域指向下一个结点。

双向链表的C++描述

节点类的描述:

template<class T>//声明链表类
class Link;

template<class T>//描述节点(节点类的定义)
class Node{
	friend class Link<T>;
	private:
		Node<T>* last=NULL; //左指针域
		Node<T>* next=NULL; //右指针域
		T data;//数据域
		Node(){}; //构造方法
};

链表类的描述:

template<class T>
class Link{
	public:
		Link(){}
		void Insert(const int k,const T& x);
		void Browser()const;//浏览所有数据
		void Delete(const T& x);//删除数据
		int Length()const;//获取链表长度
	private:
		Node<T>* first=NULL;//指向第一个节点
		Node<T>* ends=NULL;//指向最后一个节点
};
插入方法的实现

插入分三种情况,如果再首位插入结点,那么需要改变first指针,而如果在末尾插入节点,则要改变end指针,剩下的在中间插入的则只需要改变其前后两个节点的信息即可。
首先,在首位插入结点
在这里插入图片描述
继续在尾部插入结点
在这里插入图片描述
由上面可以总结出,当一个节点的右指针域为NULL时,end指针一定指向这个节点,而如果左指针域为空时,那么first指针一定指向这个节点。
代码实现示例:

template<class T>
void Insert(const int k,T& x){
	int length = Length();
	if(k<1 || k>(length+1)){ //参数不合法
		cerr<<"the k is out of bound"<<endl;
		throw *this;
	}
	Node<T>* pr=first;//用来指向要插入位置的前一个位置(当k为1时指向第一个元素)
	for(int i=2;i<k;i++){//将pr指针指向要插入的位置的前一个位置
		pr = pr->next;
	}
	Node<T>* pt = new Node<T>(); //创建节点,封装x
	pt->data = x; 
	if(k==1){
		if(length==0){//将first和end都指向此节点
			first=pt; 
			ends = pt;
		}else{ //将新节点的下一个结点设为原来的first节点,然后再将原来的first节点的上一个结点设为新节点,使first指针指向新的表首节点
			pt->next=pr;
			pr->last=pt; 
			first=pt;
		}
	}else{
		Node<T>* pn = pr->next;
		pr->next = pt;
		pt->last = pr;
		pt->next = pn;
		if(pn==NULL){
			ends = pt;
		}else{
			pn-last=pt;
		}
	}
}
浏览所有数据方法的实现

此方法是用来测试链表的构建及一些操作方法的,所以会对双向链表从正反两个方向分别遍历,以验证节点之间双向的联系。

template<class T>
void Link<T>::Browser(){
	int length = Length();
	if(length==0) throw "this link is empty";
	Node<T>* p = first;
	while(p!=NULL){
		cout<<p->data;
		p = p->next;
		if(p!=NULL){
			cout<<" -> ";
		}
	}
	cout<<endl;
	p = ends;
	while(p!=NULL){
		cout<<p->data;
		p = p->last;
		if(p!=NULL){
			cout<<" <- ";
		}
	}
	cout<<endl;
}
获取数组长度方法的实现

这个和单向链表一样,直到下一个节点为空时停止循环即可。

template<class T>
int Link<T>::Length(){
	int length = 0;
	Node<T>* p = first;
	while(p){
		length++;
		p=p->next;
	}
	return length;
}
删除某个元素的方法的实现

和插入元素一样,在这里,如果删除的是表头节点或者最后一个节点,那么相应的,first和end指针的值就需要做相应的改变,具体图解如下:
在这里插入图片描述
删除最后一个节点的过程如下:
在这里插入图片描述
代码实现如下:

template<class T>
void Link<T>::Delete(const T& x){
    Node<T>* p = first;
    Node<T>* q = ends;
    Node<T>* temp=NULL; //用来暂时保存要删除的节点
    int  length = Length();
    for(int i=0;i<((length/2)+1);i++){
        if(p->data==x){ //找到退出
            temp = p;
            break;
        }
        if(q->data==x){ //找到退出
            temp = q;
            break;
        }
        q = q->last;
        p = p->next;
    }
    //此时,temp保存的是将要删除的节点
    if(temp==first){ //删除首节点
        first = first->next;
        first->last = NULL;
        delete temp;
    }else if(temp==ends){ //删除尾节点
        ends = ends->last;
        ends->next = NULL;
        delete temp;
    }else if(temp!=NULL){ //删除中间的节点
        Node<T>* pr = temp->next; //temp的下一个结点
        Node<T>* pl = temp->last; //temp的上一个结点
        pl->next = pr;
        pr->last = pl;
        delete temp;
    }
}

最后需要注意的是最后一个else中temp!=NULL必须添加,不能只写else,这是为了防止传入的x在链表中原本就没有。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值