線性表(List):零個(gè)或多個(gè)數(shù)據(jù)元素的有限序列
序列: 元素之間是有順序的,第一個(gè)元素?zé)o前驅(qū)最后一個(gè)元素?zé)o后繼,其他元素都有一個(gè)前驅(qū)和一個(gè)后繼
有限: 元素個(gè)數(shù)是有限的
在較復(fù)雜的線性表中,一個(gè)數(shù)據(jù)元素可以由若干個(gè)數(shù)據(jù)項(xiàng)組成,每個(gè)數(shù)據(jù)元素具有相同類型的數(shù)據(jù)項(xiàng)。
線性表的基本操作
- 線性表的創(chuàng)建和初始化
- 線性表重為空表
- 根據(jù)位序得到數(shù)據(jù)元素
- 查找某個(gè)元素是否存在
- 獲取線性表長(zhǎng)度
- 插入和刪除數(shù)據(jù)
ADT 線性表(List)
Data
線性表的數(shù)據(jù)對(duì)象集合為{a1,a2,..........an},每個(gè)元素的類型均為DataType。其中,除第一個(gè)元素a1外,每個(gè)元素有且只有一個(gè)直接前驅(qū)元素,除最后一個(gè)元素an外,每個(gè)元素有且只有一個(gè)直接后繼元素。數(shù)據(jù)元素之間的關(guān)系是一對(duì)一的關(guān)系。
Operation
InitList(*L): 初始化操作,建立一個(gè)空的線性表L。
ListEmpty(L): 若線性表為空,返回true,否則返回false。
ClearList(*L): 將線性表清空
GetElem(L,i,*e): 將線性表L中的第i個(gè)位置元素返回給e
LocateElem(L,e):在線性表L中查找與給定值e相等的元素,如果查找成功,返回該元素在表中序號(hào)表示成功;否則,返回0表示失敗
ListInsert(*L,i,e): 在線性表L中的第i個(gè)位置插入新元素e
ListDelete(*L,i,*e): 刪除線性表L中第i個(gè)位置元素,并用e返回其值
ListLength(L): 返回線性表L的元素個(gè)數(shù)
endADT
-
線性表預(yù)定義常量和類型
數(shù)據(jù)結(jié)構(gòu)的表示(存儲(chǔ)結(jié)構(gòu))用類型定義(typedef)描述。數(shù)據(jù)元素類型約定為ElemType,由用戶在使用該數(shù)據(jù)類型時(shí)自行定義。
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 20 /* 存儲(chǔ)空間初始化分配量 */
typedef int Status; /* Status是函數(shù)的類型,其值是函數(shù)結(jié)果狀態(tài)代碼,如OK等 */
typedef int ElemType; /* ElemType類型根據(jù)實(shí)際情況而定,這里假設(shè)為int */
-
線性表數(shù)據(jù)結(jié)構(gòu)類型定義
typedef struct
{
ElemType data[MAXSIZE]; /* 數(shù)組,存儲(chǔ)數(shù)據(jù)元素 */
int length; /* 線性表當(dāng)前長(zhǎng)度 */
}SqList;
-
順序線性表初始化
Status InitList(SqList *L)
{
L->length=0;
return OK;
}
i=InitList(&L);
printf("初始化L后: L.length=%d\n",L.length);
初始化L的長(zhǎng)度為0,接下來在表頭插入五個(gè)元素
-
插入元素到順序線性表中
Status ListInsert(SqList *L, int i, ElemType e)
{
int k;
if (L->length==MAXSIZE) /* 順序線性表已經(jīng)滿 */
return ERROR;
if (i<1 || i>L->length+1)/* 當(dāng)i比第一位置小或者比最后一位置還要大時(shí) */
return ERROR;
if (i<=L->length) /* 若插入數(shù)據(jù)位置不在表尾 */
{
for(k=L->length;k>=i;k--) /* 將要插入位置之后的數(shù)據(jù)元素向后移動(dòng)一位,給欲插入元素騰出位置 */
L->data[k]=L->data[k-1];
}
L->data[i-1]=e; /* 將新元素插入 */
L->length++;
return OK;
}
//測(cè)試數(shù)據(jù)
for(j=1;j<=5;j++)
i=ListInsert(&L,1,j);
printf("在L的表頭依次插入1~5后:L.data=");
ListTraverse(L); //輸出元素

可能會(huì)好奇插入元素后輸出為什么是倒敘的,因?yàn)槲覝y(cè)試的時(shí)候子函數(shù)ListInsert中的參數(shù)i設(shè)置為1,就是每次插入數(shù)據(jù)都是從data[0]插入,每進(jìn)來一個(gè)數(shù)據(jù)前一個(gè)數(shù)據(jù)都要向后移一位。
若設(shè)置子函數(shù)ListInsert中的參數(shù)i設(shè)置為j的輸出情況如下:
for(j=1;j<=10;j++)
ListInsert(&L,j,j);
printf("在L的表尾依次插入1~10后: L.data=");
ListTraverse(L);

這樣每次插入的數(shù)據(jù)就是data[j-1], 就是在尾部插入。
-
判斷順序線性表是否為空
Status ListEmpty(SqList L)
{
if(L.length==0)
return TRUE;
else
return FALSE;
}
-
將順序線性表清空
Status ClearList(SqList *L)
{
L->length=0;
return OK;
}
-
查找順序線性表中元素
Status GetElem(SqList L,int i,ElemType *e)
{
if(L.length==0 || i<1 || i>L.length)
return ERROR;
*e=L.data[i-1];//第i個(gè)位置的元素在數(shù)組的i-1位置
return OK;
}
-
查找某個(gè)元素是否存在順序線性表中
int LocateElem(SqList L,ElemType e)
{
int i;
if (L.length==0)
return 0;
for(i=0;i<L.length;i++)
{
if (L.data[i]==e)
break; // 跳出for
}
if(i>=L.length)
return 0;
return i+1;//元素的位序要加一
}
-
從順序線性表中刪除元素
/* 初始條件:順序線性表L已存在 1<=i<=ListLength(L) */
/* 操作結(jié)果: 刪除L的第i個(gè)數(shù)據(jù)元素,并用e返回其值,L的長(zhǎng)度減一 */
Status ListDelete(SqList *L, int i, ElemType *e)
{
int k;
if (L->length==0) /* 線性表為空 */
return ERROR;
if (i<1 || i>L->length) /* 刪除位置不正確 */
return ERROR;
*e=L->data[i-1];
if (i<L->length) /* 如果刪除不是最后位置 */
{
for(k=i-1;k<L->length-1;k++)/* 將刪除位置后繼元素前移*/
L->data[k]=L->data[k+1];
}
L->length--;
return OK;
}
-
順序輸出元素
Status ListTraverse(SqList L)
{
int i;
for(i=0;i<L.length;i++)
visit(L.data[i]);//輸出函數(shù)
printf("\n");
return OK;
}
-
在La中合并Lb中的元素
現(xiàn)實(shí)生活中涉及關(guān)于線性表更復(fù)雜操作,可以用基本操作的組合來實(shí)現(xiàn)。比如:實(shí)現(xiàn)兩個(gè)線性表集合A和B的并集操作,就是把存在集合B中但并不存在A中的數(shù)據(jù)元素插入到A中
仔細(xì)分析這個(gè)操作會(huì)發(fā)現(xiàn)只要循環(huán)集合B中的每個(gè)元素,判斷當(dāng)前元素是否存在A中,若不存在,則插入到A中即可。即從線性表Lb中依次取得每個(gè)數(shù)據(jù)元素,并依值在線性表La中進(jìn)行查訪,若不存在,則插入之。
實(shí)現(xiàn)代碼如下:
// 在La中合并Lb中的元素
void unionL(SqList *La,SqList Lb)
{
int La_len,Lb_len,i;
ElemType e;
La_len=ListLength(*La);
Lb_len=ListLength(Lb);
for (i=1;i<=Lb_len;i++)
{
GetElem(Lb,i,&e); // 依次返回Lb線性表中的值 依次取出Lb中的元素
if (!LocateElem(*La,e)) //與La中的對(duì)所有元素做對(duì)比
ListInsert(La,++La_len,e); //若不存在La中,則插入La中
}
}
-
把La和Lb線性表中的元素歸并為一個(gè)新的線性表Lc
因?yàn)長(zhǎng)c中的值需要改變,所以只能傳地址進(jìn)來。在此函數(shù)中Lc是指針變量,所以進(jìn)入ListInsert()時(shí)直接是ListInsert(Lc,++k,ai)。在調(diào)用GetElem()時(shí)變量e是指針類型,是因?yàn)樽兞縠在子函數(shù)中做出的改變是永久的,并不會(huì)隨著子函數(shù)運(yùn)行完畢改變就會(huì)消失。因?yàn)樽雍瘮?shù)中的變量都是臨時(shí)變量隨著子函數(shù)的結(jié)束而結(jié)束,所以出現(xiàn)指針變量可以在子函數(shù)中做出改變使其不隨子函數(shù)的結(jié)束而結(jié)束。
// 將兩個(gè)非遞減有序線性表La和Lb歸并為一個(gè)新的線性表Lc
void MergeList(SqList La,SqList Lb,SqList *Lc)
{
//InitList(Lc);
int i,j,La_len,Lb_len,ai,bj;
int k = 0;
i = j = 1;
La_len = ListLength(La);
Lb_len = ListLength(Lb);
while((i<=La_len) && (j<= Lb_len))
{ //
GetElem(La,i,&ai);
GetElem(Lb,j,&bj);
if(ai<=bj)
{
ListInsert(Lc,++k,ai);
++i;
}else{
ListInsert(Lc,++k,bj);
++j;
}
}
while(i<=La_len)
{
GetElem(La,i++,&ai);
ListInsert(Lc,++k,ai);
}
while(j<=Lb_len)
{
GetElem(Lb,j++,&bj);
ListInsert(Lc,++k,bj);
}
}
線性表的順序存儲(chǔ)結(jié)構(gòu)
線性表的順序存儲(chǔ)結(jié)構(gòu)指的是用一段地址連續(xù)的存儲(chǔ)單元依次存儲(chǔ)線性表的數(shù)據(jù)元素
數(shù)組長(zhǎng)度和線性表長(zhǎng)度的區(qū)別
數(shù)組長(zhǎng)度是存放線性表的存儲(chǔ)空間的長(zhǎng)度,存儲(chǔ)分配后這個(gè)量一般是不變的。
線性表長(zhǎng)度是線性表中數(shù)據(jù)元素的個(gè)數(shù),隨著線性表插入和刪除操作的進(jìn)行,這個(gè)量是變化的。
在任意時(shí)刻,線性表的長(zhǎng)度應(yīng)該小于等于數(shù)組的長(zhǎng)度。
地址計(jì)算方法

順序存儲(chǔ)的存取時(shí)間性能是O(1),屬于隨機(jī)存儲(chǔ)結(jié)構(gòu)。
線性表順序存儲(chǔ)結(jié)構(gòu)的優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
- 無須為表示表中元素之間的邏輯關(guān)系而增加額外的存儲(chǔ)空間
- 可以快速地存取表中任一位置的元素
缺點(diǎn) - 插入和刪除操作需要移動(dòng)大量元素
- 當(dāng)線性表長(zhǎng)度變化較大時(shí),難以確定存儲(chǔ)空間的容量
- 造成存儲(chǔ)空間的“碎片”
題外話
最近看鏈表遇到了指針,這可是我一直的痛點(diǎn)。又翻出來看了看,發(fā)現(xiàn)自己的問題確實(shí)還挺嚴(yán)重。
int *b = &a;
在上面這行代碼上糾結(jié)了很久,最后發(fā)現(xiàn)其實(shí)應(yīng)該這么理解。
int* b;
b = &a;
也就是說b是一個(gè)指針變量,b指向a變量對(duì)應(yīng)的地址,*b指向a變量對(duì)應(yīng)的值
&: 取地址符, * : 取值符,二者互為逆運(yùn)算。
#include <stdio.h>
int main()
{
int a = 1;
int* b;
b = &a; //b指針?biāo)赶虻臄?shù)據(jù)等于a的地址
//int *b = &a;
printf("a = %d, b = %d\n", a, *b);
//a = 1, b = 1
a = 6;
printf("a = %d, b = %d\n", a, *b);
// a = 6, b = 6
*b = 9;
printf("a = %d, b = %d\n", a, *b);
// a = 9, b = 9
return 0;
}
經(jīng)典的swap()
void swap(int *p1, int *p2)
{
int temp;
temp = *p1;
*p1 = *p2;
*p2 = temp;
}
int main()
{
int a = 1;
int b = 2;
// 指針傳參數(shù)會(huì)在子函數(shù)改變 a, b 數(shù)值
swap(&a, &b);
printf("a= %d, b = %d\n", a, b);
return 0;
}
swap傳入的是a和b的地址,p1=&a,p2=&b,temp =* p1= * (&a) = a。即交換的是地址中的值,地址不變。
