本例子是《數據結構與算法c++描述》中的線性表中抽象數據類型 (abstract data type, ADT):
目錄
基本概念:
先看基本類型吧:
本例子採用了類模板,這裏提醒一下大家,不要把定義與實現一個寫在.h與.cpp, 因爲模板的性質,當你在別的文件使用的時候,就會報無法解析外部符號的錯誤。
本例子代碼是書上的代碼,後邊的新加的功能是課後習題需要開發的,同時也對這個程序完善了一部分。
首先我們先看類的定義:
listearLine.h 文件中
/*
數組結構中的線性表數組樣式
對應書中代碼:數據結構算法與應用c++描述
程序編寫:比卡丘不皮
編寫時間:2020年6月24日 13:51:23
*/
#pragma once
#include <iostream>
#include "all_error.h"
#include <time.h>
using namespace std;
template<class T>
class LinearList
{
public:
LinearList();
LinearList(int MaxListSize ); //構造函數默認爲
~LinearList(); //析構函數
LinearList(const LinearList<T> & L); //複製構造函數
bool IsEmpty()const; //判斷是否爲空
int Length() const { return length; } //返回表的長度,即表中元素個數
bool Find(int k, T& x)const; //返回第k個元素至x中 若不存在第k個元素,則返回false
int Search(const T& x) const; // 返回x所在位置 如果x 不在表中,則返回0
LinearList<T>& Delete(int k, T& x); // 刪除第k個元素並將它返回至x中
LinearList<T>& Insert(int k, const T& x); // 在第k個元素之後插入x
//改變數組最大長度函數,l爲數組長度,m爲數組最大長度
LinearList<T>& changeSize(T * elementNew, int nlength, int max);
LinearList<T>& Reverse(); //元素次序的反轉 element[k]=element[length-k]
LinearList<T>& Half(); //數組減半
void Reset() { current = 1; } //設置數據位
bool Current(T &x); //返回當前的值
bool End()const; //是否爲最後邊
bool Front() const; //是否爲最前邊
void Next(); //到下個值
void Previous(); //到前一個值
void clear();
//線性表交叉組合
LinearList<T>& Alternate(const LinearList<T> & list1, const LinearList<T> & list2);
//有規律(元素從小到大排列)線性表的組合
LinearList<T>& Merge(const LinearList<T> & list1, const LinearList<T> & list2);
//線性表分割函數
void Split(LinearList<T> & list1, LinearList<T> & list2);
void Output(ostream& out) const; // 輸出函數
LinearList<T> & operator =(const LinearList<T> & x); //重載賦值運算
private:
int length; //數組的長度
int MaxSize; //數組最大值
T * element; //一維動態數組
int current; //元素當前的位置
};
這裏先講下
#include "all_error.h"
這裏我寫了拋出的異常類:
/*
線性表中的數組結構
對應書中代碼:數據結構算法與應用c++描述
程序編寫:比卡丘不皮
編寫時間:2020年6月24日 13:51:23
*/
#pragma once
#include <iostream>
using namespace std;
class Noman
{
public:
Noman()
{
cout << "內存不足"<< endl;
}
~Noman()
{
}
};
//使new引發NoMemory異常而不是xalloc異常
void my_new_handler()
{
throw Noman();
}
new_handler Old_Handler_ = set_new_handler(my_new_handler);
class OutOfBounds
{
public:
OutOfBounds()
{
cout << "超出範圍" << endl;
}
~OutOfBounds()
{
}
};
爲了超出數組觸發不同的異常警告。
還是回到類成員函數中(listearLine.h 裏面):
構造函數與析構函數:
template<class T>
inline LinearList<T>::LinearList()
{
MaxSize = 1;
element = new T[MaxSize];
length = 0;
}
//構造函數默認爲10
template<class T>
LinearList<T>::LinearList(int MaxListSize)
{
MaxSize = MaxListSize;
element = new T[MaxSize];
length = 0;
}
//析構函數
template<class T>
LinearList<T>::~LinearList()
{
delete[] element;
}
是否爲空:
//判斷是否爲空
template<class T>
bool LinearList<T>::IsEmpty() const
{
return 0 == length;
}
Find函數:
//返回第k個元素至x中 若不存在第k個元素,則返回false
template<class T>
bool LinearList<T>::Find(int k, T & x) const
{
if (k<1 || k>length)
{
return false;
}
else
{
x = element[k-1];
}
return true;
}
查找函數:
template<class T>
int LinearList<T>::Search(const T & x) const
{
for (int i =0; i<length; i++)
{
if (x == element[i])
{
return ++i;
}
}
return 0;
}
刪除數據:
template<class T>
LinearList<T>& LinearList<T>::Delete(int k, T & x)
{
if (Find(k,x))
{
for (int i = k; i < length; i++)
{
element[i - 1] = element[i];
}
length--;
if (length == (MaxSize / 4) && length > 0)
{
MaxSize /= 2;
changeSize(element,length,MaxSize);
}
return *this;
}
else
{
throw OutOfBounds();
}
}
這裏本沒有:
if (length == (MaxSize / 4) && length > 0)
{
MaxSize /= 2;
changeSize(element,length,MaxSize);
}
這段代碼,這裏是後面習題中添加的功能,後面又詳細解答。
插入函數:
//插入數據
//在第k個元素之後插入x;函數返回修改後的線性表
template<class T>
LinearList<T>& LinearList<T>::Insert(int k, const T & x)
{
//在第k個元素之後插入x;函數返回修改後的線性表
//如果不存在第k個元素,則引發異常OutOfBound
//如果表已經滿,則引發異常NoMem
if (k<0 || k > length)
{
throw OutOfBounds();
}
if (length == MaxSize)
{
throw Noman();
}
//向後移動一位 書中這裏是減一 書寫錯誤
for (int i = length - 1; i >= k; i--)
{
element[i + 1] = element[i];
}
element[k] = x;
length++;
if (length == MaxSize)
{
MaxSize *= 2;
changeSize(element, length, MaxSize);
}
return *this;
}
輸出函數與重構 << 符號輸出:
template<class T>
void LinearList<T>::Output(ostream & out) const
{
for (int i = 0; i < length; i++)
{
out << element[i] << " ";
}
}
template<class T>
ostream & operator<<(ostream & out, const LinearList<T> & x)
{
x.Output(out);
return out;
}
已上的函數功能都是書中的代碼部分,下面的部分爲書上 課後習題裏面需要的。
題目部分函數:
changeSize函數:
//改變大小,節省空間
template<class T>
inline LinearList<T>& LinearList<T>::changeSize(T * elementNew, int nlength, int max)
{
element = new T[max];
for (int i = 0; i<nlength; i++)
{
element[i] = elementNew[i];
}
delete[] elementNew;
return *this;
}
這個函數可以節省空間,可以看插入函數與刪除函數,在執行刪除操作期間,如果線性表的尺寸降至當前 MaxSize 的四分之一,則分配一個更小的、尺寸爲 MaxSize / 2的數組,並將老數組中的數據複製到新數組中,最後將老數組刪除。
題目2:
Reverse函數:
//數據反轉函數
template<class T>
LinearList<T>& LinearList<T>::Reverse()
{
for (int i = 0; i< (length/2); i++)
{
T temp = element[i];
element[i] = element[length-1-i];
element[length - 1 - i] = temp;
}
return *this;
}
//就地反轉函數利用對象
template<class T>
void ReverseEle(LinearList<T> & data)
{
data.Reverse();
}
這裏是後面對比使用的函數。
template<class T>
LinearList<T>& LinearList<T>::Half()
{
length = (length + 1) / 2;
//這裏兩種方案來寫程序,也是劍指offer中的知識點
//方法1
T *elementNew = new T[length+1];
for (int i = 0; i<=length; i++)
{
elementNew[i] = element[2 * i];
}
delete[] element;
element = new T[length+1];
for (int i = 0; i <= length; i++)
{
element[i] = elementNew[i];
}
delete[] elementNew;
return *this;
//方法二是直接利用構造函數,這樣就不用自己寫delete了,這裏就不記錄了
}
複製構造函數:
//複製構造函數
template<class T>
LinearList<T>::LinearList(const LinearList<T>& L)
{
if (this == &L)
{
return;
}
else
{
this->length = L.length;
this->MaxSize = L.MaxSize;
delete[] element;
element = new T[length+1];
memcpy(element, L.element, length * sizeof(T));
}
}
當寫完後一定要寫重載 = 符號不然很容易出問題:
template<class T>
inline LinearList<T>& LinearList<T>::operator=(const LinearList<T>& x)
{
if (this == &x)
{
return *this;
}
this->MaxSize = x.MaxSize;
this->length = x.length;
memcpy(element, x.element,length * sizeof(T));
}
操作函數:
template<class T>
bool LinearList<T>::Current(T & x)
{
if (current < 0 || current > MaxSize)
{
return false;
}
else
{
x = element[current - 1];
return true;
}
}
template<class T>
inline bool LinearList<T>::End() const
{
return current == length;
}
template<class T>
inline bool LinearList<T>::Front() const
{
return current == 1;
}
template<class T>
inline void LinearList<T>::Next()
{
if (current < length)
{
current++;
}
else
{
throw OutOfBounds();
}
}
template<class T>
inline void LinearList<T>::Previous()
{
if (current > 1)
{
current--;
}
else
{
throw OutOfBounds();
}
}
template<class T>
void LinearList<T>::clear()
{
this->MaxSize = 1;
delete [] element;
element = new T[MaxSize];
this->length = 0;
}
Reset 在定義的時候已經寫完了。這裏我自己添加了個clear函數。
線性交叉函數:
template<class T>
LinearList<T>& LinearList<T>::Alternate(const LinearList<T>& list1, const LinearList<T>& list2)
{
//線性交叉
if (length>0)
{
delete[] element; //清除原有數據
}
length = list1.length + list2.length;
element = new T[length];
// MaxSize = length;
LinearList<T> A = list1, B = list2; //複製構造函數
A.Reset();
B.Reset();
int value = 0; //記錄當前位置的值
int minLenght = A.length <= B.length ? A.length: B.length; //獲取當前比較小的值
for (int i = 0; i<minLenght;i++)
{
if (A.Current(value))
{
element[2 * i] = value;
}
if (B.Current(value))
{
element[2 * i + 1] = value;
}
if (A.End() || B.End()) //判斷當前是否爲最後一個
{
break;
}
else
{
A.Next();
B.Next();
}
}
for (int i = 2 * minLenght; i<length; i++ )
{
//判斷不是結尾
if (!A.End())
{
A.Next();
A.Current(value);
element[i] = value;
}
else
{
B.Next();
B.Current(value);
element[i] = value;
}
}
return *this;
}
Merge函數:
template<class T>
LinearList<T>& LinearList<T>::Merge(const LinearList<T>& list1, const LinearList<T>& list2)
{
if (length > 0)
{
delete[] element;
}
length = list1.length + list2.length;
MaxSize = length;
element = new T[length]; //創建新的標籤
int ca = 0, cb = 0, ct = 0;
//先拍都有的數據
while (ca < list1.length && cb < list2.length)
{
if (list1.element[ca] >= list2.element[cb])
{
element[ct++] = list2.element[cb++];
}
else
element[ct++] = list1.element[ca++];
}
//在排剩下的數據
if (ca == list1.length) //表示list1數組排完了
{
for (int i = cb;i < list2.length;i++)
{
element[ct] = list2.element[i];
ct++;
}
}
else
{
for (int i = ca; i < list1.length; i++)
{
element[ct] = list1.element[i];
ct++;
}
}
return *this;
}
Split函數:
void LinearList<T>::Split(LinearList<T>& list1, LinearList<T>& list2)
{
int aNum = 0;
int bNum = 0;
for (int i = 0; i < length; i++)
{
if ((i & 1) == 0)
{
list1.Insert(aNum++, element[i]);
}
else
{
list2.Insert(bNum++, element[i]);
}
}
}
測試程序:
//測試列表數組
void testLinearList()
{
try
{
//設置爲空的時候
cout << "初始化開始" << endl;
LinearList<int> L;
cout << "Lenght = " << L.Length() << endl;
cout << "IsEmpty = " << L.IsEmpty() << endl;
L.Insert(0, 2);
L.Insert(1, 6);
//也可以這樣寫
L.Insert(2, 5).Insert(3, 4).Insert(4, 8);
cout << "List is " << L << endl;
cout << "isEmpty " << L.IsEmpty() << endl;
//測試插入函數
L.Insert(1, 10).Insert(2,1);
cout << "List is " << L << endl;
cout << "isEmpty " << L.IsEmpty() << endl;
cout << "lengh is " << L.Length()<< endl;
int z;
L.Find(1, z); //找到第一個數的數據
cout << "first element " << z << endl;
cout << "Lenght = " << L.Length() << endl;
cout << "lengh is " << L.Length() << endl;
L.Delete(1, z);
cout << "Deleted element is " << z << endl;
cout << "List is " << L << endl;
cout << "lengh is " << L.Length() << endl;
cout << endl;
//測試查找
cout << "test Search" << endl;
cout << "List is " << L << endl;
cout << "6 in L? " << L.Search(6) << endl;
cout << "9 in L? " << L.Search(9) << endl;
cout << endl;
cout << "test Reverse" << endl;
L.Reverse();
cout << "List is " << L << endl;
cout << "isEmpty " << L.IsEmpty() << endl;
cout << "lengh is " << L.Length() << endl;
//反轉測試
for (int i = 0; i<500000; i++)
{
L.Insert(i, i);
}
cout << L.Length() << endl;
clock_t startTime, stopTime;
startTime = clock();
L.Reverse();
stopTime = clock();
cout << "類成員反轉使用的時間: " << float(stopTime - startTime) / CLK_TCK << endl;
//就地反轉
startTime = clock();
ReverseEle(L);
stopTime = clock();
cout << "就地反轉使用的時間: " << float(stopTime - startTime) / CLK_TCK << endl;
//測試half
L.Half();
cout << "length is : " << L.Length()<< endl;
//測試複製構造函數
LinearList<int> ML(L);
cout << "length L is : " << L.Length() << endl;
cout << "length ML is : " << ML.Length() << endl;
//測試current類容
ML.Reset(); //位置置0
//測試next
ML.Next(); //移動到下個位置
cout << "判斷是否是前邊的 " << ML.Front() << endl;
int value = 0;
cout << "當前是否有問題 :" << ML.Current(value) << endl;
cout << "輸出當前的值是 :" << value << endl;
//其他函數測試也是相同的
//測試交叉數據
LinearList<int> A, B;
for (int i = 0; i<4; i++)
{
A.Insert(i, i);
B.Insert(i, i);
}
B.Insert(4, 4).Insert(5, 5);
cout << "A is List : " << A <<endl;
cout << "B is List : " << B <<endl;
LinearList<int> C;
C.Alternate(A,B);
cout << "C is list :" << C << endl;
//測試合併排序,前提都是順序的
A.Insert(4, 10);
LinearList<int> D;
D.Merge(A,B);
cout << "D is list :" << D << endl;
//測試
A.clear();
B.clear();
D.Split(A,B);
cout << "D is list :" << D << endl;
cout << "A is List : " << A << endl;
cout << "B is List : " << B << endl;
}
catch (const std::exception&)
{
cerr << "An exception has occurred" << endl;
}
}
主函數:
#include <iostream>
#include "listearLine.h"
using namespace std;
int main()
{
//測試線性表數組
testLinearList();
return 0;
}
已上就是題目中的程序了,從第9題目開始,只是變成下標爲1開始數組,這裏類推一下就好不難的。寫題目部分程序畢竟是自己來寫的,難免會有程序問題,若你有發現問題,可在下面評論區交流,或者關注我博客,讓我們一起進步,加油。
需要書籍的可以來取:
本文例子的連接:
鏈接:本文例子 提取碼:ao3c
如果例子失效,可以私信,或者下方評論。