目錄
1.線性表的基本概念
1.1線性表的定義
線性表是一個具有相同特性數據元素的 有限 序列。
如:由n(n>=0)個數據元素(a1,a2,…,an)構成的有限的序列。
記作:L={a1,a2,…,an}
a1:表頭元素
an:表尾元素
相同特性 | 所有元素屬於同一數據類型。 |
有限 | 數據元素個數是有限的。 |
序列 | 數據元素由邏輯序號唯一確定。一個線性表中可以由相同值的元素。 |
線性表中所有元素的個數叫做線性表的長度,用n表示,n>=0。n=0時,表示線性表是一個空表,即表中不包含任何元素。
1.2線性表的運算
運算 | 描述 |
---|---|
初始化線性表InitList(&L) | 構造一個線性表L |
銷燬線性表Destroy(&L) | 釋放線性表L所佔的內存空間 |
判斷線性表是否爲空表ListEmpty(L) | 若L爲空表,返回真,否則返回假 |
求線性表的長度ListLength(L) | 返回L中的元素個數 |
輸出線性表DispList(L) | 線性表L不爲空時,順序顯示L中各結點的值域 |
求線性表L中指定位置的某個元素GetElem(L,i,&e) | 用e返回L中第i個元素的值 |
定位查找LocateElem(L,e) | 返回L中第一個值域與e相等的邏輯位序。若這樣的元素不存在,則返回0 |
插入一個數據元素ListInsert(&L,i,e) | 在L的第i個元素之前插入新的元素e,L的長度增加1 |
刪除數據元素ListDelete(&L,i,&e) | 刪除L的第i個元素,用e返回其值,L的長度減1 |
不同的存儲結構的實現代碼不一樣,這裏先不給出實現代碼。
之後在具體的存儲結構中給出具體代碼。
1.3線性表的知識結構
2.線性表的順序存儲結構
2.1順序表
把線性表中的所有元素按照順序存儲結構方法進行存儲。
按邏輯順粗依次存儲到一片連續的存儲空間中。
順序表類型定義:
typedef char ElemType;//假設ElemType爲char
typedef struct
{
ElemType data[MaxSize];
int length;
} SqList;//順序表類型
2.2順序表的運算實現
1.建立順序表
a[0…n-1]>>>順序表L——整體創建順序表
void CreateList(SqList L,ElemType a[],int n)
{
int i;
L=(SqList*)malloc(sizeof(SqList));
for(i=0,i<n,i++)
L->data[i]=a[i];
L->Length=n;
}
參數說明:
SqList *L;通過順序表指針L操作順序表。
SqList *&L;引用符號&放在形參L前;輸出型參數均用&,不論其值是否改變。
2.初始化線性表
void InitList(Sqlist*&L)
{
L=(SqList*)malloc(sizeof(SqLit));
L-Length=0;
}
3.銷燬線性表
void DestroyList(Sqlist *&L)
{
free(L);
}
4.判斷是否爲空表
bool ListEmpty(SqList*L)
{
return(L->length==0);//length爲0,返回真,不爲0,返回假
}
5.求線性表的長度
int ListLength(SqList*L)
{
return(L->length);
}
6.求某個數據元素的值
bool GetElem(SpList*L,int i,ElemType &e)
{
if(i<1||i>L->length)
return false;
e=L->data[i-1];
return true;
}
7.按元素查找
int LocateElem(SpList*L,ElemType e)
{
int i=0;
while(i<L->length&&L->data[i]!=e)
i++;
if(i>L->length)
return 0;
else
return i+1;
}
8.插入數據元素
bool ListInsert(SqList*&L,int i,ElemType e)
{
int i,j;
if(i<1||i>L->length+1)//插入位置有誤
return false;
i--;//將邏輯序號轉化爲物理序號,如第一個元素對應data[0]
for(j=L->length;j>i;j--)
L->data[j]=L->data[j-1];//要插入元素,先把元素往後移,空出一個位置用於插入
L->data[i]=e; //插入元素
L->length++;//長度加一
return true;//插入成功
}
//平均時間複雜度爲 O(n);
9.刪除第i個元素
bool ListDelete(SqList*&L,int i,ElemType &e)
{
int j;
if(i<1||i>L->length)//刪除位置有誤
return false;
i--;// 將邏輯序號轉化爲物理序號
e=L->data[i];//記錄刪除的元素
for(j=i;j<L->length;j++)
L->data[j]=L->data[j+1]; //元素前移,補齊空位
L->length--;//長度減一
return true;//刪除成功
}
//平均時間複雜度爲 O(n);
3.線性表的鏈式存儲結構
3.1鏈表
線性表中每個結點有唯一的前趨結點。
設計鏈式存儲結構時,每個邏輯結點單獨存儲,爲了表示邏輯關係,增加指針域。
每個物理結點增加一個指向後繼結點的指針域——>單鏈表
每個物理結點增加一個指向後繼結點的指針域和一個指向前趨結點的指針域——>雙鏈表
爲什麼增加一個頭結點?
1.使得第一個結點的操作與其他結點的操作相一致,否則要操作第一結點時要單獨討論。‘
2.無論鏈表是否爲空,都有一個頭結點,因此空表和非空表的處理都同一了。假如沒有頭結點,當爲空表時又需要單獨討論。
存儲密度:
指結點數據本身所佔的存儲量和整個結點結構所佔的存儲量之比
一般地,存儲密度越大,存儲空間利用率就越高。
順序表的存儲密度爲1(100%),而鏈表的存儲密度小於1。
3.2單鏈表
單鏈表的結點類型LinkList的定義如下
typedef char ElemType ;//假設ElemType爲char型
typedef struct LNode
{
ElemType data;
struct LNode *next;//指向後一個結點
}LinkList;
1.插入和刪除結點操作
(1)插入結點
特點:只需修改相關結點指針域,不需要移動過結點,節省了時間。
(2)刪除結點
特點:只需修改相關結點的指針於,不用像順序表一樣移動結點,節省時間。
頭插法建表
1.從一個空表開始,創建一個頭結點
2.讀取元素,生成新的結點
3.把新的結點插入到表頭上
代碼
void CreateListF(LinkList *&L,ElemType a[],int n)
{
LinkList *s;
int i;
L=(LinkList*)malloc(sizeof(LinkList));//創建頭結點
L->next=NULL;//頭結點的指針域指向空
for(i=0;i<n;i++)
{
s=(LinkList*)malloc(sizeof(LinkList));//生成新的結點
s->data=a[i];//從數組中取得元素
s->next= L->next;//新的結點指向首元結點
L->next=s;//頭結點指向新的結點,該結點變成首元結點
}
}
尾插法建表
1.從一個空表開始,創建一個頭結點
2.讀取字符數組的元素,生成新的結點
3.把新的結點插到表尾
void CreateListR(LinkList *&L,ElemType a[],int n)
{
LinkList *s,*r;
int i;
L=(LinkList*)malloc(sizeof(LinkList));//創建頭結點
r=L;//r指向尾結點,開始時指向頭結點,此時頭結點就是尾結點
for(i=0;i<n;i++)
{
s=(LinkList*)malloc(sizeof(LinkList));
s->data=a[i];
r->next=s;//把s插入尾部
r=s;//把r移到尾部
}
r->next=NULL;//插入所有元素之後,把尾部指向空,創建完成
}
3.3單鏈表的基本算法實現
//初始化單鏈表
void InitList(LinkList *&L)
{
L=(LinkList*)malloc(sizeof(LinkList));//創建頭結點
L->next=NULL;//指向空
}
//銷燬單鏈表
void DestroyList(LinkList *&L)
{
LinkList *pre=L,*p=L->next;//pre爲p的前趨
while(p!=NULL)
{
free(pre);//釋放pre
pre=p;//pre後移
p=pre->next;//然後把p也後移,把結點一個一個釋放掉
}
}
//判斷是否爲空表
bool ListEmpty(LinkList *L)
{
return (L->next==NULL);//如果頭結點的下一個爲空,就爲空表,否則不爲空
}
//求單鏈表的長度
int ListLength(LinkList *L)
{
int n=0;
LinkList *p=L;//不能移動頭結點的指針,不然單鏈表會被破壞,所以把L賦給p
while(p->next!=NULL){
n++;//計數
p=p->next;//後移
}
return (n);
}
//遍歷輸出
void DisList(LinkList *L)
{
LinkList *p=L->next;
while(p!=NULL){
printf("%c ",p->data);
p=p->next;
}
printf("\n");
}
//求第i個位置的元素
bool GetElem(LinkList *L,int i, ElemType &e)
{
int j=0;
LinkList *p=L;
while(j<i&&p!=NULL)
{
j++;//計數
p=p->next;//找到第i個結點p
}
if(p==NULL) return false;//該節點不存在
else{
e=p->data;
return true;
}
}
//查找某元素的位置
int LocteElem(LinkList *L,ElemType e)
{
int i=1;
LinkList *p=L->next;
while(p!=NULL)
{
p=p->next;
i++;
}
if(p==NULL) return (0);//該結點不存在
else return (i);//返回邏輯序號
}
//插入數據元素
bool ListInsert(LinkList *&L,int i,ElemType e)
{
int j=0;
LinkList *p=L,*s;
while(j<i-1&&p!=NULL)
{
j++;
p=p->next;//找到第i-1個結點
}
if (p==NULL) return false;//找不到
else{
s=(LinkList*)malloc(sizeof(LinkList));//創建新結點
s->data=e;//給data賦值
s->next=p->next;//開始插入
p->next=s;//把s插入到了p之後
return true;
}
}
//刪除數據元素
bool ListDelete(LinkList *&L,int i,ElemType &e)
{
int j=0;
LinkList *p=L,*q;
while(j<i-1)
{
j++;
p=p->next;//找到第i-1個結點
}
if (p==NULL) return false;
else{
q=p->next;
if(q==NULL) return false;//p已經是最後一個元素了
e=q->data;
p->next=q->next;
free(q);
return true;
}
}
3.4雙鏈表
在線性表的鏈式存儲結構中,每個物理結點增加一個指向後繼結點的指針域和一個指向前趨結點的指針域——>雙鏈表
雙鏈表有什麼優點
1.從任一結點出發可以快速找到前趨和後繼結點
2.從任一結點出發可以訪問到其他結點
結點類型定義:
typedef char ElemType;
typedef struct DNode
{
ElemType data;
struct DNode *prior;//指向前趨 結點
struct Dnode *next;//指向後繼結點
}DLinkList;
插入與刪除
基本操作算法實現
void InitList(DLinkList *&L) //初始化
{
L=(DLinkList *)malloc(sizeof(DLinkList)); //創建頭結點
L->prior=L->next=NULL;
L->freq=0;
}
void DestroyList(DLinkList *&L) //銷燬線性表
{
DLinkList *p=L,*q=p->next;
while (q!=NULL)
{
free(p);
p=q;
q=p->next;
}
free(p);
}
bool ListEmpty(DLinkList *L) //判線性表是否爲空表
{
return(L->next==NULL);
}
int ListLength(DLinkList *L) //求線性表的長度
{
DLinkList *p=L;int i=0;
while (p->next!=NULL)
{
i++;
p=p->next;
}
return(i);
}
void DispList(DLinkList *L) //輸出線性表
{
DLinkList *p=L->next;
while (p!=NULL)
{
printf("%c ",p->data);
p=p->next;
}
printf("\n");
}
bool GetElem(DLinkList *L,int i,ElemType &e) //求線性表中某個數據元素值
{
int j=0;
DLinkList *p=L;
while (j<i && p!=NULL)
{
j++;
p=p->next;
}
if (p==NULL)
return false;
else
{
e=p->data;
return true;
}
}
int LocateElem(DLinkList *L,ElemType e) //按元素值查找
{
int n=1;
DLinkList *p=L->next;
while (p!=NULL && p->data!=e)
{
n++;
p=p->next;
}
if (p==NULL)
return(0);
else
return(n);
}
bool ListInsert(DLinkList *&L,int i,ElemType e) //插入數據元素
{
int j=0;
DLinkList *p=L,*s;
while (j<i-1 && p!=NULL)
{
j++;
p=p->next;
}
if (p==NULL) //未找到第i-1個結點
return false;
else //找到第i-1個結點*p
{
s=(DLinkList *)malloc(sizeof(DLinkList)); //創建新結點*s
s->data=e;
s->freq=0;
s->next=p->next; //將*s插入到*p之後
if (p->next!=NULL) p->next->prior=s;
s->prior=p;
p->next=s;
return true;
}
}
bool ListDelete(DLinkList *&L,int i,ElemType &e) //刪除數據元素
{
int j=0;
DLinkList *p=L,*q;
while (j<i-1 && p!=NULL)
{
j++;
p=p->next;
}
if (p==NULL) //未找到第i-1個結點
return false;
else //找到第i-1個結點*p
{
q=p->next; //q指向要刪除的結點
if (q==NULL) return false; //不存在第i個結點
e=q->data;
p->next=q->next; //從單鏈表中刪除*q結點
if (p->next!=NULL) p->next->prior=p;
free(q); //釋放*q結點
return true;
}
}
void Sort(DLinkList *&head) //雙鏈表元素排序
{
DLinkList *p=head->next,*q,*r;
if (p!=NULL) //若原雙鏈表中有一個或以上的數據結點
{
r=p->next; //r保存*p結點後繼結點的指針
p->next=NULL; //構造只含一個數據結點的有序表
p=r;
while (p!=NULL)
{
r=p->next; //r保存*p結點後繼結點的指針
q=head;
while (q->next!=NULL && q->next->data<p->data) //在有序表中找插入*p的前驅結點*q
q=q->next;
p->next=q->next; //將*p插入到*q之後
if (q->next!=NULL) q->next->prior=p;
q->next=p;
p->prior=q;
p=r;
}
}
}
3.5循環鏈表
循環單鏈表:將表中尾結點的指針與改成指向表頭結點,整個鏈表形成一個環。由此從表中任一結點出發,均可找到鏈表中其他結點。
循環雙鏈表:形成兩個環。