1. 程式人生 > 實用技巧 >c++資料結構連結串列和雙頭連結串列的實現

c++資料結構連結串列和雙頭連結串列的實現

連結串列的基本概念不加以贅述,看看雙頭連結串列的定義如下:

在一個長度為L的連結串列List中,Input: head tail --->在List中查詢head和tail值的連結串列的節點,從tail節點往後的連結串列連線到List的頭部去,然後改變tail節點的next-->head,圖如下:

下面看看c++版本的實現:

#ifndef LIST_H_
#define LIST_H_
#include<iostream>
#include<array>
#include<cassert>

template<typename T>
class List {
protected:
	struct Node {
		T data;
		struct Node* parents;
		struct Node* next;
		Node(T d) :data(d) {
			parents = nullptr;
			next = nullptr;

		}
		Node() = default;
	};
	using node = struct Node;
	using pnode = struct Node*;
	void delete_list();
	std::array<pnode, 2>A;//儲存環路的兩個節點
	pnode intersection{ nullptr };//儲存相交位置的節點
	bool flag;//判斷是否存在環路得標誌位
	bool flag2{ false };//判斷是否轉換為雙頭連結串列
	void insert(pnode p);
public:
	pnode phead;
	//雙頭連結串列得頭
	pnode phead1;
	pnode phead2;
	List() = default;//default construct function
	List(pnode p1) :phead(p1), phead1(nullptr), phead2(nullptr) {
		phead = nullptr; phead->next = nullptr; phead->parents = nullptr;
		phead1 = nullptr; phead1->next = nullptr; phead1->parents = nullptr;
		phead2 = nullptr; phead2->next = nullptr; phead2->parents = nullptr;
		A[0] = A[1] = nullptr;
	}
	List(T num);
	List(List<T>& c);

	List<T>& operator=(List<T>& c);

	T& operator[](int index);
	bool INTERSEC() const;//判斷是否相交
	std::size_t Len();//返回連結串列長度
	//轉換為單鏈表
	void conver_to_list();
	void insert(T n);

	pnode find(T data);
	std::size_t find_count(T c);//返回查詢節點比較的次數
	void print_list();

	void  convert_to_double(T m, T n);//轉換為對應得雙頭連結串列
	//找到環路節點
	void find_loop() const;
	bool empty();
	~List() {
		if (flag2 == true)
		{
			conver_to_list();
			delete_list();
		}
		else if (flag2 == false)
		{
			delete_list();
		}
	}
};

template <typename T>
void List<T>::delete_list()
{


	pnode p = nullptr;
	if (phead != NULL)
	{
		while (phead)
		{
			p = phead->next;
			delete phead;
			phead = p;
		}
	}
}
template <typename T>
List<T>::List(T num)
{
	phead = new node(num);
}
template <typename T>
List<T>::List(List<T>& c)
{
	this->phead = c->phead;
}
template <typename T>
List<T>& List<T>::operator=(List<T>& c)
{
	this->phead = c.phead;
	return *this;
}
template <typename T>
T& List<T>::operator[](int index)
{
	if (index < 0)
	{
		std::cout << "INDEX Error" << std::endl;
	}
	else
	{
		auto p2 = this->phead;
		for (size_t i = 0; i < index; i++)
		{
			p2 = p2->next;
		}
		return p2->data;
	}
}
template<typename T>
bool List<T>::INTERSEC() const
{
	if (phead1 == nullptr && phead2 == nullptr)
	{
		throw "Please convert to a double-ended linked list first";
	}
	else {
		if (intersection != nullptr)
		{
			std::cout << "存在相交的節點,相交的節點的值是:" << intersection->data << std::endl;
			return true;
		}
		std::cout << "不相交" << std::endl;
	}
	return false;
}
template<typename T>
inline std::size_t List<T>::Len()
{
	std::size_t size = 0;
	pnode p1 = phead;
	while (p1 != nullptr)
	{
		size++;
	}
	return size;
}
template<typename T>
void List<T>::conver_to_list()
{

	if (phead1 != nullptr || phead2 != nullptr)
	{
		//對於輸入的m==n在連結串列的首尾節點的時候的進行轉換為原來的連結串列
		if (phead1 != nullptr && phead2 == nullptr)
		{
			//沒有環路m==n的時候
			if (flag == false) {//在連結串列尾節點的時候
				if (phead1 == phead)
				{
					phead1 = phead2 = nullptr;
				}
				//在連結串列頭部的時候

				else {
					pnode pcur = phead1;
					while (pcur->next != phead)
					{
						pcur = pcur->next;
					}
					pcur->next = nullptr;
					phead->next = phead1;
					phead->parents = nullptr;
					phead1->parents = phead;
					phead1 = phead2 = nullptr;

				}
			}
			//存在環路
			else if (flag == true)
			{

				assert(A[0] != A[1]);
				//end在連結串列的末尾的時候
				A[1]->next = nullptr;
				phead1 = phead2 = nullptr;


			}

		}
		else if (phead1 == nullptr && phead2 != nullptr)
		{
			assert(flag == true);
			//first在頭部end在中間的時候
			pnode pcur = phead2;
			while (true)
			{
				if (pcur->next == phead)
				{
					pcur->next = nullptr;
					break;
				}
				pcur = pcur->next;
			}
			A[1]->next = nullptr;
			A[1]->next = phead2;
			phead2->parents = A[1];
			phead1 = phead2 = nullptr;

		}

		//對於輸入m==n但是在連結串列的中間位置的時候進行轉化為原來的list
		else if (phead1 != nullptr && phead2 != nullptr)
		{
			if (phead1 != phead2)
			{

				//處理m==n但是在連結串列中間的時候(注意:無環路)
				if (flag == false)
				{
					//先找相同的節點然後把phead2的部分拉過去到後面即可
					pnode pcur1 = phead1;
					pnode pcur2 = phead2;
					pnode pcur3 = nullptr;
					while (true)
					{
						if (pcur1 == pcur2)
						{
							pcur2->next = nullptr;
							pcur3 = pcur1;
							break;
						}


						if (pcur1->next != nullptr)
						{
							pcur1 = pcur1->next;
						}
						else if (pcur2->next != nullptr)
						{
							pcur2 = pcur2->next;
						}
					}

					pnode pcur4 = phead2;
					while (pcur4->next != pcur3)
					{
						pcur4 = pcur4->next;
					}
					pcur4->next = nullptr;
					pcur3->next = phead2;
					phead2->parents = pcur3;


					phead1 = phead2 = nullptr;
				}
				//處理不等於的時候的情形(這是一般的情況下):兩個節點既不相等並且兩個節點都不在頭部或者末尾的地方
				else
				{

					assert(A[0] != A[1]);

					A[1]->next = nullptr;
					//刪除phead2的指向
					pnode pcur1 = phead2;
					while (true)
					{
						if (pcur1->next == intersection)
						{
							pcur1->next = nullptr;
							break;
						}
						pcur1 = pcur1->next;
					}
					A[1]->next = phead2;
					phead2->parents = A[1];

					phead1 = phead2 = nullptr;
				}

			}
			//first在頭部 end在尾部的時候
			else if (phead1 == phead2)
			{
				A[1]->next = nullptr;
				phead->parents = nullptr;
				phead1 = phead2 = nullptr;
			}
		}

		flag2 = false;
	}
	assert(phead1 == nullptr && phead2 == nullptr);
}
template <typename T>
void List<T>::insert(T n)
{

	//尾插法建立雙向連結串列
	if (phead == nullptr)
	{
		phead = new node(n);

	}
	else {
		pnode pnew = new node(n);
		//尋找插入位置
		pnode pcur = phead;
		while (pcur->next != nullptr)
		{
			pcur = pcur->next;
		}
		pcur->next = pnew;
		pnew->parents = pcur;

	}
}
template<typename T>
void List<T>::insert(pnode p)
{
	if (phead == nullptr)
	{
		phead = p;
	}
	else {
		pnode pnew = p;
		//尋找插入位置
		pnode pcur = phead;
		while (pcur->next != nullptr)
		{
			pcur = pcur->next;
		}
		pcur->next = pnew;
		pnew->parents = pcur;

	}
}
template <typename T>
typename List<T>::pnode List<T>::find(T data)
{
	pnode pcur = phead;
	while (pcur != nullptr)
	{
		if (pcur->data == data)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return nullptr;
}
template<typename T>
std::size_t List<T>::find_count(T c)
{
	//確保phead並沒有發生改變
	pnode pcur = phead;
	std::size_t count = 0;
	while (pcur != nullptr)
	{
		count++;
		if (pcur->data == c)
		{
			return count;
		}
		pcur = pcur->next;

	}
	return 0;

}
template <typename T>
void List<T>::print_list()
{
	if (empty())
	{
		std::cout << "This is empty list" << std::endl;
	}
	else
	{

		pnode pcur = phead;
		while (pcur != nullptr)
		{
			std::cout << "data:" << pcur->data << "\t";
			if (pcur->next != nullptr)
			{
				std::cout << "next-data:" << pcur->next->data << "\t";
			}
			if (pcur->parents != nullptr)
			{
				std::cout << "parent-data" << pcur->parents->data << std::endl;
			}

			pcur = pcur->next;
		}
	}
}
template<typename T>
void  List<T>::convert_to_double(T m, T n)
{
	/*T m, n;
	std::cout << "請輸入m,n得值是多少:" << std::endl;
	std::cin >> m >> n;*/
	//查詢對應得節點
	pnode phead_m = find(m);
	pnode phead_n = find(n);





	if (phead_m == nullptr || phead_n == nullptr)
	{
		flag = false;//不存在環路
	}
	//如果查詢到得是一樣值

	else if (phead_m == phead_n)
	{
		flag = false;//不存在環路但是可以建立雙頭連結串列
		//case1 查詢到得節點在list得頭部節點上面
		if (phead_m == phead)
		{
			pnode pcur = phead_m->next;
			phead1 = pcur;
			phead2 = nullptr;
			pcur->parents = nullptr;
			while (pcur->next != nullptr)
			{
				pcur = pcur->next;
			}
			pcur->next = phead_m;
			phead_m->parents = pcur;
			phead_m->next = nullptr;
		}
		//case2 查詢到得兩個節點都是list得尾部
		else if (phead_m->next == nullptr)
		{
			phead1 = phead;
			phead2 = nullptr;
		}
		//case3 查詢得節點既不在連結串列得頭部 也不再連結串列得尾部節點
		else if (phead_m->next != nullptr && phead_m->parents != nullptr)
		{


			std::cout << "In middle with list" << std::endl;
			pnode pcur = phead_m->next;
			phead1 = phead;
			pcur->parents = nullptr;
			phead2 = pcur;
			while (pcur->next != nullptr)
			{
				pcur = pcur->next;

			}
			pcur->next = phead_m;
			//不修改父節點這個時候對於phead_m擁有兩個父節點
			phead_m->next = nullptr;

			//相交
			intersection = phead_m;

		}
	}
	//如果查詢的是兩個完全不一樣的值的時候
	else if (phead_m != phead_n)
	{

		/*一個節點在頭一個節點在尾部的時候
		一個節點在尾部(頭部),另一個節點在中間的時候
		兩個節點都在中間的時候
		判斷哪個節點在前哪個節點在後*/
		//如果不等於必然存在環路
		flag = true;
		//case1判斷前後關係並且進行調整
		pnode first = new node, end = new node;
		std::size_t c1 = find_count(m);
		std::size_t c2 = find_count(n);

		if (c1 > c2)
		{
			first = phead_n;
			end = phead_m;
		}
		else if (c2 > c1)
		{
			first = phead_m;
			end = phead_n;
		}
		//case2 一個節點在頭部一個節點在尾部的時候(不相交)
		//存放環路節點
		A[0] = first;
		A[1] = end;
		std::cout << first->data << "\t" << end->data << std::endl;
		if (first->parents == nullptr && end->next == nullptr)
		{

			first->parents = end;
			end->next = first;
			phead1 = phead2 = first;
		}
		//case3 first節點在頭部,end節點在中間的時候,把end後面的拉過去 end->next=first即可(不相交)
		else if (first->parents == nullptr && end->next != nullptr)
		{

			pnode pre = end->next;
			phead1 = nullptr;//第一個頭節點
			end->next = first;//建立環路
			pnode pre2 = pre;
			while (pre->next != nullptr)
			{
				pre = pre->next;
			}
			pre->next = first;
			pre2->parents = nullptr;
			phead2 = pre2;


		}



		//case4 end在尾部 first在中間的時候(不相交)
		else if (end->next == nullptr && (first->parents != nullptr && first->next != nullptr))
		{
			phead1 = phead;
			end->next = first;
			phead2 = nullptr;

		}
		//case5 first 和end 節點都在中間的時候(相交)
		else if ((first->next != nullptr && first->parents != nullptr) && (end->next != nullptr && end->parents != nullptr))
		{
			phead1 = phead;

			pnode pre = end->next;
			pnode pre2 = pre;

			pre2->parents = nullptr;
			while (pre->next != nullptr)
			{
				pre = pre->next;
			}
			pre->next = first;
			end->next = first;
			first->parents = end;

			phead2 = pre2;
			intersection = first;

		}
	}
	flag2 = true;
}
template<typename T>
void List<T>::find_loop() const
{
	if (flag == false)
	{
		std::cout << "不存在環路" << std::endl;
	}
	else if (flag == true)
	{
		std::cout << "環路的首部是:" << A[0]->data << "\t"
			<< "環路的尾部是:" << A[1]->data << std::endl;
	}

}
template <typename T>
bool List<T>::empty()
{
	if (phead == nullptr)
	{
		return true;
	}
	return false;
}
#endif