要用好鏈表的首要前提就是要理解什麼是鏈表。
鏈表是用任意存儲單元來存放數據的,這個存儲單元可以使連續的,也可以是不連續的。爲了正確的表示元素間的邏輯關係,在存儲每個元素值得同時還必須存儲指示其後繼或是前驅元素的地址信息。這兩部分信息組成數據元素的存儲映像即結點。包含兩個域,數據域和指針域。故而可以用結構體來表示鏈表中的結點,並且每個新結點使用前都要動態分配存儲空間。
如果想要了解清楚p->next,p=r;p=p->next;p->next=r->next;等的具體含義,可以在程序語言中設置斷點並調試,在調試變量窗口中查看它們的變化與不同,從而深入的理解鏈表的基本用法。其他的就是思維上的問題了,比如在某個節點p前插入元素,形象的說就是在這個鏈表該元素的位置切開,將新元素放進去,再連接好這條鏈表的斷裂處(將p的link域地址改爲新節點地址,將新節點link域改爲p的下一節點地址,這樣就連接好了斷裂處,這是對單鏈表而言的,雙鏈表情況是類似的)。
下面就先看看單鏈表及其構建、元素查找、插入、刪除等操作的函數是怎麼實現的吧。代碼後的運行結果展示可以很清楚的看到各個操作之後新鏈表的情況。
#include "iostream"
using namespace std;
//單鏈表用法
typedef struct node
{
int data;
struct node *next;
}linklist;
linklist* creat(int n)//尾插法建立單鏈表,即頭結點非空,爲第一個節點
{
linklist*head,*New,*tail;
head=NULL;
tail=NULL;
for (int i=1;i<=n;i++)
{
New=(linklist*)malloc(sizeof(linklist));
New->data=i;
if(head==NULL) head=New;
else tail->next=New;
tail=New;
}
if(tail!=NULL) tail->next=NULL;
return head;
}
void print(linklist*head)//輸出鏈表元素值
{
while (head)
{
printf("%d ",head->data);
head=head->next;
}
}
linklist*Get(linklist*head,int i) //按序號查找鏈表的值
{
int j=1;
linklist*p=head;
while (p!=NULL&&j<i)
{
p=p->next;
j++;
}
if(i==j) return p;
else return NULL;
}
linklist*Locate(linklist*head,int key) //按值查找單鏈表數據
{
linklist*p=head; //要從第一個節點開始查找,因爲head爲第一個節點(非空)
while (p!=NULL&&p->data!=key)
p=p->next;
if(p==NULL) return NULL;
else return p;
}
void InsertAfter(linklist*p,int x)//將值爲x的新節點插入p節點後
{
linklist*s;
s=(linklist*)malloc(sizeof(linklist));
s->data=x;
s->next=p->next; //將數據插入最後一個元素之後會出問題嗎??
//答:不會!
p->next=s;
}
void InsertBefore(linklist*head,linklist*p,int x)//將值爲x的新節點插入p節點前
{
linklist*s,*q;
s=(linklist*)malloc(sizeof(linklist));
s->data=x;
q=head;
//不能在第一個節點前插入數據嗎??
//答:不能,因爲第一個節點在此爲頭結點,而頭結點沒有前驅節點
//如果將q->next改爲q,那麼當在頭結點插入元素x時就形成了無限的循環鏈表,節點數爲2:head和s
while(q->next!=p)
q=q->next;
s->next=p;
q->next=s;
}
void Delete(linklist*head,linklist*p)//刪除鏈表元素
{
linklist*q;
q=head;
while (q->next!=p)//能刪除最後一個,但是不能刪除第一個,理由和InsertBefore類似
q=q->next;
q->next=p->next;
free(p);
}
int main()
{
linklist*p=(linklist*)malloc(sizeof(linklist));
linklist*q=(linklist*)malloc(sizeof(linklist));
linklist*r=(linklist*)malloc(sizeof(linklist));
int n;
cout<<"輸入創建單鏈表的節點個數n:";
cin>>n;
cout<<"建立的新鏈表如下所示:"<<endl;
p=creat(n);
print(p);
cout<<endl;
cout<<"輸入按序號查找法想要查找的元素,輸入序號小於等於"<<n<<endl;
int m;
cin>>m;
q=Get(p,m);
printf("查找成功:元素爲%d\n",q->data);
r=q;
cout<<"輸入在鏈表上述查找元素前插入的數據"<<endl;
int fn;
cin>>fn;
InsertBefore(p,r,fn);
cout<<"插入數據後的新鏈表爲:"<<endl;
print(p);
cout<<endl;
cout<<"在鏈表上述查找元素之後插入的數據"<<endl;
int an;
cin>>an;
InsertAfter(r,an);
cout<<"插入後新鏈表爲:"<<endl;
print(p);
cout<<endl;
cout<<"刪除上述查找的元素"<<m<<endl;
cout<<endl;
Delete(p,r);
cout<<"刪除後鏈表爲"<<endl;
print(p);
cout<<endl;
return 0;
}
結合上述運行結果圖、提示,在對照代碼看一遍,基本上單鏈表的基本用法就掌握了。
雙鏈表在Get、Locate等只涉及一個方向的指針時,是基本一樣的。而在刪除與插入節點時卻大不相同,下面只簡略介紹雙鏈表的不同之處,以代碼的形式展現,可以對比單鏈表的相關操作進行學習。
void DeleteNode(linklist*p)
{
p->pre->next=p->next;
p->next->pre=p->pre;
free(p);
}
void InsertBefore(linklist*p,int x)
{
linklist*s;
s=(linklist*)malloc(sizeof(linklist));
s->data=x;
s->pre=p->pre;
s->next=p;
p->pre->next=s;
p->pre=s;
}
以上便是單雙鏈表的基本用法與實現,實際運用到底使用哪個,主要考慮兩方面(存儲空間、運算時間),有的應用不需要查找前驅,則用單鏈表,需要頻繁前後查找的就用雙鏈表。