1. 程式人生 > >資料結構學習筆記——C++實現雙向迴圈連結串列模板類(超詳解)

資料結構學習筆記——C++實現雙向迴圈連結串列模板類(超詳解)

定義了兩個標頭檔案分別放置結點類模板(Node.h)和雙鏈表模板(DoubleLinkList.h),
然後在原始檔的main函式中測試。

Node.h

#pragma once
# include <iostream>

template <class DataType>
class Node
{
	
public:
	DataType data;
	Node<DataType> *prior;
	Node<DataType> *next;
	Node();
	Node(DataType data_, Node<DataType> *prior_, Node<DataType> *next_);

};
template <class DataType>
Node<DataType>::Node()//定義一個空結點
{
	prior = NULL;
	next = NULL;
}

template <class DataType>
Node<DataType>::Node(DataType data_, Node<DataType> *prior_ , Node<DataType> *next_ )//定義一個完整結點
{
	prior = prior_;
	next = next_;
	data = data_;
}

DoubleLinklist.h

/*
實現雙向迴圈連結串列
實現的運算操作有
 增 ,刪 ,改 ,查 
 初始化, 清空 ,求線性表長度
*/

#include "Node.h"
using namespace std;
template <class DataType>
class DoubleLinkList
{
	int length;
	Node<DataType> *head;
public:
	DoubleLinkList();//初始化————初始化一個空表,
	DoubleLinkList(const  DataType data_[],int n);//初始化————初始化一個含有資料的表
	void Insert(const DataType data_, int pos);//增————向某個位置pos之前插入一個數據data_
	void Delete(int pos);//刪————刪除某個位置pos處的結點
	void Change(const DataType data_,int pos);//改————改動某個位置pos處的結點
	DataType Search1(int pos);//查————根據下標查資料
	int Search2(const DataType &e);//查————根據資料查下標
	void Clear();//清空————僅保留頭節點
	int GetLength();//得到長度
	void PrintAll();//遍歷連結串列輸出各節點數值
	~DoubleLinkList();//析構————刪除所有結點,釋放所有指標

};


//初始化————初始化一個空表(只有頭節點)
template <typename  DataType>
DoubleLinkList<typename  DataType>::DoubleLinkList()
 {
	head = new Node<DataType>; //動態分配一個空結點,即頭節點
	assert(head);
	head->prior = head;//頭節點的首指標指向其本身
	head->next = head;//頭節點的尾指標指向其本身
	length = 0;
 }


//初始化————初始化一個含有資料的表
//實參用陣列傳入要寫入的資料
//由於C++中沒有自帶直接求陣列長度的函式 
//所以需要手動把陣列的長度n寫進去
template <typename  DataType>
DoubleLinkList<typename  DataType>::DoubleLinkList(const DataType data_[],int n)
{
	head = new Node<DataType>; //動態分配一個空結點,即頭節點
	Node<DataType> *p = head;//建立一個指向結點的指標p後面將用這個指標遍歷,實現不斷的加資料結點
	for (int i = 0; i < n; i++) {
		//這兩行程式碼其實包含了四個操作
		//step1 :new運算子建立下一個結點 且通過建構函式將資料域賦值為data_[i] 
		//step2:通過建構函式將該結點的首指標指向p(即上一個節點) 尾指標指向NULL
		//step3:通過賦值運算子將上一個結點的尾指標指向下一個結點
		//step4 將指標p移動至下一個結點 以便下一步的迭代
		p->next = new Node<DataType>(data_[i], p,NULL);
		p = p->next;
	}
	length = n;
	p->next = head;
	head->prior = p;
}


//增————向某個位置pos插入一個數據data_
template <typename  DataType>
void DoubleLinkList<typename  DataType>::Insert(const  DataType data_, int pos)
{
	//step0:準備工作 建立要遍歷的指標p和新建要插入的資料結點add
	Node<DataType> *p = head;//建立一個指向結點的指標p後面將用這個指標遍歷
	Node<DataType> *add = new Node<DataType>(data_, NULL, NULL);//建立一個新結點,包含要插入的資料data_
	//step1:判斷插入位置是否正確
	if (pos<1 || pos>length+1)
	{
		std::cout << "插入資料失敗" << endl;
	}
	
	else
	{
		//step2,用指標p進行遍歷,直到達到指定位置
		int i;
		for(i = 0; i < pos; i++)
		{
			p = p->next;
		}
		//step3 ,開始插入
		add->prior = p->prior;
		p->prior->next = add;
		p->prior = add;
		add->next = p;
		length += 1;
		std::cout << "插入資料成功" << endl;
	}

}

//刪————刪除某個位置pos的結點
template <typename  DataType>
void DoubleLinkList<typename  DataType>::Delete(int pos)
{
	//step0:準備工作 建立要遍歷的指標p和新建要插入的資料結點add
	Node<DataType> *p = head;//建立一個指向結點的指標p後面將用這個指標遍歷
	//step1:判斷刪除位置是否正確
	if (pos<1 || pos>length)
	{
		std::cout << "刪除資料失敗" << endl;
	}
	else
	{
		int i;
		for(i = 0; i < pos; i++)
		{
			p = p->next;
		}
	//step2:開始刪除
		p->prior->next = p->next;
		p->next->prior = p->prior;
		length = length - 1;
		std::cout << "刪除資料成功" << endl;
		delete p;//千萬注意此處要delete指標p!
	}
}


//改————改動某個位置pos處的結點
template <typename  DataType>
void DoubleLinkList<typename  DataType>::Change(const DataType data_,int pos)
{
	//step0:準備工作 建立要遍歷的指標p和新建要改動的資料結點add
	Node<DataType> *p = head;//建立一個指向結點的指標p後面將用這個指標遍歷
	
	//step1:判斷插入位置是否正確
	if (pos<1 || pos>length + 1)
	{
		std::cout << "改動資料失敗" << endl;
	}

	else
	{
		int i;
		//step2,用指標p進行遍歷,直到達到指定位置
		for(i = 0; i < pos; i++)
		{
			p = p->next;
		}
		//step3 ,開始改動
		Node<DataType> *change = new Node<DataType>(data_, p->prior, p->next);//建立一個新結點,包含要改動的資料data_
		p->prior->next = change;
		p->next->prior = change;
		std::cout << "改動資料成功" << endl;
		delete p;//千萬注意此處要delete指標p!
	}

}
//查————根據下標查資料
template <typename  DataType>
DataType DoubleLinkList<typename  DataType>::Search1(int pos)
{
	//step0:準備工作 建立要遍歷的指標p
	Node<DataType> *p = head;//建立一個指向結點的指標p後面將用這個指標遍歷
	//step1:判斷查詢位置是否正確
	if (pos<1 || pos>length)
	{
		std::cout << "查詢資料失敗" << endl;
		return 0;
	}
	else
	{
		int i;
		for(i = 0; i < pos; i++)
		{
			p = p->next;
		}
		//step2:返回查詢資料
		return p->data;
		std::cout << "查詢資料成功" << endl;

	}
}


//查————根據資料查下標
template <typename  DataType>
int DoubleLinkList<typename  DataType>::Search2(const DataType &e)
{
	//step0:準備工作 建立要遍歷的指標p
	Node<DataType> *p = head;//建立一個指向結點的指標p後面將用這個指標遍歷
	//step1:開始查詢
	int i = 0;
	while((i<length)&&(p->data != e))
	{
		i++;
		p = p->next;
	}
	if (p == head)
	{
		return 0;
		std::cout << "找不到該資料" << endl;
	}
	else
	{
		return i;
		std::cout << "成功找到該資料" << endl;
	}
	
}

//清空
template <typename  DataType>
void DoubleLinkList<typename  DataType>::Clear()
{
	int i = 0;
	while (i < length)
	{
		i++;
		Delete(1);
	}
	length = 0;
}
//析構
template <typename  DataType>
DoubleLinkList<typename  DataType>::~DoubleLinkList()
{
	Clear();
	delete head;
	cout << "解構函式已執行" << endl;
}

//求線性表長度
template <typename  DataType>
int DoubleLinkList<typename  DataType>::GetLength()
{
	return length;
}
//遍歷輸出
template <typename  DataType>
void DoubleLinkList<typename  DataType>::PrintAll()
{
	int i;
	Node<DataType> *p = head;//建立一個指向結點的指標p後面將用這個指標遍歷
	cout << "該連結串列的所有資料如下" << endl;
	for (i = 0; i < length; i++)
	{
		p = p->next;
		cout << p->data << " ";
	}
}

main

#include "DoubleLinkList.h"
#include<iostream>
int main()
{
	int data[4] = { 2, 3, 4, 12};
	DoubleLinkList<int> list(data, 4);//初始化
	list.PrintAll();
	cout << "線性表的長度為" << list.GetLength() << endl;//獲取連結串列的長度
	list.Insert(62,4);//增
	list.PrintAll();
	list.Delete(3);//刪
	list.PrintAll();
	list.Change(35, 1);//改
	list.PrintAll();
	int data1 = list.Search1(3);
	int pos1 = list.Search2(35);
	cout << "連結串列中位置3處的資料為  " << data1 << "資料35在連結串列中的位置為  " << pos1;//查
	while (1);

	
}

總結出現過的小問題:

1.定義連結串列模板類的時候
除了建構函式,其他成員函式中不要初始化頭節點head = new Node<DataType>; 
因為這樣會導致該函式後面的指標p指標指向新的head,即變成空指標,無法指向連結串列頭節點。
2.要注意輸入過程中輸入法中英文的切換 ,經常出現中文的括號,特別不易察覺。
3.在類模板的成員函式定義時 ,
如template <typename  DataType>
DoubleLinkList<typename  DataType>::DoubleLinkList() 
 最好使用typename關鍵字 用class可能會報錯