本例子是《數據結構與算法c++描述》中的線性表中單向鏈表(singly linked list)
單鏈表原理圖:
first 爲頭節點 ,由於第一個節點 e1 的指針指向第二個節點 e2, e2 的指針指向e3 ,. . .,最後一個節點鏈接域爲 N U L L (或0 ),故這種結構也被稱作鏈(chain)。
單鏈表定義:
/*
數組結構中的線性錶鏈表樣式
對應書中代碼:數據結構算法與應用c++描述
程序編寫:比卡丘不皮
編寫時間:2020年6月30日 17:55:29
*/
#pragma once
#include "all_error.h"
#include <iostream>
using namespace std;
template<class T>
class Chain; //聲明
template<class T>
class ChainIterator; //迭代器
template<class T>
class ChainNode
{
friend Chain<T>;
friend ChainIterator<T>; //因爲要訪問private
private:
T data;
ChainNode<T> * link;
};
template<class T>
class Chain
{
friend ChainIterator<T>; //應爲要訪問private
public:
Chain() { first = 0; last = first; };//初始化函數
~Chain(); //析構函數
Chain(const Chain<T> & Val); //複製構造函數
bool IsEmpty() const { return first == 0; } //判斷是不是空
int Length()const; //鏈表的長度
bool Find(int k, T &x) const; //查找第k可的數據,沒有爲false
int Search(const T & x) const; //查找數據函數
Chain<T>& Delete(int k, T & x); //刪除某個數據
Chain<T>& Insert(int k, const T & x); //插入數據
void Output(ostream & out)const; // 輸出接點數據
//擴充鏈表
void Erase(); //刪除鏈表
void Zero() { first = 0; } //數據first 爲0
Chain<T>& Append(const T & x); //在最後添加
Chain<T>& Reverse(); //元素次序的反轉 原地的反轉
//線性表交叉組合 參數一定不是自己的變量
Chain<T>& Alternate(const Chain<T>& list1, const Chain<T>& list2);
//有規律(元素從小到大排列)線性表的組合
Chain<T>& Merge(const Chain<T>& list1, const Chain<T>& list2);
//鏈表排序 排序法
void sort();
//線性表分割函數 交叉分割
void Split(Chain<T>& list1, Chain<T>& list2);
Chain<T> & operator =(const Chain<T>& x); //複製構造函數一定要同時寫這個
private:
ChainNode<T> * first; //記錄頭指針
ChainNode<T> * last; //記錄尾指針
};
//鏈表遍歷類
template<class T>
class ChainIterator
{
public:
T * Initialize(const Chain<T> & c)
{
location = c.first;
if (location)
{
return &location->data;
}
return 0;
}
T * Next()
{
if (!location)
{
return 0;
}
location = location->link;
if (location)
{
return &location->data;
}
return 0;
}
private:
ChainNode<T> *location;
};
有關 #include "all_error.h" 請看我博客中(1)的寫法:
傳送門:https://blog.csdn.net/weixin_42126427/article/details/107013624
首先
template<class T>
class Chain; //聲明
template<class T>
class ChainIterator; //迭代器
聲明,在對應類中friend class 是可以在對應鏈表中訪問private變量,所以要聲明。
析構函數:
template<class T>
Chain<T>::~Chain()
{
ChainNode<T> * next;
while (first)
{
next = first->link;
delete first;
first = next;
}
}
循環對當前節點刪除,而不是隻是把first 給delete ,這裏一定要這樣刪除,不然類存泄漏.
Length()函數:
template<class T>
int Chain<T>::Length() const
{
ChainNode<T> * current = first;
int len = 0;
while (current)
{
len++;
current = current->link;
}
return len;
}
目的:計算鏈表的長度,返回長度。
Find(int k, T &x)函數:
template<class T>
bool Chain<T>::Find(int k, T & x) const
{
if (k < 1)
{
return false;
}
ChainNode<T> * current = first;
int index = 1; //current的索引
while (index < k && current)
{
current = current->link;
index++;
}
if (current)
{
x = current->data;
return true;
}
return false;
}
查找第k可的數據,沒有爲false,有數據,把值賦值給X;
搜索Search(const T & x):
//尋找x, 如果發現x 返回x的地址
//如果不在列表中,返回0
template<class T>
int Chain<T>::Search(const T & x) const
{
ChainNode<T> *current = first;
int index = 1;
while (current && current->data != x)
{
current = current->link;
index++;
}
if (current)
{
return index;
}
return 0;
}
刪除 Delete(int k, T & x)
刪除原理圖:
比如要刪除第4個,要找到第4個,然後讓3連接到5上,然後釋放4的節點;
//把第k個元素取至x,然後從鏈表中刪除第 k個元素
//如果不存在第 k個元素,則引發異常 OutOfBounds
template<class T>
Chain<T>& Chain<T>::Delete(int k, T & x)
{
if (k < 1 || !first)
{
throw OutOfBounds();
}
// p最終將指向第 k個節點
ChainNode<T> * p = first;
// 將p移動至第k個元素,並從鏈表中刪除該元素
if (k == 1) //p已經指向第k個元素
{
first = first->link; //這樣就刪除了
}
else
{
ChainNode<T> * q = first;
for (int index = 1; index < k-1 && q; index++)
{
q = q->link;
}
if (!q || !q->link)
{
throw OutOfBounds();
}
//存在第k個元素 刪除對應元素
p = q->link;
if (p == last)
{
last = q;
}
q->link = p->link; //兩種可能性,p->link爲0, 那麼 q->link 就是0, 若有下個數據,就連接下個數據
}
x = p->data;
delete p;
return *this;
}
輸出Output(ostream & out)
template<class T>
void Chain<T>::Output(ostream & out) const
{
ChainNode<T> *current;
for (current = first; current; current = current->link)
{
out << current->data << " ";
}
}
//重載<<
template<class T>
ostream & operator <<(ostream & out, const Chain<T> & x)
{
x.Output(out);
return out;
}
插入Insert(int k, const T & x):
插入原理圖:
當插入爲0爲在first插入最前,當爲k不是0在中間插入 ,OutOfBounds 這些錯誤在上面的傳送門中的頭文件中
//在第k個元素之後插入x
//如果不存在第 k個元素,則引發異常OutOfBounds
//如果沒有足夠的空間,則傳遞 Nomem異常
template<class T>
Chain<T>& Chain<T>::Insert(int k, const T & x)
{
if (k < 0)
{
throw OutOfBounds();
}
//p最終將指向第 k個節點
ChainNode<T> * p = first;
//將p移動至第k個元素
for (int index = 1; index < k && p; index++ )
{
p = p->link;
}
//不存在第k個元素
if (k > 0 && !p)
{
throw OutOfBounds();
}
//插入
ChainNode<T> * y = new ChainNode<T>;
y->data = x;
if (k) //在p之後插入
{
y->link = p->link;
p->link = y;
}
else
{
y->link = first;
first = y;
}
if (!y->link)
{
last = y; //記錄最後一個節點
}
return *this;
}
刪除Erase():
//刪除說有節點
template<class T>
void Chain<T>::Erase()
{
ChainNode<T> *next;
while (first)
{
next = first->link;
delete first;
first = next;
}
}
添加Append(const T & x):
//在鏈表尾部添加一個節點
template<class T>
Chain<T>& Chain<T>::Append(const T & x)
{
ChainNode<T> *y;
y = new ChainNode<T>;
y->data = x; y->link = 0;
if (first) //鏈表非空
{
last->link = y;
last = y;
}
else
{
first = last = y;
}
return *this;
}
已上函數都是書上的源碼部分,對應的函數都已經實現,下面部分爲後面習題部分自己實現的函數功能,自己寫的測試可以通過,若你發現有些有問題歡迎在下面評論。
複製構造函數:
//複製構造函數
template<class T>
Chain<T>::Chain(const Chain<T>& Val)
{
this->Erase(); //先清除所有數據
ChainNode<T> * next = Val.first;
while (next)
{
this->Append(next->data);
next = next->link;
}
}
//重載 =
template<class T>
Chain<T>& Chain<T>::operator=(const Chain<T>& x)
{
this->Erase(); //先清除所有數據
ChainNode<T> * next = x.first;
while (next)
{
this->Append(next->data);
next = next->link;
}
return *this;
}
反轉 Reverse():
//次序反轉
template<class T>
Chain<T>& Chain<T>::Reverse()
{
if (first ==0 && first == last && first->link == 0)
{
return *this;
}
ChainNode<T> * current = first; //記錄位置
ChainNode<T> * pre = NULL;
last = first; //頭變尾
//好好體會一下這個
while (first)
{
current = first;
first = first->link;
current->link = pre;
pre = current;
}
first = current; //current 爲頭指針
return *this;
}
交叉線性Alternate(const Chain<T>& list1, const Chain<T>& list2)
//交叉線性
template<class T>
Chain<T>& Chain<T>::Alternate(const Chain<T>& list1, const Chain<T>& list2)
{
if (this == &list1 && this == &list2)
{
cout << "參數不能是本身變量" << endl;
return *this;
}
//清除前鏈表的數據
this->Erase();
// int length = list1.Length() + list2.Length();
ChainNode<T> * pList1 = list1.first;
ChainNode<T> * pList2 = list2.first;
while (pList1 && pList2)
{
this->Append(pList1->data);
this->Append(pList2->data);
//指向下個
pList1 = pList1->link;
pList2 = pList2->link;
}
if (!pList1) //pList1爲空的話
{
while (pList2)
{
this->Append(pList2->data);
pList2 = pList2->link;
}
}
else
{
while (pList1)
{
this->Append(pList1->data);
pList1 = pList1->link;
}
}
return *this;
}
合併有序數據Merge(const Chain<T>& list1, const Chain<T>& list2)
//合併數據 前提數據必須是有序的。
template<class T>
Chain<T>& Chain<T>::Merge(const Chain<T>& list1, const Chain<T>& list2)
{
//清除前鏈表的數據
if (this == &list1 && this == &list2)
{
cout << "參數不能是本身變量" << endl;
return *this;
}
//清除前鏈表的數據
this->Erase();
//獲取兩個鏈表的頭節點
ChainNode<T> * pList1 = list1.first;
ChainNode<T> * pList2 = list2.first;
while (pList1 && pList2)
{
if (pList1->data <= pList2->data)
{
this->Append(pList1->data);
pList1 = pList1->link;
}
else
{
this->Append(pList2->data);
pList2 = pList2->link;
}
}
//當其中一個鏈表爲空或者都爲空,開始下面處理
if (!pList1)
{
while (pList2)
{
this->Append(pList2->data);
pList2 = pList2->link;
}
}
else
{
while (pList1)
{
this->Append(pList1->data);
pList1 = pList1->link;
}
}
return *this;
}
排序sort()
//選擇排序
template<class T>
void Chain<T>::sort()
{
ChainNode<T> * p = first->link; //獲取
ChainNode<T> * min = first; //先獲取頭節點數據爲最小值
while(min)
{
p = min->link;
while (p)
{
if (min->data > p->data)
{
T temp = min->data;
min->data = p->data;
p->data = temp;
}
p = p->link;
}
min = min->link; //找到下一位
}
}
這裏是書上沒有要求的,爲了後面測試方便。
拆分Split(Chain<T>& list1, Chain<T>& list2):
template<class T>
void Chain<T>::Split(Chain<T>& list1, Chain<T>& list2)
{
//拆分的兩個變量不能爲當前拆分的值
if (this == &list1 && this == &list2)
{
cout << "參數不能是本身變量" << endl;
return ;
}
//若要拆分 list1 與 list2 首先要滯空
list1.Erase();
list2.Erase();
ChainNode<T> * current = first;
int index = 1;
while (current)
{
if ((index & 1) == 1) //當爲奇數的時候
{
list1.Append(current->data);
}
else //當爲偶數的時候
{
list2.Append(current->data);
}
//到達下個數據
current = current->link;
index++;
}
}
測試函數:
void testChain()
{
Chain<int> c;
cout << "新建鏈表: " << endl;
cout << "鏈表是否爲空: " << c.IsEmpty() << endl;
cout << "鏈表的長度: " << c.Length() << endl;
cout << "鏈表爲: " << c << endl << endl;
//測試插入函數
c.Insert(0, 1).Insert(1, 2).Insert(2, 3).Insert(3,4);
cout << "鏈表是否爲空: " << c.IsEmpty() << endl;
cout << "鏈表的長度: " << c.Length() << endl;
cout << "鏈表爲: " << c << endl << endl;
int value = 0;
//find函數查找
c.Find(2, value);
cout << "測試Find()函數---c.Find(2,x):" << endl;
cout << "x爲: " << value << endl << endl;
//查找函數
cout << "測試Search():" << endl;
cout << "元素0的位置爲: " << c.Search(0) << endl;
cout << "元素2的位置爲: " << c.Search(2) << endl << endl;
//測試刪除
cout << "開始刪除鏈表" << endl;
cout << "源鏈表: " << c << endl;
c.Delete(2, value);
cout << "刪除的數據是: " << value <<endl;
cout << "刪除後的鏈表: " << c << endl;
//append 函數測試
c.Append(10);
cout << "插入後鏈表爲: " << c << endl << endl;
//測試新遍歷器
int * va;
ChainIterator<int> d;
va = d.Initialize(c);
while (va)
{
cout << *va << " ";
va = d.Next();
}
cout << endl;
//測試構建複製函數
cout << "構建複製函數" << endl;
Chain<int> NewNum(c);
cout << "源鏈表: " << c << endl;
cout << "新鏈表: " << NewNum << endl;
//測試 =
cout << "=號賦值" << endl;
NewNum = c;
cout << "源鏈表: " << c << endl;
cout << "新鏈表: " << NewNum << endl;
cout << endl;
//測試反轉函數
cout << "源鏈表: " << c << endl;
c.Reverse();
cout << "反轉後的鏈表: " << c << endl;
cout << endl;
cout << " 測試Alternate 函數 " << endl;
Chain<int> testAl;
testAl.Alternate(NewNum,c);
cout << "NewNum 列表是: " << NewNum << endl;
cout << "c 列表是: " << c << endl;
cout << "testAl 列表是: " << testAl << endl;
cout << endl;
//排序測試 選擇排序
cout << "排序測試" << endl;
cout << "c 列表是: " << c << endl;
cout << "testAl 列表是: " << testAl << endl;
c.sort();
testAl.sort();
cout << "排序後 c 列表是: " << c << endl;
cout << "排序後 testAl 列表是: " << testAl << endl;
//排序後測試Merge 函數
cout << "排序後測試Merge 函數" << endl;
Chain<int> testMerge;
testMerge.Merge(c, testAl);
cout << "Merge(c, testAl) 函數: " << testMerge << endl;
cout << endl;
//測試Split
cout << "測試Split 函數" << endl;
Chain<int> one, two;
cout << "拆分前testMerge數據:" << testMerge << endl;
cout << "拆分前one數據: " << one << endl;
cout << "拆分前two數據: " << two << endl;
testMerge.Split(one, two);
cout << "拆分後testMerge數據:" << testMerge << endl;
cout << "拆分後one數據: " << one << endl;
cout << "拆分後two數據: " << two << endl;
}
主函數:
#include <iostream>
#include "listearLine.h"
#include "Chain.h"
using namespace std;
int main()
{
//測試線性表數組
//testLinearList();
//測試線性錶鏈表
testChain();
return 0;
}
程序運行結果:
新建鏈表:
鏈表是否爲空: 1
鏈表的長度: 0
鏈表爲:
鏈表是否爲空: 0
鏈表的長度: 4
鏈表爲: 1 2 3 4
測試Find()函數---c.Find(2,x):
x爲: 2
測試Search():
元素0的位置爲: 0
元素2的位置爲: 2
開始刪除鏈表
源鏈表: 1 2 3 4
刪除的數據是: 2
刪除後的鏈表: 1 3 4
插入後鏈表爲: 1 3 4 10
1 3 4 10
構建複製函數
源鏈表: 1 3 4 10
新鏈表: 1 3 4 10
=號賦值
源鏈表: 1 3 4 10
新鏈表: 1 3 4 10
源鏈表: 1 3 4 10
反轉後的鏈表: 10 4 3 1
測試Alternate 函數
NewNum 列表是: 1 3 4 10
c 列表是: 10 4 3 1
testAl 列表是: 1 10 3 4 4 3 10 1
排序測試
c 列表是: 10 4 3 1
testAl 列表是: 1 10 3 4 4 3 10 1
排序後 c 列表是: 1 3 4 10
排序後 testAl 列表是: 1 1 3 3 4 4 10 10
排序後測試Merge 函數
Merge(c, testAl) 函數: 1 1 1 3 3 3 4 4 4 10 10 10
測試Split 函數
拆分前testMerge數據:1 1 1 3 3 3 4 4 4 10 10 10
拆分前one數據:
拆分前two數據:
拆分後testMerge數據:1 1 1 3 3 3 4 4 4 10 10 10
拆分後one數據: 1 1 3 4 4 10
拆分後two數據: 1 3 3 4 10 10
請按任意鍵繼續. . .
本文例子程序:
鏈接:https://pan.baidu.com/s/1m8KlkXVzzNGKmKE9I9lwCA 提取碼:c2te
如果遇到什麼問題可以聯繫我,關注我的博客,一起加油學習吧。