線性表主要分爲兩種,順序表和鏈表。
順序表主要利用的是數組,大部分時候只需要重新定義數組的插入或刪除操作就可以,所以順序表的存儲結構跟數組是相同的,都是一串連在一起的內存。因此,順序表在存儲數據的同時也保存了數據之間的邏輯關係。並且可以通過訪問數組下標直接訪問對應元素。這裏主要總結鏈表。
鏈表是將內存中零散的不連續的部分連接在一起,這個起連接作用的東西就是指針。我們可以用過指針去訪問已知的可以訪問的地址,這就好比偵探遊戲,如果沒有明確的線索,再聰明的偵探也沒辦法接近真相,而對於鏈表的設計者而言,指針就是線索,失去了指針,鏈表也就沒辦法延續下去。
爲了同時存儲數據和邏輯關係,需要把這兩部分整合到一起,Node結構體應運而生。
template<typename T>
struct Node
{
T data;
Node<T>*next;
};
在這裏我們用到了模板,這麼做可以讓代碼的服用程度更高,不做過多解釋。
在Node的定義中不難看出,Node本身包含一個指向自身類型的指針,具體的可以說指針next指向下一個節點Node。
在使用鏈表前,應該先創建鏈表。
創建鏈表分爲兩種,帶頭結點與不帶頭節點的創建,而這唯一的區別就是,頭指針First指向的Node型內存中是否有具體的數據。如果有就是不帶頭結點的創建方式,此時鏈表是有一個元素的,反之就是帶頭結點的創建方式。但是它們的next均指向NULL(也就是空懸),至此我們完成了空鏈表的創建。稍加思考不難發現,帶頭結點的創建方式可以將空表與非空表的創建統一起來,更加方便(這不絕對,視情況而定)。
具體代碼實現如下:
template <typename T>
class LinkList
{
private:
Node<T>* first;
public:
LinkList();//
LinkList(T a[],int n);//
LinkList(T a[],int n,int tail);//
~LinkList();//
int Length();//
T LocationFind(int i);//
int ValueFind(T value);//
void Insert(int i,T x);
T LocationDelete(int n);//
int ValueDelete(T x);//
bool Empty();
void Print();
};
template <typename T>
LinkList<T>::LinkList()
{
first = new Node<T>;
first->next = NULL;
}
first指針是指向第一個節點的必備指針,如果沒有first,鏈表就無跡可尋,簡單地講,我們把鏈表弄丟了。
創建完空表後我們就可以進行插入了,當然也可以寫一個非空表的創建,不過這些操作都可以用插入代替。不同於順序表,鏈表需要依次處理每個節點,這是其分佈結構決定的,也是區別於順序表最大的不同。我們可以選擇按值插入或者按地址插入,不過這都需要先找到符合條件的指針,因此設置工作指針p,p從頭節點開始遍歷鏈表,通過p = p->next 進行後移,直到鏈表結束或者找到符合條件的節點。
具體代碼實現如下:
cpp
template<typename T>
void LinkList<T>::Insert(int n,T x)
{
Node<T>* p = first;
int counts = 0;
while(p->next!=NULL&&counts<n-1)
{
p = p->next;
counts++;
}
if(p->next==NULL) throw "插入位置錯誤";
else{
Node<T>* q = new Node<T>;
q->data = x;
q->next = p->next;
p->next = q;
}
}
查找操作其實就是插入操作的一部分,同樣可以分爲按值查找和按地址查找。
具體代碼實現如下:
template <typename T>
T LinkList<T>::LocationFind(int i)
{
Node<T>* p = first->next;
int counts = 0;
while(p!=NULL&&counts<i-1)
{
p = p->next;
counts++;
}
if(p==NULL) return -1;
else return p->data;
}
template <typename T>
int LinkList<T>::ValueFind(T x)
{
Node<T>* p = first->next;
int counts = 1;
while(p!=NULL)
{
if(p->data==x) return counts;
else{
p = p->next;
counts++;
}
}
return -1;
}
刪除操作是在查找操作的前提下進行的,只需要地址刪除就足夠了,因爲按值查找返回的就是地址,可以調用其獲取。
具體代碼實現如下:
template <typename T>
T LinkList<T>::Delete(int n)
{
T x;
Node<T> *p = first, *q = NULL; //工作指針p指向頭結點
int count = 0;
while (p != NULL && count < n - 1) //查找第i-1個結點
{
p = p->next;
count++;
}
if (p == NULL || p->next == NULL || n == 0) //結點p不存在或p的後繼結點不存在
return -1;
//throw "Error";
else {
q = p->next; x = q->data; //暫存被刪結點
p->next = q->next; //摘鏈
delete q;
return x;
}
}
定義完增刪查改後,其他操作都可以在此基礎上進行。
最後是鏈表的析構,設置工作指針,依次後移,直至鏈表爲空,並回收內存。
具體代碼實現如下:
template <typename T>
LinkList<T>::~LinkList()
{
Node<T>* p = first;
while(first->next!=NULL)
{
first = first->next;
delete p;
p = first;
}
}
回收內存十分重要,不可省略。