一、單鏈表
1、結點:爲了表示每個數據元素ai與其直接後繼元素ai+1之間的邏輯關係,對數據元素ai來說,除了存儲其本身的信息之外,還需要存儲一個指示其直接後繼的信息(直接後繼的存儲位置)。我們把存儲數據元素信息的域稱爲數據域,把存儲直接後繼位置的域稱爲指針域。指針域中存儲的信息稱做指針或鏈。這兩部分信息組成數據元素ai的存儲映像,稱爲結點(Node)。
2、單鏈表:n個結點(ai的存儲映像)鏈結成一個鏈表,即爲線性表(a1,a2,...,an)的鏈式存儲結構,因爲此鏈表的每個結點中只包含一個指針域,所以叫做單鏈表。
3、頭指針:我們把鏈表中第一個結點的存儲位置叫做頭指針。(單鏈表L:L既是單鏈表的名字,也是其頭指針。)
4、規定線性鏈表的最後一個結點指針爲"空"(通常用NULL表示) 。
5、頭結點:有時爲了更加方便的對鏈表進行操作,會在單鏈表的第一個結點前設置一個結點,稱爲頭結點。頭結點的數據域可以不存儲任何信息,其指針域存儲指向第一個結點的指針。
6、空表:線性表爲空,則頭結點的指針域爲空,表示如下
或
我們大概大概知道了用圖示表達了內存中單鏈表的存儲狀態。但我們關心的是它在內存中的位置嗎?不是的,我們關心的是它所表示的線性表中的數據元素及數據元素之間的邏輯關係。所以我們改用更方便的存儲示意圖表示單鏈表,如下
若帶有頭結點的單鏈表,則如圖所示
二、 單鏈表存儲結構
typedef int ElemType;//根據實際情況而定,這裏假設爲int
/*線性表的單鏈表的存儲結構*/
typedef struct Node
{
ElemType data;
struct Node *next;
}Node;
typedef struct Node *LinkList;//定義LinkList
三、單鏈表的基本操作
1、創建鏈表
int CreateListHead(LinkList *L,int n)//注意L爲二級指針
{
LinkList p;
int i;
/*先建立一個帶頭結點的單鏈表*/
*L = (LinkList)malloc(sizeof(Node));
if(!(*L))
return false;
(*L)->next = NULL;
srand(time(0));//初始化隨機種子
for(i=0;i<n;i++)
{
p = (LinkList)malloc(sizeof(Node));//生成新的結點
p->data = rand()%100+1;//隨機生成100以內的數字
p->next = (*L)->next;
(*L)->next = p;//插入到表頭,即插入頭結點後其它結點之前
}
return true;
}
int CreateListTail(LinkList *L,int n)//注意L爲二級指針
{
LinkList p,rear;
int i;
/*先建立一個帶頭結點的單鏈表*/
*L = (LinkList)malloc(sizeof(Node));
if(!(*L))
return false;
(*L)->next = NULL;
rear = *L;/*rear爲指向尾部結點的指針*/
srand(time(0));//初始化隨機種子
for(i=0;i<n;i++)
{
p = (LinkList)malloc(sizeof(Node));//生成新的結點
p->data = rand()%100+1;//隨機生成100以內的數字
rear->next = p;//將表尾終端結點的指針的後繼指針指向新生成的結點
rear = p;//將當前的新結點定義爲表尾終端結點
}
rear->next = NULL;//當前鏈表結束
return true;
}
2、鏈表是否爲空
int ListEmpty(LinkList L)
{
if(!L)
{
printf("帶頭結點的鏈表不存在\n");
return false;
}
if(L->next)//非空鏈表
return true;
else
return false;
}
3、鏈表的長度
int ListLenght(LinkList L)
{
if(!L)
return false;
LinkList p;
int i = 0;
p = L->next;
while(p)
{
i++;
p = p->next;
}
return i;
}
4、獲取第i個位置的元素
int GetElem(LinkList L,int i,ElemType *e)
{
if(!L)
return false;
LinkList p;
int j = 1;
p = L;
while(p && j<i)
{
j++;
p = p->next;
}
if(!(p->next))
return false;
*e = p->next->data;
return true;
}
5、查找元素,並返回其位置
/*在線性表L中查找與給定的e相等的元素,如果查找成功,返回該元素在表中序號表示成功,否則返回0表示失敗*/
int LocateElem(LinkList L,ElemType e)
{
if(!L)
return false;
LinkList p;
p = L;
int i = 0;
while(p)
{
++i;
if(p->next)
if(e == p->next->data)
return i;
p = p->next;
}
return false;
}
6、鏈表的插入
/*初始條件:順序線性表L已存在,1<=i<=ListLebgth(L)
操作結果:在L中第i個結點位置之前插入新的數據元素e,L的長度加1
*/
int ListInser(LinkList *L,int i,ElemType e)
{
if(!(*L) || (i<1))
return false;
LinkList p;
int j = 1;
p = *L;
while(p && j<i)//尋找第i-1個結點
{
j++;
p = p->next;
}
if(!p)
return false;
LinkList s = (LinkList)malloc(sizeof(Node));
s->data = e;
s->next = p->next;
p->next = s;
return true;
}
7、鏈表的刪除
int ListDelete(LinkList *L,int i,ElemType *e)
{
if(!(*L) || (i<1))
return false;
LinkList p,q;
int j = 1;
p = *L;
while(p->next && j<i)//遍歷尋找第i-1個結點
{
j++;
p = p->next;
}
if(!(p->next))//第i個結點不存在
return false;
q = p->next;
p->next = q->next;
*e = q->data;
free(q);
return true;
}
8、清空鏈表
/*初始條件:帶頭結點的單鏈表L已存在,操作結果:將L重置爲空表*/
int ClearList(LinkList *L)
{
if(!(*L))
return false;
LinkList p,q;
p = (*L)->next;//p指向第一個結點
(*L)->next = NULL;//頭結點指針域爲空
while(p)
{
q = p;
p = p->next;
free(q);
}
return true;
}
9、打印鏈表
int printfList(LinkList L)
{
if(!L)
return false;
LinkList p;
p=L->next;
if(!p)
{
printf("鏈表爲空\n");
return false;
}
while(p)
{
printf("p->data:%d\n",p->data);
p=p->next;
}
printf("\n");
return true;
}
四、測試代碼及測試結果
#include <stdio.h>
#include <malloc.h>
#include <time.h>
#include <Windows.h>
/*聲明:鏈表是帶頭結點的線性表*/
void main(void)
{
LinkList L;
CreateListTail(&L,10);
ListEmpty(L);
int len = ListLenght(L);
printf("===========len=%d===========\n",len);
printfList(L);
int pos = LocateElem(L,8888);
printf("查找數據8888是否成功:pos=%d\n",pos);
printf("插入數據8888\n");
ListInser(&L,4,8888);
pos = LocateElem(L,8888);
printf("查找數據8888是否成功:pos=%d\n",pos);
printfList(L);
ElemType e;
GetElem(L,4,&e);
printf("線性表的第四個元素值:%d\n",e);
printf("刪除數據8888\n");
ListDelete(&L,4,&e);
printfList(L);
printf("清空鏈表\n");
ClearList(&L);
printfList(L);
system("pause");
}
五、拓展 (應用)
1、單鏈表的合併
/*帶頭結點的單鏈表合併操作
說明:
1.已知La和Lb爲升序線性鏈表
2.要求合併La和Lb爲Lc且Lc爲升序*/
int LinkedListMergeLaLb(LinkList La, LinkList Lb, LinkList *Lc)
{
LinkList pa,pb,pc;//pc爲指向Lc的尾指針
pa=La->next;
pb=Lb->next;
*Lc=La; //借用表La的頭結點作爲表Lc的頭結點
pc=*Lc;
while((pa!=NULL)&&(pb!=NULL))
{
if(pa->data<=pb->data)
{
pc->next=pa;
pc=pa;
pa=pa->next;
}
else
{
pc->next=pb;
pc=pb;
pb=pb->next;
}
}
if(pa!=NULL)
{
while(pb)
{
pc->next=pa;
pc = pa;
pa = pa->next;
}
}
else
{
while(pb)
{
pc->next=pb;
pc = pb;
pb = pb->next;
}
}
free(pb); //將Lb的表頭結點釋放
return true;
}