Single Linked List單連結清單
文章目錄
- Single Linked List單連結清單
-
- 單連結清單結點類定義
- 單連結清單類定義
- makeEmpty函數
- 關于inputFront和inpurRear
- 關于void input(T endTag, InsMod im = INR);
- List類構造函數
- inverse函數
- insert函數
- Locate函數
- remove函數
- search函數
- 整一個頭檔案
檔案:
主函數:
#include <fstream> //調用檔案輸入輸出流類
#include "LinkedList.h" //調用單連結清單頭檔案
using namespace std; //聲明命名空間
int main(){
List<int> list; //執行個體化類List
ifstream fin("list.txt"); //這裡将輸入檔案設為ifstream類型
assert(fin); //判斷檔案讀取過程是否成功編譯
fin >> list; //調用輸入流operator>>,将檔案中的資料一一輸傳入連結表list
//這句話的效果相當于operator>>(fin,list);
//因為ifstream公有繼承了iostream
cout << "The initial list in the file is:\n" << list << endl;
list.Inverse(); //逆轉這個單連結清單
cout << "Inverse the list, then the list is:\n" << list << endl;
cout << "========================================\n";
int i, elem; //定義兩個整數變量
cout << "Test the Insert, Remove and Search function:\n";
cout << "Each test will terminate by an invaid input.";
cout << "\n----------------------------------------\n";
cout << "1. Test the Insert(int i, T &elem):\n";
while (1){
cout << "Input the index i and data elem to insert: ";
cin >> i >> elem;
if (!cin){ //結束條件1:輸入異常。
//這是異常處理的一種,當碰到cin了錯誤的類型時
cin.clear(); //clear将cin的資料元素failbit修改到原來的狀态
cin.ignore(100,'\n'); //清空緩存區
break;
}
if (i < 0) break; //結束條件2:輸入負數
if (list.Insert(i, elem)) cout << "Insert successful!\n";
else cout << "Insert failed!\n";
}
cout << "\nAfter inserted\n" << list << endl;
cout << "----------------------------------------\n";
cout << "2. Test the Remove(int i, T &elem):\n";
while (1){
cout << "Input the index i in which you want to remove: ";
cin >> i;
if (!cin){
cin.clear();
cin.ignore(100,'\n');
break;
}
if (i < 0) break;
if (list.Remove(i, elem)) cout << "The element " << elem << " has been removed!\n";
else cout << "Remove failed!\n";
}
cout << "\nAfter removed\n" << list << endl;
cout << "----------------------------------------\n";
cout << "3. Test the Search(T &elem):\n";
while (1){
cout << "Input the element you want to search: ";
cin >> elem;
if (!cin){
cin.clear();
cin.ignore(100,'\n');
break;
}
if (elem < 0) break;
LinkNode<int> *p = list.Search(elem);
if (p) cout << "The element " << elem << " is in the list.\n";
else cout << "The element is not exist!\n";
}
cout << "\n----------------------------------------\n";
cout << "End test!" << endl;
return 0;
}
單連結清單結點類定義
template <typename T>struct LinkNode{
T data; //資料域
LinkNode<T> *link; //指針域
LinkNode(LinkNode<T> *ptr = NULL){ //構造函數,預設指針域為空
link = ptr;
}
LinkNode(const T &item, LinkNode<T> *ptr = NULL){
data = item; //傳入一個類型為T的資料,構造函數中定義為資料元素
link = ptr; //指針域,若未指派初始化為NULL
}
};
單連結清單類定義
template <typename T>class List{
public:
List(){ //無參構造函數,其中也調用了LinkNode的無參構造函數
first = new LinkNode<T>; //first指向新的記憶體空間LinkNode<T>
}
List(const T &x){ //有參構造函數,其中也調用了LinkNode的有參構造函數
first = new LinkNode<T>(x);
//first指向新的記憶體空間LinkNode<T>,資料成員為x
}
List(List<T> &L); //拷貝構造函數
~List(){ //析構函數
makeEmpty(); //釋放首節點之後的所有結點
delete first; //釋放首節點
}
void makeEmpty(); //聲明清空函數
int Length()const;//聲明計算長度函數
LinkNode<T> *getHead()const{ //傳回指向結點指針的函數(通路私有成員first)
return first;
}
LinkNode<T> *Search(const T &x);//聲明查找函數,傳回指向結點的指針
LinkNode<T> *Locate(int i); //聲明定位函數,傳回指向結點的指針
bool getData(int i,T&x)const;//擷取資料函數,傳入結點号i,T類型資料x
void setData(int i, T &x); //設定資料函數,傳入i和x
bool Insert(int i, T &x); //插入函數,傳入i和x
bool Remove(int i, T &x); //删除函數,傳入i和x
bool IsEmpty()const{ //判斷單連結清單是否為空
return (first->link == NULL)?true:false;
//首節點往後第一個結點不為空則傳回true,否則傳回false
}
bool IsFull()const{ //判斷連結清單是否滿函數
return false;
}
void Sort(); //排序函數
void Inverse();//反轉函數
void input(T endTag, InsMod im = INR); //輸入函數
void output(); //輸出函數
List<T> &operator = (List<T> &L); //傳回List的引用,這是指派運算符的常用套路
friend ostream& operator << (ostream &out, List<T> &L){
//函數傳回的是ostream對象的引用out
LinkNode<T> *current = L.first->link; //current指向頭結點後面第一個結點
while (current){ //周遊連結清單元素
out << current->data <<'\t'; //将目前指針的資料元素傳給ostream對象out
current = current->link; //current指向下一個結點
}
out<<endl; //最後傳入換行符
return out; //運算符傳回out
}
//調用時前面加cout<<list,相當于operator<<(cout,list);主函數中這樣表達結果是正确的。
/******************輸入運算符和輸出運算符目的不同,**************************
******************輸出運算符隻需要周遊清單,*******************************
******************但輸入運算符需要确定輸出位置、容量改變等問題。*************/
friend istream& operator >> (istream &in, List<T> &L){ //現在可以這麼說了:
//類定義内定義友元函數operator>>,傳回類型為istream的引用,
//傳入istream對象in的引用(可以修改in),以及清單類型資料L的引用(可以修改L)。
LinkNode<T> *newNode,*last; //定義兩個LinkNode型的指針newNode和last
T val; //定義一個val型的資料成員T
L.makeEmpty(); //将L設定為一個隻剩首節點的空連結清單
last = L.first; //指針last此時設定為指向首節點
while (!in.eof()){
//eof是istream流類庫中的函數,判斷緩存區是否仍有資料要讀入
in >> val; //輸入val
newNode = new LinkNode<T>(val); //将val放入newNode指向的新結點
assert(newNode); //在這裡檢查編譯過程,若錯誤則友善定位
last->link = newNode; //将newNode放入單連結清單的尾部
last = newNode; //last指向newNode
}
last->link = NULL;//最後将尾節點的指針域設為NULL
return in; //傳回輸入流類型in的引用,友善連續輸入
}
protected:
LinkNode<T> *first; //指針first指向連結清單List的首節點(是以類型為指向結點的指針)
//然後
void inputFront(T endTag);
void inputRear(T endTag);
};
makeEmpty函數
//定義makeEmpty函數
template <typename T>void List<T>::makeEmpty(){
LinkNode<T> *q; //定義一個LinkNode類型的指針p
while (first->link) { //隻要首節點之後不是NULL
q = first->link; //q指向首節點之後的第一個結點
first->link = q->link; //first指向第一個結點後面的結點(如果後面沒有則指向NULL
delete q; //釋放p的空間
}
}
/*注意這裡沒有first = first -> link,也就是first指針并沒有後移*/
/*調用時,如List<int> list; list.makeEmpty();*/
這樣删下來此連結清單就變成了一個隻有首節點的空連結清單。
關于inputFront和inpurRear
1、inputFront——在連結清單頭指針後插入新結點
template <typename T>void List<T>::inputFront(T endTag){ //傳入資料設為結束标記
LinkNode<T> *newNode; //定義一個指向結點的指針newNode
T val; //一個類型為T的資料元素
cin >> val;
while ( val != endTag){
newNode = new LinkNode<T>(val); //調用關鍵字new配置設定記憶體,調用LinkNode的構造函數
if (!newNode){ //如果newNode配置設定的空間不為0,配置設定成功
cerr << "Memory allocating error!" << endl;
exit(1);
}
newNode->link = first->link; //兩步插入下一個結點
first->link = newNode;
cin >> val;
}
}
2、inputRear——在連結清單尾部插入新結點
template <typename T>void List<T>::inputRear(T endTag){
LinkNode<T> *newNode, *last=first; //定義兩個LinkNode型指針,last指向first
T val;
while(last->link!=NULL) last=last->link; //使last指向最後一個結點
cin >> val; //輸入val
while (val != endTag){
newNode = new LinkNode<T>(val);
if (!newNode){
cerr << "Memory allocating error!" << endl;
exit(1);
}
last->link = newNode;
last = newNode;
cin >> val;
}
last->link = NULL;
}
關于void input(T endTag, InsMod im = INR);
在開頭定義了枚舉變量InsMod(難怪我搜不到這個變量)
template <typename T>void List<T>::input(T endTag, InsMod im){
//InsMod是一個狀态變量,此函數傳入需要放傳入連結表的資料endTag和狀态變量
if (im == INF) inputFront(endTag); //如果狀态為INF,則在鍊首放入endTag
else inputRear(endTag); //否則在連結清單尾部放入endTag
}
List類構造函數
LinkNode的構造函數(類内定義)
LinkNode(LinkNode<T> *ptr = NULL){
link = ptr;
}
LinkNode(const T &item, LinkNode<T> *ptr = NULL){
data = item;
link = ptr;
}
List的構造函數(類内定義)
List(){
first = new LinkNode<T>;
}
List(const T &x){ //頭結點中可放特殊資訊
first = new LinkNode<T>(x);
}
List(List<T> &L); //拷貝構造函數
~List(){ //析構函數
makeEmpty();
delete first;
}
List拷貝構造函數(類外定義)
template <typename T>List<T>::List(List<T> &L){ //拷貝構造函數
T value;
LinkNode<T> *srcptr = L.first(); //srcptr指向L的第一個結點
LinkNode<T> *destptr = first = new LinkNode<T>; //destptr指向本單連結清單的第一個結點并配置設定空間
while (srcptr->link){ //周遊連結清單L
value = srcptr->link->data; //第一個結點是空的,如果不是空的,可以省掉->link
destptr->link = new LinkNode<T>(value); //同樣,本連結清單第一個結點也是空的,第二個結點指派為value
destptr = destptr->link; //destptr指針後移
srcptr = srcptr->link; //srcptr指針後移
}
destptr->link = NULL; //設定最後一個結點指針指向NULL
}
提問1:為什麼連結清單的第一個結點是空的?
這是一種程式設計習慣。頭結點其實也可以放資料,隻是在連結清單節點的插入,删除操作中極為不便,尤其是雙向連結清單,邏輯通用性不強,處理不好很容易出錯。
提問2:為什麼連結清單類的元素first是指針而構造函數中卻有
first()
?
不加括号也可以正常編譯正常調用,也許是C++中允許的一種寫法。
inverse函數
template <typename T>void List<T>::Inverse(){ //沒有傳回值
LinkNode<T> *h, *p, *pr; //定義指針h、p、pr
h = NULL; //先讓h指向NULL
p = first->link; //p指向首結點後面的結點
while (p){ //通過p來周遊連結清單
pr = h;
h = p;
p = h->link;
h->link = pr;
}
//循環過後h指向原結點最後一個結點
first->link = h; //使首節點指向h,完成連結清單的逆轉
}
insert函數
template <typename T> bool List<T>::Insert(int i, T &x){
LinkNode<T> *current = Locate(i); //定義一個current指針。Locate函數傳回第i個結點的指針LinkNode<T> *Locate(int i);
if (!current) return false; //當第i個為NULL時,傳回false
LinkNode<T> *newNode = new LinkNode<T>(x); //為指針newNode配置設定一個新結點
if (!newNode){ //如果沒有配置設定成功,調用cerr
cerr << "Memory allocating error!" << endl;
//在輸出錯誤資訊時一般用cerr,因為它不占用緩存區
exit(1);
}
newNode->link = current->link;
current->link = newNode;
return true;
}
提問:為什麼第一個錯誤是return false,第二個錯誤是exit(1)?
exit是在調用處強行退出程式,運作一次程式就結束。這個狀态辨別了應用程式的一些運作資訊,這個資訊和機器和作業系統有關。return是函數的退出(傳回);exit是程序的退出。exit(0)是正常退出,就是代碼一切正常的時候的退出。
Locate函數
template <typename T>LinkNode<T> *List<T>::Locate(int i){
//函數傳回LinkNode型指針,輸入參數為整數i
if (i < 0) return NULL; //如果輸入号碼小于0,傳回NULL
LinkNode<T> *current = first;
//設定一個LinkeNode型指針current,初始狀态指向首結點
int k = 0; //設定k為計數器,走到第i個結點時停止
while (current && k < i){ //部分周遊連結清單
current = current->link; //current後移
k++;
}
return current;
}
remove函數
template <typename T>bool List<T>::Remove(int i, T &x){
LinkNode<T> *current = Locate(i-1); //将current指針定位到第i-1個結點
if (!current || !current->link) return false;
//如果第i-1個或者第i個指針為NULL,則傳回false
LinkNode<T> *del = current->link; //指針del指向第i個結點
current->link = del->link; //使第i-1個結點指針域指向第i+1個結點
x = del->data; //x等于第i個結點的資料元素
delete del; //釋放第i個結點的記憶體
return true;
}
search函數
template <typename T>LinkNode<T> *List<T>::Search(const T &x){
//Search函數傳回指向結點的指針
LinkNode<T> *current = first->link; //定義current指針指向第一個結點
while (current && current->data != x){ //周遊連結清單
current = current->link;
}
return current; //找到時傳回current指針
}
整一個頭檔案
#ifndef LINKEDLIST_H
#define LINKEDLIST_H
#include <iostream>
#include <cassert>
using namespace std;
#ifndef INSMOD_INF_INR
#define INSMOD_INF_INR
enum InsMod {INF, INR};//¶¨ÒåÏòÇ°»¹ÊÇÏòºóÉú³É
#endif
template <typename T>struct LinkNode{
T data;
LinkNode<T> *link;
LinkNode(LinkNode<T> *ptr = NULL){
link = ptr;
}
LinkNode(const T &item, LinkNode<T> *ptr = NULL){
data = item;
link = ptr;
}
};
template <typename T>class List{
public:
List(){
first = new LinkNode<T>;
}
List(const T &x){//Í·½áµãÖпɷÅÌØÊâÐÅÏ¢
first = new LinkNode<T>(x);
}
List(List<T> &L);
~List(){//¸Ä
makeEmpty();
delete first;
}
void makeEmpty();
int Length()const;
LinkNode<T> *getHead()const{
return first;
}
LinkNode<T> *Search(const T &x);
LinkNode<T> *Locate(int i);
bool getData(int i,T&x)const;
void setData(int i, T &x);
bool Insert(int i, T &x);
bool Remove(int i, T &x);
bool IsEmpty()const{
return (first->link == NULL)?true:false;
}
bool IsFull()const{
return false;
}
void Sort();
void Inverse();
void input(T endTag, InsMod im = INR);
void output();
List<T> &operator = (List<T> &L);
friend ostream& operator << (ostream &out, List<T> &L){
LinkNode<T> *current = L.first->link;
while (current){
out << current->data <<'\t';
current = current->link;
}
out<<endl;
return out;
}
friend istream& operator >> (istream &in, List<T> &L){//ÒѸÄ
LinkNode<T> *newNode,*last;
T val;
L.makeEmpty();
last = L.first;
while (!in.eof()){
in >> val;
newNode = new LinkNode<T>(val);
assert(newNode);
last->link = newNode;
last = newNode;
}
last->link = NULL;
return in;
}
protected:
LinkNode<T> *first;
void inputFront(T endTag);
void inputRear(T endTag);
};
template <typename T>List<T>::List(List<T> &L){
T value;
LinkNode<T> *srcptr = L.first;
LinkNode<T> *destptr = first = new LinkNode<T>;
while (srcptr->link){
value = srcptr->link->data;
destptr->link = new LinkNode<T>(value);
destptr = destptr->link;
srcptr = srcptr->link;
}
destptr->link = NULL;
}
template <typename T>void List<T>::makeEmpty(){
LinkNode<T> *q;
while (first->link) {
q = first->link;
first->link = q->link;
delete q;
}
}
template <typename T>int List<T>::Length()const{
LinkNode<T> *p = first->link;
int count = 0;
while (p){
p = p->link;
count++;
}
return count;
}
template <typename T>LinkNode<T> *List<T>::Search(const T &x){
LinkNode<T> *current = first->link;
while (current && current->data != x){
current = current->link;
}
return current;
}
template <typename T>LinkNode<T> *List<T>::Locate(int i){//¸Ä
if (i < 0) return NULL;
LinkNode<T> *current = first;
int k = 0;
while (current && k < i){
current = current->link;
k++;
}//δÕÒµ½·µ»ØNULL
return current;
}
template <typename T>bool List<T>::getData(int i,T&x)const{
if (i <= 0) return NULL;
LinkNode<T> *current = Locate(i);
if (!current) return false;
else{
x=current->data;
return true;
}
}
template <typename T>void List<T>::setData(int i, T &x){
if (i <= 0) return;
LinkNode<T> *current = Locate(i);
if (!current) return;
else current->data = x;
}
template <typename T> bool List<T>::Insert(int i, T &x){
LinkNode<T> *current = Locate(i);
if (!current) return false;
LinkNode<T> *newNode = new LinkNode<T>(x);
if (!newNode){
cerr << "Memory allocating error!" << endl;
exit(1);
}
newNode->link = current->link;
current->link = newNode;
return true;
}
template <typename T>bool List<T>::Remove(int i, T &x){
LinkNode<T> *current = Locate(i-1);
if (!current || !current->link) return false;
LinkNode<T> *del = current->link;
current->link = del->link;
x = del->data;
delete del;
return true;
}
template <typename T> void List<T>::output(){
LinkNode<T> *current = first->link;
while (current){
cout << current->data << endl;
current = current->link;
}
}
template <typename T> List<T> &List<T>::operator = (List<T> &L){//¸Ä
T value;
LinkNode<T> *destptr=first,*srcptr = L.first;
makeEmpty();//ÏÈÇå¿Õ£¬ºó¸´ÖÆ
while (srcptr->link)
{
value = srcptr->link->data;
destptr->link = new LinkNode<T>(value);
destptr = destptr->link;
srcptr = srcptr->link;
}
destptr->link = NULL;
return *this;
}
template <typename T>void List<T>::input(T endTag, InsMod im){
if (im == INF) inputFront(endTag);
else inputRear(endTag);
}
template <typename T>void List<T>::inputFront(T endTag){//¸Ä£¬Ìí¼Ó
LinkNode<T> *newNode;
T val;
cin >> val;
while ( val != endTag){
newNode = new LinkNode<T>(val);
if (!newNode){
cerr << "Memory allocating error!" << endl;
exit(1);
}
newNode->link = first->link;
first->link = newNode;
cin >> val;
}
}
template <typename T>void List<T>::inputRear(T endTag){//¸Ä
LinkNode<T> *newNode, *last=first;
T val;
while(last->link!=NULL) last=last->link;
cin >> val;
while (val != endTag){
newNode = new LinkNode<T>(val);
if (!newNode){
cerr << "Memory allocating error!" << endl;
exit(1);
}
last->link = newNode;
last = newNode;
cin >> val;
}
last->link = NULL;
}
template <typename T>void List<T>::Sort(){
}
template <typename T>void List<T>::Inverse(){
LinkNode<T> *h, *p, *pr;
h = NULL;
p = first->link;
while (p){
pr = h;
h = p;
p = h->link;
h->link = pr;
}
first->link = h;
}
#endif