數據結構的運算是建立在邏輯結構層次上的,而運算的具體實現是建立在存儲結構上的。
抽象數據結構定義為
ADT Linear_List
{
數據對象:任意數據元素的集合D={a|任意數據元素}
數據關系:除頭元素和尾元素外,其它元素均有一個直接的前驅和一個直接的后繼
}ADT Linear_List
線性表的順序存儲結構
數據結構在內存中的存儲通常有兩種:順序存儲(又稱順序表)和鏈式存儲(又稱鏈表)。
順序表
-
圖示
image.png
特點:各元素用一塊地址連續的存儲空間,邏輯上相鄰的元素物理存儲上也相鄰。
地址的計算公式(第i個元素的地址):
Loc(ai)=Loc(a1)+(i-1)d
其中:1≤i≤n,d是每個元素所占的地址大?。ㄍǔ樽止潝担?br> 如:設Loc(a1)的起始地址是0011,d=2(字節)
Loc(a2)=Loc(a1)+(2-1)d=Loc(a1)+d=0011+2=0013
- C語言定義
在C語言中,對順序表顯然用數組存儲比較適宜。
#define MAXSIZE 100 //選擇足夠大的空間
typedef int ElemType; //ElemType是int類型的一個別名
typedef struct node
{
ElemType data[MAXSIZE];//存儲各表元素
int length; //表長
} SeqList; //順序表的類型
SeqList L;//L為順序表(結構體變量)
image.png
注意:
元素次序為:1,2,3,……,i
C語言中數組元素的下標次序為:0,1,2,……,i-1
若用L(結構體變量)表示:
第i個元素自身:L.data[i-1]
前驅是:L.data[i-2]
后繼是:L.data[i]
表長:L.length
基本操作
1.順序表的初始化
- SeqListInit(L) //初始化操作,構造一個空表
void SeqListInit(SeqList L)
{
L.length=0; //將順序表長度置0
}
2.順序表求長度
- SeqListLength(L) //求表長
int SeqListLength(SeqList L)
{
return(L.length); //返回順序表的長度
}
時間復雜度:O(1)
3.順序表取元素
- SeqListGet(L,i) //取元素
ElemType SeqListGet(SeqList L,int i)
{
if(i>=1&&i<=L.length) //i值是否合法
return(L.data[i-1]); //返回該元素
else {printf(“i值不合法”);exit(0);}
}
時間復雜度:O(1)
4.順序表元素的定位操作
- SeqListLocate(L,x) //查找元素
int SeqListLocate(SeqList L,ElemType e)
{
i=1;
while(i<=L.length&&e!=L.data[i-1])i++;
if(i<=L.length)return(i);
else {printf(“此元素不在表中”);exit(0);}
}
時間復雜度:O(n)
- 順序表求前驅操作
- SeqListPrior(L,e) //求元素的前驅
ElemType SeqListPrior(SeqList L,ElemType e)
{
i=SeqListLocate(L,e);
if(i==1){printf(“第一個元素沒有前驅”);
exit(0);}
else return(L.data[i-2]);//返回該元素的前驅
}
時間復雜度:O(n)
- 順序表求后繼操作
- SeqListPrior(L,e) //求元素的后繼
ElemType SeqListPrior(SeqList L,ElemType e)
{
i=SeqListLocate(L,e);
if(i==L.length)
{printf(“最后一個元素沒有后繼”);
exit(0);}
else return(L.data[i]);//返回該元素的后繼
}
時間復雜度:O(n)
7.順序表的前插操作
- SeqListInsert(L,i,e) //前插元素
在表的第i的位置之前插入一個值為b的新元素,使表長變為L.length+1
操作步驟:
(1)檢查插入要求的合理性
(2)將至順序向后挫動,為新元素b讓出位置
(3)將b插入空出的第i的位置
(4)修改表長的值L.length+1
void SeqListInsert(SequList L,int i,ElemType b)
{
int j;
if(L.length==MAXSIZE)
{printf("表滿,無法插入");exit(0);}
if(i<1||i>L.length)
{printf("插入位置i非法");exit(0);}
for(j=L.length-1;j>=i-1;j--)
L.data[j+1]=L.data[j]; // 元素后移
L.data[i-1]=b; // 在第i個位置上插入b
L.length++; // 表長加1
}
時間復雜度為O(n)
8.順序表的刪除
- ListDelete(L,i) //刪除元素
將表的第i個元素從表中刪除,使原表長L.length-1
刪除步驟如下:
(1) 檢查刪除要求的合理性
(2)將至順序向前移動擠掉(刪除)
(3) 修改表長的值L.length-1。
void SeqListDelete(SeqList L,int i)
{
int j;
if(i<1||i>L.length)
{printf("刪除位置i非法");exit(0);}
for(j=i;j<=L.length-1;j++)
L.data[j-1]=L.data[j]; // 元素前移
L.length--; // 表長減1
}
時間復雜度為O(n)
9.順序表判空操作
- ListEmpty(L) //判空表
int SeqListEmpty(SeqList L)
{
return(!L.length);//L.length==0
}
10.順序表的遍歷
- SeqListTraverse(L) //遍歷
void SeqListTraverse(SeqList L)//遍歷
{
int i;
if(SeqListEmpty(L))printf(“空表”);
// SeqListEmpty(L)==1
else for(i=1;i<=L.length;i++)
printf("%5d",L.data[i-1]);
}
說明:以上操作只是表的基本操作,絕不是表的全部操作。
11.順序表的創建
void SeqListcreat(SeqList L) // 創建
{
int i=0;
ElemType x;
printf("創建順序表,輸入若干整數,-1作為結束: ");
scanf("%d",&x);
while(x!=-1)
{
L.data[i]=x;
scanf("%d",&x);
i++;
}
L.length=i; // 記錄數據個數(即表長)
}
運用
一、 順序表的創建、遍歷插入和刪除
#define MAXSIZE 100
#include<stdio.h>
typedef int ElemType;
typedef struct
{
ElemType data[MAXSIZE];
int length;
} SeqList;
SeqList L;
int empty(void) //判空表
{
return(!L.length);
}
void creat(void) // 創建
{
int i=0;
ElemType x;
printf("創建順序表,輸入若干整數,-1作為結束: ");
scanf("%d",&x);
while(x!=-1)
{
L.data[i]=x;
scanf("%d",&x);
i++;
}
L.length=i; // 記錄數據個數(即表長)
}
void visit(void)//遍歷
{
int i;
if(empty())printf("空表");
else
for(i=1;i<=L.length;i++)printf("%5d",L.data[i-1]);
printf("\n表長是:%d\n\n",L.length);
}
void insert(int i,ElemType b)//插入
{
int j;
if(L.length==MAXSIZE)printf("表滿,無法插入");
if(i<1||i>L.length)
{printf("插入位置i非法\n");exit(0);}
for(j=L.length-1;j>=i-1;j--)L.data[j+1]=L.data[j];
// 元素后移
L.data[i-1]=b; // 在第i個位置上插入b
L.length++; // 表長加1
}
void deletes(int i)//刪除
{
int j;
if(i<1||i>L.length)
{printf("刪除位置i非法\n");exit(0);}
for(j=i;j<=L.length-1;j++)L.data[j-1]=L.data[j];
// 元素前移
L.length--; // 表長減1
}
void main()
{
int i;ElemType x;
creat();//創建
printf("創建后的順序表L: ");visit();//創建后的遍歷
printf("輸入插入位置int i和數據int x: ");
scanf("%d%d",&i,&x);
insert(i,x);//插入
printf("插入后的順序表L: ");visit();//插入后的遍歷
printf("輸入刪除位置int i: ");scanf("%d",&i);
deletes(i);//刪除
printf("刪除后的順序表L: ");visit();//刪除后的遍歷
}
二、 已知順序表的數據元素遞增有序,在表中插入一個數據后仍保持順序表有序。
// 輸入若干數據,先變為遞增有序順序表,再在表中插入一個元素,并仍保持遞增有序。
#define MAXSIZE 100
#include<stdio.h>
typedef int DataType;
typedef struct node
{
DataType data[MAXSIZE+1];
int last;
} SequList;
void Creat(SequList *a) //創建順序表
{
DataType x;
int i=0;
printf("創建順序表,輸入若干整數(int),-1作為結束: ");
scanf("%d",&x); // 輸入第一個數據
while(x!=-1)
{
i++;
a->data[i]=x;
scanf("%d",&x); // 輸入下一個數據
}
a->last=i; // 記錄數據個數(即表長)
}
void Sort(SequList *a) //順序表排序
{
int i,j;
DataType temp;
for(i=1;i<a->last;i++)//選擇排序
for(j=i+1;j<=a->last;j++)
if(a->data[i]>a->data[j])
temp=a->data[i],
a->data[i]=a->data[j],
a->data[j]=temp;
}
void OrdInsert(SequList *a,DataType value)//插入一個元素
{
int i,pos=1;//從第一個元素開始
a->data[a->last+1]=value;//設置監視哨
while(value>a->data[pos])pos++;
//查找插入位置
for(i=a->last;i>=pos;i--)
a->data[i+1]=a->data[i];//數據元素移動
a->data[pos]=value;//插入數據
a->last++;//修改表長
}
void Visit(SequList L)//遍歷順序表
{
int i;
for(i=1;i<=L.last;i++)
printf("%5d",L.data[i]);
printf("\n\n");
}
void main()
{
DataType value;
SequList L;
Creat(&L);//創建順序表
printf("創建后的順序表:");Visit(L);
Sort(&L);
printf("排序后的順序表:");Visit(L);
printf("輸入要插入的數據int value: ");
scanf("%d",&value);
OrdInsert(&L,value);//插入
printf("插入后的鏈表:");
Visit(L);//插入后的遍歷
}
優缺點
- 優點:元素的位置容易確定(依據數組的下標)。所以生成和遍歷可以順序(或倒序)和隨機進行。
- 缺點:由于邏輯相鄰的元素物理存儲上也相鄰,所以需要一片連續的存儲單元;插入和刪除會引起前后大量元素的移動,又由于通常是用數組來存放,而數組事先要確定長度(靜態)。如果數組長度確定過小,插入時容易造成溢出;如果數組長度確定過大,刪除過多的元素造成存儲浪費。
這種機制有點像我們以前走過的“計劃經濟”過程。
下面介紹的鏈式存儲可以解決這種不足。從“計劃經濟”轉變為“市場經濟”。