必威电竞|足球世界杯竞猜平台

雙向鏈表
來源:互聯網

雙向鏈表(double linked list),又稱雙向鏈表,是一種特殊的數據結構,其中每個數據節點都包含兩個指針,分別指向直接后繼和直接前驅節點。因此,從雙向鏈表中的任意一個節點開始,都可以方便地訪問其前驅節點和后繼節點。

雙向鏈表是一種線性數據結構,由頭結點和多個包含數據域、前向指針域和后向指針域的結點組成。頭結點的數據域可以存儲線性表的長度等附加信息。雙向鏈表中,每個結點的前向指針指向其直接前驅,后向指針指向其直接后繼。雙向循環鏈表是一種特殊的雙向鏈表,其頭結點的前向指針指向最后一個結點,后向指針指向第一個結點,最后一個結點的后向指針指向頭結點。在雙鏈表中,某些操作如ListLength,GetElem和LocateElem等只需要涉及一個方向的指針,因此它們的算法描述與單鏈表相同。然而,在插入和刪除數據元素時,雙鏈表需要同時修改兩個方向上的指針,這與單鏈表有所不同。在進行雙鏈表操作之前,需要先建立雙鏈表。

操作

線性表的雙向鏈表存儲結構:

typedef?struct?DuLNode

{

ElemType?data;

struct?DuLNode?*prior,*NeXT;

}DuLNode,*DuLinkList;

帶頭結點的雙向循環鏈表的基本操作:

void?InitList(DuLinkList?L)

{?/*?產生空的雙向循環鏈表L?*/

L=(DuLinkList)malloc(sizeof(DuLNode));

if(L)

L->next=L->prior=L;

else

exit(OVERFLOW);

}

銷毀雙向循環鏈表L:

void?DestroyList(DuLinkList?L)

{

DuLinkList?q,p=L->next;?/*?p指向第一個結點?*/

while(p!=L)?/*?p沒到表頭?*/

{

q=p->NeXT;

free(p);

p=q;

}

free(L);

L=NULL;

}

重置鏈表為空表:

void?ClearList(DuLinkList?L)?/*?不改變L?*/

{? DuLinkList?q,p=L->next;?/*?p指向第一個結點?*/

while(p!=L)?/*?p沒到表頭?*/

{

q=p->next;

free(p);

p=q;

}

L->next=L->prior=L;?/*頭結點的兩個指針域均指向自身?*/

}

驗證是否為空表:

Status?ListEmpty(DuLinkList?L)

{?/*?初始條件:線性表L已存在

if(L->next==L&&L->prior==L)

return?TRUE;

else

return?FALSE;

}

元素的操作

計算表內元素個數

int?ListLength(DuLinkList?L)

{?/*?初始條件:L已存在。操作結果:?*/

int?i=0;

DuLinkList?p=L->next;?/*?p指向第一個結點?*/

while(p!=L)?/*?p沒到表頭?*/

{

i++;

p=p->NeXT;

}

return?i;

}

賦值:

Status?GetElem(DuLinkList?L,int?i,ElemType?*e)

{?/*?當第i個元素存在時,其值賦給e并返回OK,否則返回ERROR?*/

int?j=1;?/*?j為計數器?*/

DuLinkList?p=L->next;?/*?p指向第一個結點?*/

while(p!=L&&j

{

p=p->next;

j++;

}

if(p==L||j>i)?/*?第i個元素不存在?*/

return?ERROR;

*e=p->data;?/*?取第i個元素?*/

return?OK;

}

查找元素:

int?LocateElem(DuLinkList?L,ElemType?e,Status(*compare)(ElemType,ElemType))

{?/*?初始條件:L已存在,compare()是數據元素判定函數?*/

/*?操作結果:返回L中第1個與e滿足關系compare()的數據元素的位序。?*/

/*?若這樣的數據元素不存在,則返回值為0?*/

int?i=0;

DuLinkList?p=L->next;?/*?p指向第1個元素?*/

while(p!=L)

{

i++;

if(compare(p->data,e))?/*?找到這樣的數據元素*/

return?i;

p=p->NeXT;

}

return?0;

}

查找元素前驅:

Status?PriorElem(DuLinkList?L,ElemType?cur_e,ElemType?*pre_e)

{?/*?操作結果:若cur_e是L的數據元素,且不是第一個,則用pre_e返回它的前驅,?*/

/*?否則操作失敗,pre_e無定義?*/

DuLinkList?p=L->next->樂華七子NEXT;?/*?p指向第2個元素?*/

while(p!=L)?/*?p沒到表頭?*/

{

if(p->data==cur_e)

{

*pre_e=p->prior->data;

return?TRUE;

}

p=p->NeXT;

}

return?FALSE;

}

查找元素后繼:

Status?NextElem(DuLinkList?L,ElemType?cur_e,ElemType?*next_e)

{?/*?操作結果:若cur_e是L的數據元素,且不是最后一個,則用next_e返回它的后繼,?*/

/*?否則操作失敗,next_e無定義?*/

DuLinkList?p=L->next->next;?/*?p指向第2個元素?*/

while(p!=L)?/*?p沒到表頭?*/

{

if(p->prior->data==cur_e)

{

*NeXT_e=p->data;

return?TRUE;

}

p=p->next;

}

return?FALSE;

}

查找元素地址:

DuLinkList?GetElemP(DuLinkList?L,int?i)?/*?另加?*/

{?/*?在雙向鏈表L中返回第i個元素的地址。i為0,返回頭結點的地址。若第i個元素不存在,*/

/*?返回NULL?*/

int?j;

DuLinkList?p=L;?/*?p指向頭結點?*/

if(i<0||i>ListLength(L))?/*?i值不合法?*/

return?NULL;

for(j=1;j<=i;j++)

p=p->NeXT;

return?p;

}

元素的插入:

Status?ListInsert(DuLinkList?L,int?i,ElemType?e)

{?/*?在帶頭結點的雙鏈循環線性表L中第i個位置之前插入元素e,i的合法值為1≤i≤表長+1?*/

/*?改進算法2.18,否則無法在第表長+1個結點之前插入元素?*/

DuLinkList?p,s;

if(i<1||i>ListLength(L)+1)?/*?i值不合法?*/

return?ERROR;

p=GetElemP(L,i-1);?/*?在L中確定第i個元素前驅的位置指針p?*/

if(!p)?/*?p=NULL,即第i個元素的前驅不存在(設頭結點為第1個元素的前驅)?*/

return?ERROR;

s=(DuLinkList)malloc(sizeof(DuLNode));

if(!s)

return?OVERFLOW;

s->data=e;

s->prior=p;?/*?在第i-1個元素之后插入?*/

s->NeXT=p->next;

p->next->prior=s;

p->next=s;

return?OK;

}

元素的刪除:

Status?ListDelete(DuLinkList?L,int?i,ElemType?*e)

{?/*?刪除帶頭結點的雙鏈循環線性表L的第i個元素,i的合法值為1≤i≤表長?*/

DuLinkList?p;

if(i<1)?/*?i值不合法?*/

return?ERROR;

p=GetElemP(L,i);?/*?在L中確定第i個元素的位置指針p?*/

if(!p)?/*?p=NULL,即第i個元素不存在?*/

return?ERROR;

*e=p->data;

p->prior->NeXT=p->next;

p->next->prior=p->prior;

free(p);

return?OK;

}

正序查找:

void?ListTraverse(DuLinkList?L,void(*visit)(ElemType))

{?/*?由雙鏈循環線性表L的頭結點出發,正序對每個數據元素調用函數visit()?*/

DuLinkList?p=L->next;?/*?p指向頭結點?*/

while(p!=L)

{

visit(p->控制資料公司);

p=p->NeXT;

}

printf("\n");

}

void?ListTraverseBack(DuLinkList?L,void(*visit)(ElemType))

逆序查找:

模板

/*****************************************************

*文件名:LinkedList.h

*功能:實現雙向鏈表的基本功能

*注意:為了使最終程序執行得更快,僅在Debug模式下檢測操作合法性。

*另外不對內存分配失敗作處理,因為一般情況下應用程序有近2GB真正可用的空間

*********************************************************/

#pragma?once

#include?

template

class?LinkedList

{

private:

class?Node

{

public:

T?data;?//數據域,不要求泛型T的實例類有無參構造函數

Node*?prior;?//指向前一個結點

node.js*?next;?//指向下一個結點

Node(const?T&?element,Node*&?pri,Node*&?nt):data(element),NeXT(nt),prior(pri){}

Node():data(data){}//泛型T的實例類的復制構造函數將被調用.在Vc2010測試可行

};

Node*?head;?//指向第一個結點

public:

//初始化:構造一個空結點,搭建空鏈

LinkedList():head(new?Node()){head->prior=head->next=head;}

//獲取元素總數

int?elementToatal()const;

//判斷是否為空鏈

bool?isEmpty()const{return?head==head->next?true:false;}

//將元素添加至最后,注意node.js的指針設置

void?addToLast(const?T&?element){Node*?ne=new?Node(element,head->prior,head);head->prior=head->prior->next=ne;}

//獲取最后一個元素

T?getLastElement()const{assert(!isEmpty());return?head->prior->data;}

//刪除最后一個元素,注意node.js的指針設置

void?delLastElement(){assert(!isEmpty());Node*?p=head->prior->prior;delete?head->prior;head->prior=p;p->next=head;}

//修改最后一個元素

void?alterLastEmlent(const?T&?newElement){assert(!isEmpty());head->prior->data=newElement;}

//插入元素

void?insertElement(const?T&?element,int?position);

//獲取元素

T?getElement(int?index)const;

//刪除元素

T?delElement(int?index);

//修改元素

void?alterElement(const?T?&?Newelement,int?index);

//查找元素

int?findElement(const?T&?element)?const;

//正序遍歷

void?Traverse(void?(*visit)(T&element));

//逆序遍歷

void?TraverseBack(void?(*visit)(T&element));

//重載[]函數

T&?operator?[](int?index);

//清空鏈表

void?clearAllElement();

//銷毀鏈表

~LinkedList();

};

/***************************************

*返回元素總數

****************************************/

template

int?LinkedList::elementToatal()const

{

int?道達爾公司=0;

for(Node*?p=head->NeXT;p!=head;p=p->next)?++Total;

return?Total;

}

/**********************************************

*在position指定的位置插入元素。原來position及后面的元

*素后移

***********************************************/

template

void?LinkedList::insertElement(const?T&?element,int?position)

{

assert(position>0?&&?position<=elementToatal()+1);

Node*?p=head;

while(position)

{

p=p->NeXT;

position--;

}

//此時p指向要插入的結點

node.js*?pNew=new?Node(element,p->prior,p);

p->prior=p->prior->next=pNew;

}

/***************************************

*返回找到的元素的副本

***************************************/

template

T?LinkedList::getElement(int?index)const

{

assert(index>0?&&?index<=elementToatal()?&&?!isEmpty());//位置索引是否合法,鏈表是否空

Node*?p=head->NeXT;

while(--index)?p=p->next;

return?p->data;

}

/**********************************

*刪除指定元素,并返回它

**********************************/

template

T?LinkedList::delElement(int?index)

{

assert(index>0?&&?index<=elementToatal()?&&?!isEmpty());//位置索引是否合法,鏈表是否空

Node*?del=head->NeXT;

while(--index)?del=del->next;

//此時p指向要刪除元素

del->prior->next=del->next;

del->next->prior=del->prior;

T?delData=del->控制資料公司;

delete?del;

return?delData;

}

/****************************************

*用Newelement代替索引為index的元素

*****************************************/

template

void?LinkedList::alterElement(const?T?&?Newelement,int?index)

{

assert(index>0?&&?index<=elementToatal()?&&?!isEmpty());//位置索引是否合法,鏈表是否空

Node*?p=head->NeXT;

while(--index)?p=p->next;

p->控制資料公司=Newelement;

}

/********************************

*找到返回元素的索引,否則返回0

********************************/

template

int?LinkedList::findElement(const?T&?element)?const

{

Node*?p=head->NeXT;

int?i=0;

while(p!=head)

{

i++;

if(p->data==element)?return?i;

p=p->next;

}

return?0;

}

/***************************************

*正向遍歷,以鏈表中每個元素作為參數調用visit函數

*****************************************/

template

void?LinkedList::Traverse(void?(*visit)(T&element))

{

Node*?p=head->NeXT;

while(p!=head)

{

visit(p->data);//注意此時外部visit函數有權限修改LinkedList的私有數據

p=p->next;

}

}

/*************************************************

*反向遍歷,以鏈表中每個元素作為參數調用visit函數

*************************************************/

template

void?LinkedList::TraverseBack(void?(*visit)(T&element))

{

Node*?p=head->prior;

while(p!=head)

{

visit(p->data);//注意此時外部visit函數有權限修改LinkedList的私有數據

p=p->prior;

}

}

/**************************************************

*返回鏈表的元素引用,并可讀寫.實際上鏈表沒有[]意義上的所有功能

*因此[]函數是有限制的.重載它是為了客戶端代碼簡潔,因為從鏈表讀寫

*數據是最常用的

***************************************************/

template

T&?LinkedList::operator?[](int?index)

{

assert(index>0?&&?index<=elementToatal()?&&?!isEmpty());//[]函數使用前提條件

Node*?p=head->NeXT;

while(--index)?p=p->next;

return?p->data;

}

/***************************

*清空鏈表

***************************/

template

void?LinkedList::clearAllElement()

{

Node*?p=head->next,*pTemp=0;

while(p!=head)

{

pTemp=p->NeXT;

delete?p;

p=pTemp;

}

head->prior=head->next=head;//收尾工作

}

/******************************

*析構函數,若內存足夠沒必要調用該函數

*******************************/

template

LinkedList::~LinkedList()

{

if(head)//防止用戶顯式析構后,對象又剛好超出作用域再調用該函數

{

clearAllElement();

delete?head;

head=0;

}

}

循環

循環鏈表是一種鏈式存儲結構,它的最后一個結點指向頭結點,形成一個環。因此,從循環鏈表中的任何一個結點出發都能找到任何其他結點。循環鏈表的操作和單鏈表的操作基本一致,差別僅僅在于算法中的循環條件有所不同。

參考資料 >

生活家百科家居網