30.数据结构-双向链表的实现

单链表的缺陷

单链表要想高效的访问链表中的元素,需要从头结点开始,如果需要逆向访问链表中的元素,这时候效率非常低下
在这里插入图片描述

双向链表

在单链表的结点中增加一个指针pre,用于指向当前结点的前驱结点,通过next指针域可以高效访问后继结点,通过pre指针域可以高效访问前驱结点,不管结点处于哪个位置都可以高效的访问前驱和后继结点
在这里插入图片描述

双向链表的继承层次结构

在这里插入图片描述
双向链表和单链表在内部实现上完全不同,所以不应该是继承关系,而应该是兄弟关系

双向链表的定义

在单链表的基础上新增加一个前驱指针域,
在这里插入图片描述

双向链表的插入操作

新结点的next指针指向next结点
在这里插入图片描述
current结点的next指针指向node结点,current结点与next结点断了联系
在这里插入图片描述
node结点的pre指针指向current结点,next结点的pre指针指向node
在这里插入图片描述

bool insert(int i, T& e)
	{
    
    
		bool ret = (i >= 0) && (i <= m_length);
		if (ret)
		{
    
    
			Node* node = create();

			if (node != NULL)
			{
    
    
				//找到插入的位置
				Node* current = position(i);
				Node* next	  = current->next;
				node->value   = e;
				node->next	  = next; // 第1步
				current->next = node;//第2步
				//插入的位置为头结点
				if (current != position(0))
				{
    
    
					node->pre = current;
				}
				else
				{
    
    
					node->pre = NULL;
				}
				//如果插入的位置是尾结点
				if (next != NULL)
				{
    
    
					next->pre = node;
				}
			
				m_length++;

			}
			else
			{
    
    
				cout << "no memery to malloc" << endl;
			}
		}

双向链表的删除

在这里插入图片描述
第一步:找到待删除结点的前一个结点,即current结点
第二步:将current结点的next指针指向待删除结点的下一个结点,即next结点
第三步:将next结点的pre指针指向current结点

bool remove(int i)
	{
    
    
		bool ret = (i >= 0) && (i <= m_length);

		if (ret)
		{
    
    
			//找到删除位置的前一个元素
			Node* current = position(i);
			Node* toDel = current->next;
			Node* next = toDel->next;
			
			if (m_current == toDel)
			{
    
    
				m_current = next;//游标指向下一个结点
			}

			current->next = next;

			//删除的位置为尾结点
			if (next != NULL)
			{
    
    
				next->pre = current;
			}

		    destroy(toDel);
			m_length--;
		}

完整代码

#ifndef  __DUAL_LINK_LIST_
#define  __DUAL_LINK_LIST_

#include <iostream>
using namespace std;

template<class T,int N>
class DualLinkList
{
    
    
public:
	struct Node
	{
    
    
		T	  value;
		Node* next;
		Node* pre;
	};

	mutable struct
	{
    
    
		char reverse[sizeof(T)];
		Node* next;
		Node* pre;
	} m_header;

	int m_length;
	int m_step;
	Node* m_current;

	virtual Node* create()
	{
    
    
		return new Node();
	}


	void destroy(Node* pn)
	{
    
    
		delete pn;
	}


public:

	DualLinkList()
	{
    
    
		m_header.next = NULL;
		m_header.pre  = NULL;
		m_length = 0;
		m_step = 0;
		m_current = NULL;
	}

	Node* position(int i)const
	{
    
    
		Node* pre = reinterpret_cast<Node*>(&m_header);
		for (int pos = 0; pos < i; pos++)
		{
    
    
			pre = pre->next;
		}

		return pre;
	}



	int find(const T& e)const
	{
    
    
		Node* pre = reinterpret_cast<Node*>(&m_header);
		int i = 0;

		while (e != pre->next->value)
		{
    
    
			pre = pre->next;
			i++;
		}

		return i;
	}


	bool end()
	{
    
    
		return m_current == NULL;
	}



	bool move(int i, int step = 1)
	{
    
    
		bool ret = (i >= 0) && (i <= m_length);
		if (ret)
		{
    
    
			m_current = position(i)->next;
			m_step = step;
		}

		return ret;
	}

	T current()
	{
    
    

		if (!end())
		{
    
    
			return m_current->value;
		}
		else
		{
    
    
			cout << "current end()" << endl;
			return -1;//不知道写啥值
		}
	}


	bool next()
	{
    
    
		int i = 0;
		while (!end() && i < m_step)
		{
    
    
			m_current = m_current->next;
			i++;
		}

		return i == m_step;
	}



	bool insert(int i, T& e)
	{
    
    
		bool ret = (i >= 0) && (i <= m_length);
		if (ret)
		{
    
    
			Node* node = create();

			if (node != NULL)
			{
    
    
				//找到插入的位置
				Node* current = position(i);
				Node* next	  = current->next;
				node->value   = e;
				node->next	  = next; // 第1步
				current->next = node;//第2步
				//插入的位置为头结点
				if (current != position(0))
				{
    
    
					node->pre = current;
				}
				else
				{
    
    
					node->pre = NULL;
				}
				//如果插入的位置是尾结点
				if (next != NULL)
				{
    
    
					next->pre = node;
				}
			
				m_length++;

			}
			else
			{
    
    
				cout << "no memery to malloc" << endl;
			}
		}

		return ret;
	}



	bool remove(int i)
	{
    
    
		bool ret = (i >= 0) && (i <= m_length);

		if (ret)
		{
    
    
			//找到删除位置的前一个元素
			Node* current = position(i);
			Node* toDel = current->next;
			Node* next = toDel->next;
			
			if (m_current == toDel)
			{
    
    
				m_current = next;//游标指向下一个结点
			}

			current->next = next;

			//删除的位置为尾结点
			if (next != NULL)
			{
    
    
				next->pre = current;
			}

		    destroy(toDel);
			m_length--;
		}

		return ret;
	}


	bool set(int i, T& e)
	{
    
    
		bool ret = (i >= 0) && (i <= m_length);

		if (ret)
		{
    
    
			Node* pre = reinterpret_cast<Node*>(&m_header);

			pre = position(i);

			pre->next->value = e;
		}

		return ret;
	}


	T get(int i) const
	{
    
    
		bool ret = (i >= 0) && (i <= m_length);
		T e;

		if (ret)
		{
    
    
			e = position(i)->next->value;
		}

		return e;
	}



	int length() const
	{
    
    
		return m_length;
	}


	void clear()
	{
    
    

		while (m_length > 0)
		{
    
    
		
			remove(0);
		}
	}

	//找到当前结点的前一个结点,m_step为每一次移动的步数
	virtual bool pre()
	{
    
    
		int i = 0;
		while( (i < m_step) && !end())
		{
    
    
			m_current = m_current->pre;
			i++;
		}

		return (i == m_step);
	}

	~DualLinkList()//O(n)
	{
    
    
		clear();
	}

};



#endif

测试代码1-遍历链表元素

#include "DualLinkList.h"
#include <iostream>

int main()
{
    
    
	DualLinkList<int, 5> sl;

	for (int i = 0; i < 5; i++)
	{
    
    
		sl.insert(0, i);
	}

	//
	for (int i = 0; i < sl.length(); i++)
	{
    
    
		cout << sl.get(i) << endl;
	}

	cout << "begin()" << endl;

	//先定位到尾结点,通过pre指针定位元素
	for (sl.move(sl.length()-1); !sl.end(); sl.pre())
	{
    
    
		cout << sl.current() << endl;
	}

	cout << "end()" << endl;

	return 0;
}

第一个for循环时间复杂度是O(n*n),是非线性的,第二个for循环的时间复杂度是O(n)。
结果:
在这里插入图片描述

测试代码2-删除链表值中数据元素为5的结点

#include "DualLinkList.h"
#include <iostream>

int main()
{
    
    
	DualLinkList<int, 5> sl;
	int j = 5;

	for (int i = 0; i < 5; i++)
	{
    
    
		sl.insert(0, i);
		sl.insert(0, j);
	}

	for (sl.move(0);!sl.end(); sl.next())
	{
    
    
		cout << sl.current() << endl;
	}

	sl.move(sl.length() - 1);//定位到最后一个元素
	cout << "begin()" << endl;

	while (!sl.end())
	{
    
    
		if (sl.current() == 5)
		{
    
    
			cout << sl.current() << endl;
			sl.remove(sl.find(sl.current()));
		}
		else
		{
    
    
			sl.pre(); //定位到前驱结点
		}
	}


	for (sl.move(0); !sl.end(); sl.next())
	{
    
    
		cout << sl.current() << endl;
	}
	cout << "end()" << endl;


	return 0;
}

结果:
在这里插入图片描述

小结

  • 双向链表的诞生是为了弥补单链表的缺陷而设计
  • 双向链表的游标可以直接访问当前结点的前驱和后继结点
  • 双向链表是线性标概念的最终实现

おすすめ

転載: blog.csdn.net/weixin_43824344/article/details/121451187