資料結構學習筆記——C++實現雙向迴圈連結串列模板類(超詳解)
阿新 • • 發佈:2018-12-15
定義了兩個標頭檔案分別放置結點類模板(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可能會報錯