一、線性表
線性表是一種抽象的數(shù)據(jù)類型,下面介紹幾種具體的線性表存儲(chǔ)結(jié)構(gòu)(即物理結(jié)構(gòu)):順序、鏈?zhǔn)胶挽o態(tài)鏈?zhǔn)健o(wú)論線性表采用哪種數(shù)據(jù)結(jié)構(gòu),她們的邏輯結(jié)構(gòu)是一樣的,都有以下性質(zhì):除第一個(gè)元素外,線性表中每個(gè)元素都有一個(gè)前驅(qū)元素;除最后一個(gè)元素外,線性表中每一個(gè)元素都有一個(gè)后繼元素。
實(shí)現(xiàn):
//線性表抽象類的定義
template<typename T>class AList
{
public:
void ClearList(); //重置線性表為空表
bool ListEmpty()const; //若線性表為空表,則返回 true;否則返回 false
int LocateElem(T e, bool(*eq) (T, T))const;
//返回第一個(gè)與 e 滿足關(guān)系 eq() 的數(shù)據(jù)元素的位序,若這樣的元素不存在,則返回值為 0
bool PriorElem(T e, bool(*eq) (T, T), T &pre_e)const;
//若 e 與表的某數(shù)據(jù)元素滿足定義的 eq() 相等關(guān)系,且該數(shù)據(jù)不是表中第一個(gè),
//則用 pre_e 返回它的前驅(qū),否則操作失敗,pre_e 無(wú)定義
bool NextElem(T e, bool(*eq) (T, T), T &next_e)const;
//若 e 與表中的某數(shù)據(jù)元素滿足定義的 eq() 相等關(guān)系,且該數(shù)據(jù)不是表中最后一個(gè),
//則用 next_e 返回它的后繼,否則操作失敗,next_e 無(wú)定義
bool ListDelete(int i, T &e);
//刪除線性表第 i 個(gè)數(shù)據(jù)元素(1 <= i <= ListLength()),并用 e 返回其值
void ListTraverse(void(*visit) (T*))const;
//依次對(duì)每個(gè)數(shù)據(jù)元素調(diào)用函數(shù) visit()
virtual bool GetElem(int i, T &e)const=0; //純虛函數(shù)
//用 e 返回線性表第 i 個(gè)元素的值(1 <= i <= ListLength())
virtual bool ListInsert(int i, T e)=0;
//在線性表第 i 個(gè)位置(1 <= i <= ListLength())之前插入新的數(shù)據(jù)元素
virtual int ListLength()const=0; //返回線性表的長(zhǎng)度,常成員函數(shù),不改變對(duì)象的值
};
1. 順序存儲(chǔ)結(jié)構(gòu)
順序存儲(chǔ)結(jié)構(gòu)容易實(shí)現(xiàn)隨機(jī)查找線性表的第 i 個(gè)元素的操作,但在實(shí)現(xiàn)插入和刪除操作時(shí)要移動(dòng)大量的數(shù)據(jù)元素。所以,它適用于數(shù)據(jù)相對(duì)穩(wěn)定的線性表。
實(shí)現(xiàn):
//繼承 AList 的順序表類
template<typename T>class SqList: public AList<T>
{
friend void MergeList(const SqList<T>&, const SqList<T>&, SqList<T>&);
//聲明普通函數(shù) MerfeList() 為 SqList 類的友元
private:
T *elem; //線性表存儲(chǔ)空間的基址
int length; //線性表的當(dāng)前表長(zhǎng)
int listsize; //線性表當(dāng)前的存儲(chǔ)容量
public:
SqList(int k=1)
{//構(gòu)造函數(shù),動(dòng)態(tài)生成具有 k 個(gè)初始存儲(chǔ)空間的空線性表
elem = new T[k];
assert(elem != NULL); //存儲(chǔ)分配失敗,退出
length = 0; //空表長(zhǎng)度為 0
listsize = k; //初始存儲(chǔ)容量
}
~SqList()
{//析構(gòu)函數(shù)
delete[] elem; //釋放 elem 所指的存儲(chǔ)空間數(shù)組
}
void ClearList()
{//重置線性表為空表
length = 0;
}
bool ListEmpty()const //常成員函數(shù),不會(huì)改變對(duì)象的值
{//若線性表為空表,則返回 true;否則返回 false
return length == 0;
}
int ListLength()const
{//返回線性表的長(zhǎng)度
return length;
}
bool GetElem(int i, T &e)const
{//用 e 返回線性表第 i 個(gè)元素的值(1 <= i <= ListLength())
if (i < 1 || i > length) //i 不再表的范圍之內(nèi)
return false;
e = *(elem + i - 1); //將第 i 個(gè)元素的值賦給 e
return true;
}
int LocateElem(T e, bool(*eq) (T, T))const
{//返回第一個(gè)與 e 滿足關(guān)系 eq() 的數(shù)據(jù)元素的位序,若這樣的元素不存在,則返回值為 0
int i = 1; //i 的初值指第一個(gè)元素
while(i <= length && !eq(*(elem + i - 1), e))
//i 沒超出表的范圍且 i 指向的數(shù)據(jù)元素與 e 不滿足關(guān)系
i++; //計(jì)數(shù)加 1 ,繼續(xù)往后找
if (i <= length) //找到滿足關(guān)系的數(shù)據(jù)元素
return i; //返回其位序
else //沒找到滿足關(guān)系的數(shù)據(jù)元素
return 0;
}
int PriorElem(T e, bool(*eq) (T, T), T &pre_e)const
{//若 e 與表的某數(shù)據(jù)元素滿足 eq() 關(guān)系,且該數(shù)據(jù)元素不是表中第一個(gè),
//則用 pre_e 返回它的前驅(qū),否則操作失敗,pre_e 無(wú)定義
int i = LocateElem(e, eq); //將第一個(gè)滿足關(guān)系的數(shù)據(jù)元素的位序賦給 i
if (i <= 1) //沒找到,或是第一個(gè)元素
return false; //操作失敗
else //找到了值為 e 的元素,其位序?yàn)?i
{
pre_e = *(elem + i - 2); //將前一個(gè)元素的值賦給 pre_e
return true; //操作成功
}
}
bool NextElem(T e, bool(*eq) (T, T), T &next_e)const
{//若 e 與表的某數(shù)據(jù)元素滿足 eq() 關(guān)系,且該數(shù)據(jù)元素不是表中最后一個(gè),
//則用 next_e 返回它的后繼,否則操作失敗,next_e 無(wú)定義
int i = LocateElem(e, eq);
if (i == 0 || i == length) //沒找到,或是最后一個(gè)元素
return false;
else
{
next_e = *(elem + i);
return true;
}
}
bool ListInsert(int i, T e)
{//在線性表第 i 個(gè)位置(1 <= i <= ListLength())之前插入新的數(shù)據(jù)元素 e
T *newbase, *q, *p;
if (i < 1 || i > length) //i 值不合法
return false;
if (length == listsize) //當(dāng)前存儲(chǔ)空間已滿
{
newbase = new T[listsize * 2];
assert(newbase != NULL); //存儲(chǔ)空間分配失敗,退出
for (int j = 0; j < length; j++)
*(newbase + j) = *(elem + j); //將原表空間中的數(shù)據(jù)復(fù)制到新的表空間
delete[] elem; //釋放原表空間
elem = newbase; //新基址賦給 elem
listsize *= 2; //更新存儲(chǔ)容量
}
q = elem + i - 1; // q 為插入位置
for (p = elem + length - 1; p >= q; p--) //插入位置之后的元素后移
*(p + 1) = *p;
*q = e;
length++;
return true;
}
bool ListDelete(int i, T &e)
{//刪除線性表第 i 個(gè)元素(1 <= i <= ListLength()),并用 e 返回其值
T *p, *q;
if (i < 1 || i > length) //i 值不合法
return false;
p = elem + i - 1; //p 為被刪除元素的位置
e = *p;
q = elem + length -1; //q 為表尾元素的位置
for (++p; p <= q; ++p) //被刪除元素之后的元素前移
*(p - 1) = *p;
length--;
return true;
}
void ListTraverse(void(*visit) (T*))const;
{//依次對(duì)每個(gè)數(shù)據(jù)元素調(diào)用函數(shù) visit()
for (int i = 0; i < length; i++)
visit(elem + i); //對(duì)每個(gè)數(shù)據(jù)元素調(diào)用 visit()
cout << endl;
}
};
2. 鏈?zhǔn)酱鎯?chǔ)
與順序存儲(chǔ)相比,鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)在實(shí)現(xiàn)插入、刪除的操作時(shí)不需要移動(dòng)大量數(shù)據(jù)元素,但是不容易實(shí)現(xiàn)隨機(jī)存取線性表的第 i 個(gè)數(shù)據(jù)元素的操作。所以,鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)適用于經(jīng)常需要進(jìn)行插入和刪除操作的線性表。
2.1 單鏈表
實(shí)現(xiàn):
//單鏈表結(jié)點(diǎn)類型結(jié)構(gòu)體
template<typename T>struct LNode
{
T data; //結(jié)點(diǎn)數(shù)據(jù)類型
LNode<T> *next; //后繼指針
};
//單鏈表的類
template<typename T>class LinkList: public AList
{//帶模板并繼承 AList 的單鏈表類
friend void MergeList(LinkList<T>&, LinkList<T>&);
protected:
LNode<T> *Head; //頭指針
public:
LinkList()
{//構(gòu)造函數(shù),構(gòu)造一個(gè)空的線性表
Head = new LNode<T>; //產(chǎn)生頭結(jié)點(diǎn)
assert(Head != NULL); //存儲(chǔ)空間分配失敗,退出
Head->next = NULL; //頭結(jié)點(diǎn)的指針為空
}
~LinkList()
{//析構(gòu)函數(shù),銷毀線性表
ClearList(); //置為空表
delete Head; //釋放頭結(jié)點(diǎn)
}
void ClearList()const
{//重置線性表為空
LNode<T> *q, *p = Head->next; //p 指向第一個(gè)結(jié)點(diǎn)
Head->next = NULL; //頭結(jié)點(diǎn)指針域?yàn)榭? while(!p = NULL) //釋放其他結(jié)點(diǎn)
{
q = p->next;
delete p;
p = q;
}
}
bool ListEmpty()const
{//若線性表為空表,則返回 true,否則返回 false
return Head->next == NULL;
}
int ListLength()const
{//返回線性表的長(zhǎng)度
int i = 0; //計(jì)數(shù)器初值為 0
LNode<T> *p = Head->next; //p 指向第一個(gè)結(jié)點(diǎn)
while(p != NULL) //沒到表尾
{
i++;
p->next;
}
return i;
}
bool GetElem(int i, T &e)const
{//當(dāng)?shù)?i 個(gè)元素存在(1 <= i <= ListLength())時(shí),其值賦給 e 并返回 true ,
//否則返回 false
int j = 1;
LNode<T> *p = Head->next; //p 指向第一個(gè)結(jié)點(diǎn)
while(p != NULL && j < i) //順序查找
{
j++;
p = p->next;
}
if (p == NULL || j > i)
return false;
e = p->data;
return true;
}
int LocateElem(T e, bool(*eq) (T, T))const
{//返回第一個(gè)與 e 滿足 eq() 關(guān)系的數(shù)據(jù)元素的位序,若這樣的元素不存在,則返回 0
int i = 0;
LNode<T> *p = Head->next;
while(p != NULL)
{
i++;
if (eq(p->data, e))
return i;
p = p->next;
}
return 0;
}
bool PriorElem(T e, bool(*eq) (T, T), T &pre_e)const
{//若 e 與表中的某數(shù)據(jù)元素滿足 eq() 關(guān)系,且該元素不是表中第一個(gè),
//則用 pre_e 返回它的前驅(qū),否則操作失敗,pre_e 無(wú)定義
LNode<T> *p = Head->next;
while(p != NULL && p->next != NULL) //p 所指結(jié)點(diǎn)有后繼
{
q = p->next;
if (eq(q->data, e))
{
pre_e = p->data;
return true;
}
p = q; //p 的后繼 q 不等于 e ,p 向后移
}
return false;
}
bool NextElem(T e, bool(*eq) (T, T), T &next_e)const
{//若 e 與表中某數(shù)據(jù)元素滿足 eq() 關(guān)系,且該元素不是表中最后一個(gè),
//則用 next_e 返回它的后繼,否則操作失敗,next_e 無(wú)定義
LNode<T> *p = Head->next;
while(p != NULL && p->next != NULL)
{
if (eq(p->data, e))
{
next_e = p->next->data;
return true;
}
p = p->next;
}
return false;
}
bool ListInsert(int i, T e)
{//在帶頭結(jié)點(diǎn)的單鏈表中第 i 個(gè)位置(1 <= i <= ListLength())之前插入新的數(shù)據(jù)元素 e
int j = 0;
LNode<T> *s, *p = Head;
while(p != NULL && j < i-1) //尋找第 i-1 個(gè)結(jié)點(diǎn)
{
j++;
p = p->next;
}
if (p == NULL || j > i-1) //i 小于 1 或者大于表長(zhǎng)+1
return false;
s = new LNode<T>; //生成新結(jié)點(diǎn)
if (s == NULL) //生成新結(jié)點(diǎn)失敗
return false; //插入失敗
s->data = e;
s->next = p->next; //新結(jié)點(diǎn)指向原第 i 個(gè)結(jié)點(diǎn)
p-next = s; //原第 i-1 個(gè)結(jié)點(diǎn)指向新結(jié)點(diǎn)
return true; //插入成功
}
bool ListDelete(int i, T &e)const
{//在帶頭結(jié)點(diǎn)的單鏈線性表中刪除第 i 個(gè)數(shù)據(jù)元素(1 <= i <= ListLength()),
//并用 e 返回其值
int j = 0;
LNode<T> *q, *p = Head;
while(p->next != NULL && j < i-1) //尋找第 i 個(gè)結(jié)點(diǎn),并令 p 指向其前驅(qū)
{
j++;
p = p->next;
}
if (p->next == NULL || j > i-1)
return false;
q = p->next; //q 指向刪除結(jié)點(diǎn)
p->next = q->next; //待刪結(jié)點(diǎn)的前驅(qū)指向待刪結(jié)點(diǎn)的后繼
e = q->data;
delete q;
return true;
}
void ListTraverse(void(*visit) (T*))const
{//依次對(duì)每個(gè)數(shù)據(jù)元素調(diào)用函數(shù) visit()
LNode<T> *p = Head->next;
while(p != NULL)
{
visit(&p->data);
p = p->next;
}
cout << endl;
}
};
2.2 單循環(huán)鏈表
單循環(huán)鏈表結(jié)點(diǎn)的存儲(chǔ)結(jié)構(gòu)和單鏈表結(jié)點(diǎn)的一樣,不同的是:?jiǎn)窝h(huán)鏈表最后一個(gè)結(jié)點(diǎn)的指針域指向頭結(jié)點(diǎn),而不是 NULL 。這樣,由表尾很容易找到表頭。但若鏈表較長(zhǎng),則由表頭找到表尾仍較費(fèi)時(shí)。因而,單循環(huán)鏈表往往設(shè)立尾指針而不是頭指針。這樣,查找最后一個(gè)結(jié)點(diǎn)和頭結(jié)點(diǎn)都很方便。這在兩個(gè)鏈表首尾相連合并成一個(gè)鏈表時(shí)尤為方便。
實(shí)現(xiàn):
//設(shè)立尾指針的單循環(huán)鏈表的類
template<typename T>class LinkListCy: public AList
{//帶模板并繼承 AList 的單循環(huán)鏈表類
friend void MergeList(LinkListCy<T>&, LinkListCy<T>&);
private:
LNode<T> *Tail; //尾指針
public:
LinkListCy()
{//構(gòu)造函數(shù),構(gòu)造一個(gè)空的線性表
Tail = new LNode<T>; //產(chǎn)生頭結(jié)點(diǎn),并使 Tail 指向此頭結(jié)點(diǎn)(尾指針指向頭結(jié)點(diǎn))
assert(Tail != NULL); //存儲(chǔ)分配失敗,退出
Tail->next = Tail; //頭結(jié)點(diǎn)的指針域指向頭結(jié)點(diǎn)
}
~LinkListCy()
{//析構(gòu)函數(shù),銷毀線性表
ClearList(); //將線性表重置為空表
delete Tail; //釋放頭結(jié)點(diǎn)
}
void ClearList()
{//將線性表重置為空表
LNode<T> *p, *q;
Tail = Tail->next; //Tail 指向頭結(jié)點(diǎn)
p = Tail->next; //p 指向第一個(gè)結(jié)點(diǎn)
while(p != Tail) //沒到表尾
{
q = p->next;
delete p;
p = q;
}
Tail->next = Tail; //頭結(jié)點(diǎn)指針域指向自身
}
bool ListEmpty()const
{//若線性表為空表,則返回 true ;否則返回 false
return Tail->next==Tail;
}
int ListLength()const
{//返回線性表中數(shù)據(jù)元素個(gè)數(shù)
int i = 0;
LNode<T> *p = Tail->next; //p指向頭結(jié)點(diǎn)
while(p != Tail)
{
i++;
p = p->next;
}
return i;
}
bool GetElem(int i, T &e)const
{//在第 i 個(gè)元素存在時(shí),其值賦給 e 并返回 true ;否則返回 false
int j = 1;
LNode<T> *p = Tail->next->next; //p 指向第一個(gè)結(jié)點(diǎn)
if (i <= 0 || i > ListLength())
return false;
while(j < i)
{
j++;
p = p->next;
}
e = p->data;
return true;
}
int LocateElem(T e, bool(*eq) (T, T))const
{//返回第一個(gè)與 e 滿足 eq() 關(guān)系的數(shù)據(jù)元素的位序,若這樣的元素不存在,則返回 0
int i = 0;
LNode<T> *p = Tail->next->next; //p 指向第一個(gè)結(jié)點(diǎn)
while(p != Tail->next)
{
i++;
if (eq(p->data, e))
return i;
p = p->next;
}
return 0;
}
bool PriorElem(T e, bool(*eq) (T, T), T &pre_e)const
{//若 e 與表中的某數(shù)據(jù)元素滿足 eq() 關(guān)系,且該元素不是表中第一個(gè),
//則用 pre_e 返回它的前驅(qū),否則操作失敗,pre_e 無(wú)定義
LNode<T> *q, *p = Tail->next->next; //p 指向第一個(gè)結(jié)點(diǎn)
q = p->next;
while(q != Tail->next)
{
if (eq(q->data, e))
{
pre_e = p->data;
return true;
}
p = q; //p 的后繼 q 不等于 e ,p 向后移
q = q->next;
}
return false;
}
bool NextElem(T e, bool(*eq) (T, T), T &next_e)const
{//若 e 與表中某數(shù)據(jù)元素滿足 eq() 關(guān)系,且該元素不是表中最后一個(gè),
//則用 next_e 返回它的后繼,否則操作失敗,next_e 無(wú)定義
LNode<T> *p = Tail->next->next;
while(p != Tail)
{
if (eq(p->data, e))
{
next_e = p->next->data;
return true;
}
p = p->next;
}
return false;
}
bool ListInsert(int i, T e)
{//在帶頭結(jié)點(diǎn)的單鏈表中第 i 個(gè)位置(1 <= i <= ListLength())之前插入新的數(shù)據(jù)元素 e
int j = 0;
LNode<T> *p = Head;
if (i <= 0 || i > ListLength()+1)
return false;
while(j < i-1) //尋找第 i-1 個(gè)結(jié)點(diǎn)
{
j++;
p = p->next;
}
s = new LNode<T>; //生成新結(jié)點(diǎn)
if (s == NULL) //生成新結(jié)點(diǎn)失敗
return false; //插入失敗
s->data = e;
s->next = p->next; //新結(jié)點(diǎn)指向原第 i 個(gè)結(jié)點(diǎn)
p-next = s; //原第 i-1 個(gè)結(jié)點(diǎn)指向新結(jié)點(diǎn)
if (p == Tail) //插在表尾
Tail = s;
return true; //插入成功
}
bool ListDelete(int i, T &e)
{//在帶頭結(jié)點(diǎn)的單鏈線性表中刪除第 i 個(gè)數(shù)據(jù)元素(1 <= i <= ListLength()),
//并用 e 返回其值
int j = 0;
LNode<T> *q, *p = Tail->next;
if (i <= 0 || i > ListLength())
return false;
while(j < i-1) //尋找第 i 個(gè)結(jié)點(diǎn),并令 p 指向其前驅(qū)
{
j++;
p = p->next;
}
q = p->next; //q 指向刪除結(jié)點(diǎn)
p->next = q->next; //待刪結(jié)點(diǎn)的前驅(qū)指向待刪結(jié)點(diǎn)的后繼
e = q->data;
if (Tail == q)
Tail=p;
delete q;
return true;
}
void ListTraverse(void(*visit) (T*))const
{//依次對(duì)每個(gè)數(shù)據(jù)元素調(diào)用函數(shù) visit()
LNode<T> *p = Tail->next->next;
while(p != Tail->next) //p 不指向頭結(jié)點(diǎn)
{
visit(&p->data);
p = p->next;
}
cout << endl;
}
};
2.3 雙向循環(huán)鏈表
實(shí)現(xiàn):
//雙向結(jié)點(diǎn)類型結(jié)構(gòu)體
template<typename T>struct DLNode
{
T data;
DLNode<T> *prior, *next;
}
//雙向鏈表類
template<typename T> class DLinkList: public AList<T>
{//帶模板并繼承 AList 的雙向鏈表類
private:
DLNode<T> *Head; //頭指針
DLNode<T>* GetElemP(int i)const
{//在雙向鏈表中返回第 i 個(gè)結(jié)點(diǎn)的地址,i 為 0 ,則返回頭結(jié)點(diǎn)的地址
//若第 i 個(gè)結(jié)點(diǎn)不存在,則返回 NULL
int j = 0;
DLNode<T> *p = Head;
if (i < 0)
return NULL;
if (i == 0)
return p;
do
{
p = p->next;
j++;
}while(p != Head && j < i); //p 沒返回到頭結(jié)點(diǎn)并且還沒到第 i 個(gè)結(jié)點(diǎn)
if (p == Head) //查找失敗
return NULL;
else
return p;
}
DLNode<T>* GetElemE(T e, bool(*eq) (T, T))const
{//在雙向鏈表中返回第一個(gè)與 e 滿足定義的 eq() 相等關(guān)系的數(shù)據(jù)元素地址
//若這樣的數(shù)據(jù)元素不存在,返回 NULL
DLNode<T> *P = Head->next;
while(p != Head && !eq(p->data, e))
p = p->next;
if (p == Head) //這樣的元素不存在
return NULL;
else
return p;
}
public:
DLinkList()
{//構(gòu)造一個(gè)空的雙向循環(huán)鏈表
Head = new DLNode<T>; //產(chǎn)生頭結(jié)點(diǎn)
assert(Head != NULL)
Head->next = Head->prior = Head; //頭結(jié)點(diǎn)的指針域指向自身
}
~DLinkList()
{//析構(gòu)函數(shù),銷毀雙向鏈表
ClearList(); //置為空表
delete Head; //釋放頭結(jié)點(diǎn)
}
void ClearList()const
{//重置雙向循環(huán)線性表為空表
DLNode<T> *P = Head->next;
while(p != Head)
{
p = p->next;
delete p->prior;
}
Head->next = Head->prior = Head;
}
bool ListEmpty()const
{//若線性表為空表,則返回 true ;否則返回 false
return Head->next == Head;
}
int ListLength()const
{//返回線性表的長(zhǎng)度
int i = 0;
DLNode<T> *p = Head->next;
while(p != Head)
{
i++;
p = p->next;
}
return i;
}
bool GetElem(int i, T &e)const
{//在第 i 個(gè)元素存在時(shí),其值賦給 e 并返回 true ;否則返回 false
DLNode<T> *p = GetElemP(i); //p 指向第 i 個(gè)結(jié)點(diǎn)
if (p != NULL) //第 i 個(gè)結(jié)點(diǎn)存在
{
e = p->data;
return true;
}
else
return false;
}
int LocateElem(T e, bool(*eq) (T, T))const
{//返回第一個(gè)與 e 滿足 eq() 關(guān)系的數(shù)據(jù)元素的位序,若這樣的元素不存在,則返回 0
int i = 0;
LNode<T> *p = Head->next; //p 指向第一個(gè)結(jié)點(diǎn)
while(p != Head)
{
i++;
if (eq(p->data, e))
return i;
p = p->next;
}
return 0;
}
bool PriorElem(T e, bool(*eq) (T, T), T &pre_e)const
{//若 e 與表中的某數(shù)據(jù)元素滿足 eq() 關(guān)系,且該元素不是表中第一個(gè),
//則用 pre_e 返回它的前驅(qū),否則操作失敗,pre_e 無(wú)定義
LNode<T> *p = GetElemE(e, eq); //p 指向結(jié)點(diǎn) e
if (p != NULL && p->prior != Head) //p 指向值為 e 的結(jié)點(diǎn)且該結(jié)點(diǎn)不是第一個(gè)結(jié)點(diǎn)
{
pre_e = p->prior->data;
return true;
}
return false;
}
bool NextElem(T e, bool(*eq) (T, T), T &next_e)const
{//若 e 與表中某數(shù)據(jù)元素滿足 eq() 關(guān)系,且該元素不是表中最后一個(gè),
//則用 next_e 返回它的后繼,否則操作失敗,next_e 無(wú)定義
DLNode<T> *p = GetElemE(e, eq); //p 指向結(jié)點(diǎn) e
if (p != NULL && p->next != Head) //p 指向值為e的結(jié)點(diǎn)且該結(jié)點(diǎn)不是最后一個(gè)結(jié)點(diǎn)
{
pre_e = p->next->data;
return true;
}
return false;
}
bool ListInsert(int i, T e)
{//在帶頭結(jié)點(diǎn)的單鏈表中第 i 個(gè)位置(1 <= i <= ListLength())之前插入新的數(shù)據(jù)元素 e
DLNode<T> *s, *p = GetElemP(i-1); //確定第 i 個(gè)結(jié)點(diǎn)前驅(qū)的位置指針 p
if (p = NULL) //第 i 個(gè)結(jié)點(diǎn)的前驅(qū)不存在(設(shè)頭結(jié)點(diǎn)為第 0 個(gè)結(jié)點(diǎn))
return false;
s = new DLNode<T>; //生成新結(jié)點(diǎn)
if (s == NULL)
return false; //生成失敗
s->data = e;
s->prior = p;
s->next = p->next;
p->next->prior = s;
p->next = s;
return true;
}
bool ListDelete(int i, T &e)
{//在帶頭結(jié)點(diǎn)的單鏈線性表中刪除第 i 個(gè)數(shù)據(jù)元素(1 <= i <= ListLength()),
//并用 e 返回其值
DLNode<T> *p =GetElemP(i);
if (p == NULL)
return false;
e = p->data;
p->prior->next = p->next;
p->next->prior = p->prior;
delete p;
return true;
}
void ListTraverse(void(*visit) (T*))const
{//依次對(duì)每個(gè)數(shù)據(jù)元素調(diào)用函數(shù) visit()
DLNode<T> *p = Head->next;
while(p != Head) //p 不指向頭結(jié)點(diǎn)
{
visit(&p->data);
p = p->next;
}
cout << endl;
}
void ListBackTraverse(void(*visit) (T*))const
{//由雙向循環(huán)線性表的頭結(jié)點(diǎn)出發(fā),逆序?qū)γ總€(gè)元素調(diào)用函數(shù) visit()
DLNode<T> *p = Head->prior; //p 指向尾結(jié)點(diǎn)
while(p != Head)
{
visit(&p->data);
p = p->prior;
}
cout << endl;
}
};
2.4 不設(shè)頭結(jié)點(diǎn)的鏈表
鏈表也可以不設(shè)頭結(jié)點(diǎn),這樣看起來(lái)更自然一些。但不設(shè)頭結(jié)點(diǎn)會(huì)使得對(duì)第 1 個(gè)元素做插入或刪除的操作與其他元素不同,這會(huì)帶來(lái)編程上的麻煩。不設(shè)頭結(jié)點(diǎn)的雙向循環(huán)鏈表在動(dòng)態(tài)存儲(chǔ)管理中有應(yīng)用。下面對(duì)它們做簡(jiǎn)單介紹,因?yàn)槭呛?jiǎn)單介紹,沒有完全實(shí)現(xiàn)線性表抽象類 AList 的 3 個(gè)純虛函數(shù),故他們不能繼承 AList 。
實(shí)現(xiàn):
//不設(shè)頭結(jié)點(diǎn)的單鏈表類
template<typename T>class LinkListNH
{//帶模板的不設(shè)頭結(jié)點(diǎn)的單鏈表類
private:
LNode<T> *Head; //頭指針
public:
LinkListNH()
{//構(gòu)造函數(shù),構(gòu)造一個(gè)空的線性表
Head = NULL; //指針為空
}
~LinkListNH()
{//析構(gòu)函數(shù),銷毀線性表
ClearList(); //將線性表重置為空表
}
void ClearList()
{//將線性表重置為空表
LNode<T> *p;
while(Head != NULL) //表不為空
{
p = Head; //p 指向第一個(gè)結(jié)點(diǎn)
Head = Head->next; //Head 指向下一個(gè)結(jié)點(diǎn)
delete p;
}
}
int ListLength()const
{//返回線性表的長(zhǎng)度
int i = 0;
LNode<T> *p = Head; //p指向頭結(jié)點(diǎn)
while(p != NULL)
{
i++;
p = p->next;
}
return i;
}
int LocateElem(T e, bool(*eq) (T, T))const
{//返回第一個(gè)與 e 滿足 eq() 關(guān)系的數(shù)據(jù)元素的位序,若這樣的元素不存在,則返回 0
int i = 0;
LNode<T> *p = Head; //p 指向第一個(gè)結(jié)點(diǎn)
while(p != NULL)
{
i++;
if (eq(p->data, e))
return i;
p = p->next;
}
return 0;
}
LNode<T>* Point(T e, bool(*eq) (T, T), LNode<T>* &p)const
{//查找表中第一個(gè)與 e 滿足 eq() 關(guān)系的結(jié)點(diǎn),如找到,返回指向該結(jié)點(diǎn)的指針,
//p 指向該結(jié)點(diǎn)的前驅(qū)(若該結(jié)點(diǎn)是第一個(gè)結(jié)點(diǎn),則 p=NULL),沒找到則返回NULL,p無(wú)定義
if (Head) //表不空
if (eq(Head->data, e))
{
p = NULL;
return Head;
}
else
{
p = Head;
while(p->next->data, e)
if (eq(p->next->data, e))
return p->next;
else
p = p->next;
}
return NULL;
}
bool ListInsert(int i, T e)
{//在不設(shè)頭結(jié)點(diǎn)的單鏈線性表第 i 個(gè)位置之前插入元素 e
int j = 1;
LNode<T> *s, *p = Head;
if (i < 1)
return false;
s = new LNode<T>;
if (s == NULL)
return false;
s->data = e;
if (i == 1) //插在表頭
{
s->next = Head; //新結(jié)點(diǎn)指向原第一結(jié)點(diǎn)
Head = s; //Head 指向新結(jié)點(diǎn)
}
else
{//插在表的其余處
while(p != NULL && j < i-1) //尋找第 i-1 個(gè)結(jié)點(diǎn)
{
j++;
p = p->next;
}
if (p == NULL) //i 大于表長(zhǎng)+1
return false;
else //p 指向第 i-1 個(gè)結(jié)點(diǎn)
{
s->next = p->next;
p-next = s;
}
}
return true;
}
bool ListDelete(T e, bool(*eq) (T, T))
{//在不設(shè)頭結(jié)點(diǎn)的單鏈線性表中,刪除與 e 滿足 eq() 關(guān)系的結(jié)點(diǎn)
LNode<T> *p, *q;
q = Point(e, eq, p); //p 指向待刪結(jié)點(diǎn)的前驅(qū)
if (q == NULL) //沒找到待刪結(jié)點(diǎn)
return false;
else //找到待刪結(jié)點(diǎn)
{
if (p == NULL) //待刪結(jié)點(diǎn)是第一個(gè)結(jié)點(diǎn)
Head = q->next;
else
p->next = q->next;
delete q;
return true;
}
}
void ListTraverse(void(*visit) (T*))const
{//依次對(duì)每個(gè)數(shù)據(jù)元素調(diào)用函數(shù) visit()
LNode<T> *p = Head; //p 指向第一個(gè)結(jié)點(diǎn)
while(p != NULL) //p 所指結(jié)點(diǎn)存在
{
visit(&p->data);
p = p->next;
}
cout << endl;
}
};
//不設(shè)頭結(jié)點(diǎn)的雙向鏈表的類
template<typename T>class DLinkListNH
{//帶模板的不設(shè)頭結(jié)點(diǎn)的雙向鏈表類
friend void Joseph(int, int);
friend class BoundaryLogo; //聲明邊界標(biāo)識(shí)法類為友類
friend class BuddySystem; //聲明伙伴系統(tǒng)為友類
private:
DLNode<T> *Head; //頭指針
DLNode<T>* GetElemP(int i)const
{//在雙向鏈表中返回第 i 個(gè)結(jié)點(diǎn)的地址,若第 i 個(gè)結(jié)點(diǎn)不存在,返回 NULL
int j = 1;
DLNode<T> *p = Head; //p 指向第一個(gè)結(jié)點(diǎn)
if (i <= 0 || Head == NULL)
return NULL;
if (i == 1) //第一個(gè)結(jié)點(diǎn)
return p;
do
{
p = p->next;
j++;
}while(p != Head && j < i);
if (p == Head) //第 i 個(gè)結(jié)點(diǎn)不存在
return NULL;
else
return p;
}
public:
DLinkListNH()
{//構(gòu)造一個(gè)空的雙向循環(huán)鏈表
Head = NULL; //頭指針為空
}
~DLinkListNH()
{//析構(gòu)函數(shù),銷毀雙向循環(huán)鏈表
ClearList();
}
void ClearList()
{//重置雙向循環(huán)鏈表為空表
//不帶頭結(jié)點(diǎn)的循環(huán)鏈表可以解開先循環(huán)再依次刪除,
//也可以不解開循環(huán)進(jìn)行依次刪除,不過(guò)最后還得單獨(dú)把頭指針?biāo)赶虻慕Y(jié)點(diǎn)刪除
DLNode<T> *p = Head; //p 指向第一個(gè)結(jié)點(diǎn)
if (Head != NULL)
{
Head->prior->next = NULL; //打開循環(huán)鏈表為單鏈表,很關(guān)鍵
while(p != NULL)
{
p = p->next;
delete Head;
Head = p;
}
}
}
int ListLength()const
{//返回線性表的長(zhǎng)度
int i = 0;
DLNode<T> *p = Head;
if (Head != NULL)
do
{
i++;
p = p->next;
}while(p != Head);
return i;
}
bool ListInsert(int i, T e)
{//在不設(shè)頭結(jié)點(diǎn)的雙向循環(huán)鏈表中第 i 個(gè)位置之前插入新的數(shù)據(jù)元素 e
DLNode<T> *s, *p = GetElemP(i-1); //p 指向第 i-1 個(gè)結(jié)點(diǎn)
s = new DLNode<T>;
if (s == NULL)
return false;
s->data = e;
if (i == 1) //在第一個(gè)結(jié)點(diǎn)前插入
{
if (Head == NULL)
s->prior = s->next =s;
else
{
s->prior = Head->prior;
s->next = Head;
s->prior->next = s;
s->next->prior =s;
}
Head = s;
return true;
}
else //i > 1
{
if (p == NULL) //第 i-1 個(gè)結(jié)點(diǎn)不存在
return false;
else
{
s->next = p->next;
s->prior = p;
s->next->prior = s;
p->next = s;
return true;
}
}
}
bool ListDelete(int i, T &e)
{//刪除不帶頭結(jié)點(diǎn)的雙向循環(huán)鏈表的第 i 個(gè)元素,并用 e 返回其值
DLNode<T> *p = GetElemP(i); //p 指向第 i 個(gè)結(jié)點(diǎn)
if (p == NULL)
return false;
e = p->data;
if (p->next == p) //表中只有一個(gè)元素,且將被刪除
Head == NULL;
else //表中有多個(gè)元素
{
P->next->prior = p->prior;
p->prior->next = p->next;
if (p == Head) //刪除的是第一個(gè)結(jié)點(diǎn)
Head = p->next;
}
delete p;
return true;
}
void ListTraverse(void(*visit) (T*))const
{//依次對(duì)雙向循環(huán)鏈表的每個(gè)數(shù)據(jù)元素調(diào)用函數(shù) visit()
DLNode<T> *p = Head;
if (Head != NULL)
do
{
visit(p->data);
p = p->next;
}while(p != Head)
cout << endl;
}
};
用不設(shè)頭結(jié)點(diǎn)的循環(huán)鏈表解決某些問(wèn)題比較方便,如約瑟夫環(huán)問(wèn)題:n 個(gè)小孩圍坐一圈,由第一個(gè)小孩開始,依次循環(huán)報(bào)數(shù),報(bào)到 m 的小孩出列。從下一個(gè)小孩重新開始循環(huán)報(bào)數(shù),直到所有小孩都出列,求小孩出列順序。
C++ 的標(biāo)準(zhǔn)模板庫(kù)(STL)也提供了鏈表類的操作,稱為 list (表),使用雙向鏈表實(shí)現(xiàn)的。可以訪問(wèn)cplusplus查詢相關(guān)操作函數(shù)。
3. 靜態(tài)鏈表存儲(chǔ)結(jié)構(gòu)
順序存儲(chǔ)結(jié)構(gòu)也可以實(shí)現(xiàn)鏈?zhǔn)酱鎯?chǔ)功能。首先,開辟一個(gè)充分大的結(jié)構(gòu)體數(shù)組。結(jié)構(gòu)體數(shù)組的一個(gè)成員(data)存放數(shù)據(jù),另一個(gè)成員(link)存放鏈表中的下一個(gè)數(shù)據(jù)元素在數(shù)組中的位置(下標(biāo)),這稱為靜態(tài)鏈表。
靜態(tài)鏈表存于數(shù)組中,單聊標(biāo)的輸出卻不是按數(shù)組的順序輸出的,是由一個(gè)指定的位置開始根據(jù) link 的值依次輸出的。靜態(tài)鏈表存儲(chǔ)結(jié)構(gòu)在赫夫曼樹和內(nèi)部排序中都有應(yīng)用。
實(shí)現(xiàn):
const int MAX_SIZE = 10; //靜態(tài)鏈表的最大長(zhǎng)度
//靜態(tài)鏈表類
template<typename T>class StLinkList
{//帶模板的靜態(tài)鏈表類
struct component //類內(nèi)定義的結(jié)構(gòu)體,只用于本類內(nèi)
{
T data;
int link;
};
private:
component SL[MAX_SIZE]; //靜態(tài)數(shù)組
int NEW()
{//若備用鏈表非空,則返回分配的結(jié)點(diǎn)下標(biāo)(備用鏈表的第一個(gè)結(jié)點(diǎn));否則返回 0
int i = SL[MAX_SIZE - 1].link; //備用鏈表第一個(gè)結(jié)點(diǎn)的位置
if (i) //備用鏈表非空
SL[MAX_SIZE - 1].link = SL[i].link;
//備用鏈表的頭結(jié)點(diǎn)指向原備用鏈表的第二個(gè)結(jié)點(diǎn)
return i; //返回新開辟結(jié)點(diǎn)的下標(biāo)
}
void DELETE(int k)
{//將下標(biāo)為 k 的空閑結(jié)點(diǎn)回收到備用鏈表中,成為備用鏈表的第一個(gè)結(jié)點(diǎn)
SL[k].link = SL[MAX_SIZE - 1].link; //回收結(jié)點(diǎn)的下標(biāo)指向備用鏈表的第一個(gè)結(jié)點(diǎn)
SL[MAX_SIZE - 1].link = k; //備用鏈表的頭結(jié)點(diǎn)指向新回收的結(jié)點(diǎn)
}
public:
StLinkList()
{//構(gòu)造一個(gè)空的鏈表,表頭為單元 [0],其余單元鏈成一個(gè)備用鏈表
//備用鏈表表頭為最后一個(gè)單元 [MAX_SIZE - 1],link 域 0 表示空指針
int i;
SL[0].link = 0; //[0] 為空鏈表的表頭
SL[MAX_SIZE - 1].link = 1; //[1] 為備用鏈表的第一個(gè)單元
for(i = 1; i < MAX_SIZE - 2; i++)
//將其余單元鏈成以 [MAX_SIZE - 1] 為表頭的備用鏈表
SL[i].link = i + 1;
SL[MAX_SIZE - 2].link = 0;
}
void ClearList()
{//將靜態(tài)鏈表重置為空表
int j, i = SL[MAX_SIZE - 1].link; //i 指示備用鏈表第一個(gè)結(jié)點(diǎn)的位序
while(i) //未到備用鏈表表尾
{
j = i;
i = SL[i].link;
}
SL[j].link = SL[0].link; //鏈表的第一個(gè)結(jié)點(diǎn)接到備用鏈表的尾部
Sl[0].link = 0; //鏈表空
}
bool ListEmpty()const
{//若是空表,返回 true;否則返回 false
return SL[0].link == 0;
}
int ListLength()const
{//返回表中數(shù)據(jù)元素個(gè)數(shù)
int j = 0, i = SL[0].link; //i 指向鏈表的第一個(gè)結(jié)點(diǎn)的位序
while(i)
{
i = SL[i].link;
j++;
}
return j;
}
bool PriorElem(T e, bool(*eq) (T, T), T &pre_e)const
{//若 e 與表中某數(shù)據(jù)元素滿足定義的 eq() 關(guān)系,且該數(shù)據(jù)不是表中第一個(gè),
//則用 pre_e 返回它的前驅(qū),否則操作失敗,pre_e 無(wú)定義
int j, i = SL[0].link;
do
{
j = i;
i = SL[i].link;
}while(i && !eq(Sl[i].data, e)); //i 所指元素存在且其值不是 e ,繼續(xù)循環(huán)
if (i) //找到該元素
{
pre_e = SL[i].data;
return true;
}
return false; //沒找到該元素,或其無(wú)前驅(qū)
}
bool NextElem(T e, bool(*eq) (T, T), T &next_e)const
{//若 e 與表中某數(shù)據(jù)元素滿足定義的 eq() 關(guān)系,且該元素不是表中最后一個(gè),
//則用 next_e 返回它的后繼,否則操作失敗,next_e 無(wú)定義
int i = SL[0].link;
while(i)
{
if (eq(SL[i].data, e) && SL[i].link) //找到該元素且其有后繼
{
next_e = SL[SL[i].link].data;
return true;
}
i = SL[i].link;
}
return false; //不存在該元素,或者其無(wú)后繼
}
bool ListInsert(int i, T e)
{//在靜態(tài)鏈表的第 i 個(gè)元素
int m, k = 0; //k 指示頭結(jié)點(diǎn)的位序
for (m = 1; m < i; m++)
{
k = SL[k].link; //k 指向下一結(jié)點(diǎn)
if (k == 0) //已到表尾
break;
}
if (m < i) //表中沒有第 i-1 個(gè)結(jié)點(diǎn)
return false;
else //表中有第 i-1 個(gè)結(jié)點(diǎn),并由 k 指向
{
m = NEW(); //申請(qǐng)新單元
if (m) //申請(qǐng)成功
{
SL[m].data = e;
SL[m].link = SL[k].link;
SL[k].link = m;
return true;
}
return false;
}
}
bool ListDelete(int i, T &e)
{//刪除第 i 個(gè)數(shù)據(jù)元素,并用 e 返回其值
int m, k = 0; //k 指示頭結(jié)點(diǎn)的位序
for(m = 1; m < i; m++)
{
k = SL[k].link;
if (k == 0) //已到表尾
break;
}
if (m < i || SL[k].link == 0) //表中沒有第 i-1 個(gè)結(jié)點(diǎn)或者沒有第 i 個(gè)結(jié)點(diǎn)
return false;
else
{
m = SL[k].link;
SL[k].link = SL[m].link;
e = SL[m].data;
DELETE(m);
return true;
}
}
void ListTraverse(void(*visit) (T*))
{//依次對(duì)每個(gè)元素調(diào)用函數(shù) visit()
int i = SL[0].link;
while(i)
{
visit(&SL[i].data);
i = SL[i].link;
}
cout << endl;
}
};
靜態(tài)鏈表存儲(chǔ)于數(shù)組中,但其順序卻不是按數(shù)組下標(biāo)的順序,而是像鏈表一樣,由當(dāng)前結(jié)點(diǎn) link 域的值決定下一結(jié)點(diǎn)的位置,這是鏈表的特性;又由于用數(shù)組存儲(chǔ),故稱為靜態(tài)鏈表。我們只要知道第一個(gè)結(jié)點(diǎn)(頭結(jié)點(diǎn))的位置就可以依次找到靜態(tài)鏈表中的其他結(jié)點(diǎn)。我們將不再鏈表中的空閑結(jié)點(diǎn)也鏈接成一個(gè)靜態(tài)鏈表,成為 “備用鏈表” 。靜態(tài)鏈表數(shù)組 SL 中有一個(gè)鏈表頭結(jié)點(diǎn)在位置 [0],有一個(gè)備用鏈表頭結(jié)點(diǎn)在位置 [MAX_SIZE - 1] ,其余結(jié)點(diǎn)不再鏈表中就在備用鏈表中。
當(dāng)靜態(tài)鏈表需要新結(jié)點(diǎn)時(shí),我們把備用鏈表中的第一個(gè)結(jié)點(diǎn)從備用鏈表中刪除,作為新結(jié)點(diǎn)插入靜態(tài)鏈表。當(dāng)刪除靜態(tài)鏈表中的結(jié)點(diǎn)時(shí),被刪除的結(jié)點(diǎn)插入備用鏈表中,成為備用鏈表的第一個(gè)結(jié)點(diǎn)。之所以從備用鏈表刪除結(jié)點(diǎn)或是插入結(jié)點(diǎn)的操作都在表頭進(jìn)行,是因?yàn)檫@樣的效率最高。
備注:如果不理解備用鏈表,可以參考下面這篇博客:
靜態(tài)鏈表 C實(shí)現(xiàn)。
靜態(tài)鏈表和單鏈表的主要區(qū)別:
- 單鏈表的結(jié)點(diǎn)通過(guò) new 關(guān)鍵字產(chǎn)生,結(jié)點(diǎn)被限制在動(dòng)態(tài)存儲(chǔ)區(qū)這個(gè)大范圍內(nèi)。因此,指示結(jié)點(diǎn)位置的指針類型實(shí)際上是常整型。而靜態(tài)鏈表的結(jié)點(diǎn)被限制在靜態(tài)數(shù)組這個(gè)小范圍內(nèi)。因此,指示結(jié)點(diǎn)位置的指針類型(link)一般是整型;
- 單鏈表不用的結(jié)點(diǎn)通過(guò) delete 關(guān)鍵字釋放,釋放后的結(jié)點(diǎn)由編譯軟件管理。而靜態(tài)鏈表中不用的結(jié)點(diǎn)要自己管理(插入到備用鏈表中)。