数据结构之——双向链表完备版(C++实现)

双向链表

双向链表

接口设计

        双向链表和单向链表对应的接口无太多差别,只是在节点类中添加了prev指针和在链表类中添加了last尾指针

#pragma once
#pragma once
#include <iostream>
using namespace std;


template<class E>
class Node
{
public:
	E element;
	Node<E>* next;
	Node<E>* prev;
	Node(Node<E>* prev, E element, Node<E>* next)
	{
		this->element = element;
		this->next = next;
		this->prev = prev;
	}
	~Node() {}
};

template<class E>
class Circle_DouLinkList
{
private:
	int size;
	Node<E>* first = NULL;
	Node<E>* last = NULL;

	static const int ELEMENT_NOT_FOUND = -1;

	Node<E>* find_node(int index);
public:
	void clear();
	void add(E element);
	void add(int index, E element);
	int Size();
	int indexOf(E element);
	bool isEmpty();
	bool contains(E element);
	E get(int index);
	E set(int index, E element);
	E remove(int index);
	void print();

	~Circle_DouLinkList(){}
};

难点讲解

①添加元素void add(int index, E element);

因为涉及头节点和尾节点,所以要分中间、头、尾三种情况来讨论

1、中间

      假设在位置2处添加元素中间添加元素
        首先将添加位置2节点的前一个节点1处的next指针指向新元素,因为存在prev指针,所以不需要再像单向链表中一样再去找前一个节点,只需要通过结点2的prev指针就可以找到前一个节点。然后将节点2的prev指针指向新元素。最后将所添加节点的prev指针指向节点1,next指针指向节点2。效果如下图所示。
中间添加元素

Node<E>* next = find_node(index);
Node<E>* prev = next->prev;
Node<E>* p = new Node<E>(prev, element, next);
prev->next = p;
next->prev = p;
2、头部

        由于前面的代码设计的prev指针,所以要考虑prev指针为空的情况,即在头节点添加元素。因此我们要加上对于在头节点添加元素的讨论。
头部添加

Node<E>* next = find_node(index);		//0
Node<E>* prev = next->prev;				//NULL
Node<E>* p = new Node<E>(prev, element, next);
next->prev = p;
if (prev == NULL)		//如果index==0
{
	first = p;
}
else
{
	prev->next = p;
}

        与上面在中间节点添加元素是一个思路,只需在在对于prev的操作进行一个讨论,前面的代码可以共用。将往节点0处添加节点为例子 。
        第一句代码next指针 获得的是节点0 ,第二句代码prev指针获得的是节点0的前一个节点,便是NULL。然后将prev,element,next分别赋值给新节点p,等价于Node* p = new Node(NULL, element, 0),然后将next指针即节点0的prev指向新节点,最后判断prev节点是否为空,即是否插入在头部,如果是在头部就将头指针指向新节点。
头节点插入

3、尾部
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;由于还存在尾节点,因此我们仍需要对于尾节点的情况进行讨论	

尾部添加
        首先要将尾节点指向新添加的元素顺便将新元素构造出来。新添加节点的prev指针就是之前的尾指针,next指针就是NULL指针。

last = new Node<E>(last, element, NULL);

尾部添加
        最后只剩下之前的尾节点的next指针指向新添加节点的这一条线。若是使用last->next=p这一句代码,则last指针在之前前面的代码就已指向新的元素,因此需要创建一个新的临时指针Node *Old_last = last;来保存之前的最后一个节点。

Node<E> *Old_last = last;
last = new Node<E>(Old_last, element, NULL);
Old_last->next = last;

ps:也可以通过新的last->prev来获取原先的尾节点

        但是讨论到这里的代码的健壮性仍是不够完善,因为用到了next指针而未考虑到next指针为NULL的可能性 ,即当是一个空链表(一个元素都未添加的情况时)
添加为第一个元素时
        因此仍旧需要再增加一个当添加元素为第一个元素的讨论
        因为是添加的第一个元素,所以应该将first和last都指向这个新元素,而在前面的代码已经实现其余的连接,即在last = new Node(Old_last, element, NULL);代码中代表的就是last = new Node(NULL, element, NULL);,因此只需要添加一句first=last即可
只有一个元素

Node<E> *Old_last = last;
		last = new Node<E>(Old_last, element, NULL);
		if (Old_last == NULL)		//如果是链表添加的第一个元素
		{
			first = last;
		}
		else
		{
			Old_last->next = last;
		}
4、总代码
template<class E>
void DouLinkList<E>::add(int index, E element)		//在某节点添加元素
{
	if (index == size)		//在尾节点添加元素
	{
		Node<E> *Old_last = last;
		last = new Node<E>(Old_last, element, NULL);
		if (Old_last == NULL)		//如果是链表添加的第一个元素
		{
			first = last;
		}
		else
		{
			Old_last->next = last;
		}
	}
	else
	{
		Node<E>* next = find_node(index);
		Node<E>* prev = next->prev;
		Node<E>* p = new Node<E>(prev, element, next);
		next->prev = p;
		if (prev == NULL)		//如果index==0
		{
			first = p;
		}
		else
		{
			prev->next = p;
		}
	}

	size++;
}

①删除元素remove(int index)

1、中间节点

删除中间元素
        假设删除的是位置2的节点。与单链表的删除方法相似,只需要用一个节点保存被删除节点的元素来返回,然后找到要删除节点的前一个节点,将前一个节点的next指针指向下下一个节点,多了一点的是需要将所删除节点的prev指针指向删除节点的前一个节点,最后再把所删除的节点delete就可以了。
删除中间节点

2、 头、尾节点

对于头与尾删除的特殊情况就在于头节点的prev指针指向为空和尾节点的next指针为空
头、尾删除
        对于头节点是将first指针指向删除节点0的下一个节点1,再将节点1的prev指针指向NULL,而后一句的代码利用中间节点删除的代码就可完成。
        而尾节点的删除与头节点的删除几乎无区别,就不多赘述了。

3、总代码
template<class E>
E  LinkList<E>::remove(int index)		//移除某个元素,返回被移除的元素  要讨论是否为头节点
{

	Node<E>* p;		//创建一个节点,用来返回被移除的元素

	if (index == 0)		//若为头节点直接指向下个节点
	{
		p = first;
		first = first->next;
		size--;
	}
	else		//若不是头节点,让上一个节点指向这个节点的下一个节点
	{
		Node<E>* prev = find_node(index - 1);		//获得前一个节点
		p = prev->next;		//获得index节点
		prev->next = prev->next->next;		//前一个节点指向index后面这个节点
		size--;
	}

	E x = p->element;
	delete p;
	return x;
}

⚪总代码

Dou_Link_List.h

#pragma once
#include <iostream>
using namespace std;


template<class E>
class Node
{
public:
	E element;
	Node<E>* next;
	Node<E>* prev;
	Node(Node<E>* prev,E element, Node<E>* next)
	{
		this->element = element;
		this->next = next;
		this->prev = prev;
	}
	~Node() {}
};

template<class E>
class DouLinkList
{
private:
	int size;
	Node<E>* first=NULL;
	Node<E>* last=NULL;

	static const int ELEMENT_NOT_FOUND = -1;

	Node<E>* find_node(int index);
public:
	void clear();		//清除所有元素
	void add(E element);		//在末尾添加元素
	void add(int index, E element);		//在index处添加元素
	int Size();		//求size
	int indexOf(E element);		//求某元素的位置
	bool isEmpty();		//链表是否为空
	bool contains(E element);		//某元素是否存在
	E get(int index);		//获得某节点的元素
	E set(int index, E element);		//设置某节点的元素
	E remove(int index);		//删除某节点的元素
	void print();		//打印

	~DouLinkList() {}
};

Dou_Link_List.cpp

#include"Dou_LinkList.h"

template<class E>
void DouLinkList<E>::clear()		//清除所有元素
{
	Node* q;
	Node<E>* p = first->next; 
	while (p != NULL)
	{
		q = p;
		p = p->next;
		delete q;
	}
	p = NULL;
	q = NULL;
}

template<class E>
int DouLinkList<E>::Size()		//求出链表的大小
{
	return size;
}

template<class E>
bool DouLinkList<E>::isEmpty()		//链表是否为空
{
	return size == 0;
}

template<class E>
bool DouLinkList<E>::contains(E element)		//某元素是否存在
{
	return indexOf(element) != -ELEMENT_NOT_FOUND;
}

template<class E>
void DouLinkList<E>::add(E element)		//在尾节点添加元素
{
	add(size, element);
}

template<class E>		//获得某元素的位置
int DouLinkList<E>::indexOf(E element)
{
	if (element == NULL)
	{
		Node<E>* node = first;
		for (int i = 0; i < size; i++)
		{
			if (node->element == NULL)
			{
				return i;
			}
			node = node->next;
		}
	}
	else
	{
		Node<E>* node = first;
		for (int i = 0; i < size; i++)
		{
			if (node->element==element)
			{
				return i;
			}
			node = node->next;
		}
	}
	return ELEMENT_NOT_FOUND;
}

template<class E>
E DouLinkList<E>::get(int index)		//获得某节点的元素
{
	return find_node(index).element;
}

template<class E>
E DouLinkList<E>::set(int index, E element)		//更改设置某节点元素
{
	Node<E>* node = find_node(index);
	E old = node->element;
	node->element = element;
	return old;
}

template<class E>
void DouLinkList<E>::add(int index, E element)		//在某节点添加元素
{
	if (index == size)		//在尾节点添加元素
	{
		Node<E> *Old_last = last;
		last = new Node<E>(Old_last, element, NULL);
		if (Old_last == NULL)		//如果是链表添加的第一个元素
		{
			first = last;
		}
		else
		{
			Old_last->next = last;
		}
	}
	else
	{
		Node<E>* next = find_node(index);
		Node<E>* prev = next->prev;
		Node<E>* p = new Node<E>(prev, element, next);
		if (prev == NULL)		//如果index==0
		{
			first = p;
		}
		else
		{
			prev->next = p;
		}
		next->prev = p;
	}

	size++;
}

template<class E>
E DouLinkList<E>::remove(int index)			//删除某节点的元素
{
	Node<E>* p = find_node(index);
	Node<E>* prev = p->prev;
	Node<E>* next = p->next;
	if (prev == NULL)		//index==0
	{
		first = next;
	}
	else
	{
		prev->next = next;
	}
	if (next == NULL)		//index==size-1
	{
		last = prev;
	}
	else
	{
		next->prev = prev;
	}
	E x = p->element;
	delete p;
	return x;
}


template<class E>
void DouLinkList<E>::print()
{
	Node<E>* p = first;
	while (p!=NULL)
	{
		cout << p->element << "->" ;
		p = p->next;
	}
}

template<class E>
Node<E>* DouLinkList<E>::find_node(int index)
{
	if (index < 0 || index >= size)
	{
		throw "index不符合规范";
	}

	if (index < (size >> 1))
	{
		Node<E>* node = first;
		for (int i = 0; i < index; i++)
		{
			node = node->next;
		}
		return node;
	}
	else 
	{
		Node<E>* node = last;
		for (int i = size-1; i > index; i--)
		{
			node = node->prev;
		}
		return node;
	}
}

main.cpp

#include <iostream>
#include "Dou_LinkList.h"
#include "Dou_LinkList.cpp"

int main()
{
    DouLinkList<int> d_list;
    d_list.add(10);
    d_list.add(20);
    d_list.add(30);
    d_list.add(40);
    d_list.add(50);
    d_list.add(60);
    d_list.add(70);
    d_list.print();
    cout << endl;
    cout << d_list.remove(2) << endl;
    cout << d_list.set(3,10) << endl;
    cout << d_list.indexOf(60) << endl;
    d_list.print();
    return 0;
}

⚪对比

动态数组和双向链表的对比和使用
对比
附:
1.本博客是由学习小码哥视频所总结文章
2.代码都通过合格检测,请放心食用~

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

GXM.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值