順序存儲結構
順序存儲結構就是用一段連續的存儲單元依次存儲線性表中的各數據元素,如下圖
a1 | a2 | a3 | … | ai-1 | ai | ai+1 | … | an |
---|
再來看看數組在內存中是怎麼存儲的
a0 | a1 | a2 | … | ai-2 | ai-1 | ai | … | an-1 |
---|
是不是和數組的存儲形式很像,沒錯,它就是由數組的方式實現的,那怎樣處理這個下標的問題呢,來看看它的結構代碼
線性表順序存儲結構的結構代碼
#define MAXSIZE 30 //線性表的元素個數,也是數組長度
typedef int ElemType; //以ElemType來替代線性表中元素數據類型
typedef struct
{
ElemType data[MAXSIZE]; //在線性表裏面存儲對應的數據
int length; //存儲當前的長度(從1開始計數,例如data[i]對應的length就是i+1,因爲數組是從0開始計數的)
}SqList;
從上面的代碼塊,我們可以看出線性表的順序存儲結構封裝需要三個屬性
- 存儲空間的起始位置(即數組data的空間存儲位置)
- 線性表的最大存儲容量(即數組長度MAXSIZE)
- 線性表的當前長度(在順序存儲結構中用length表示,length的值是數組元素data[i]的下標加1)
順序存儲結構的地址計算方法
前面已經強調過,線性表數據元素是從1開始計數的所以線性表中地址計算也是從1開始的。假設ElemType佔用 c 個存儲單元,則第 i+1 個元素和第 i 個元素的存儲關係爲(用 LOC()(location的縮寫)函數來獲取存儲位置)
LOC(ai+1)= LOC(ai)+ c。
所以我們可以推導出任意位置 i 的關係 LOC(ai)= LOC(a1)+ ( i-1) c*
元素 | a1 | a2 | a3 | … | ai-1 | ai | ai+1 | … | an | 空閒空間 |
---|---|---|---|---|---|---|---|---|---|---|
下標 | 0 | 1 | 2 | … | i-2 | i-1 | i | … | n-1 |
我們可以很容易推斷出來,在順序存儲結構中,存儲數據的時間性能爲O(1),只需要根據當前長度找到對應數組元素下標就可以了,也通常將其稱爲隨機存儲結構
順序存儲結構的查找、插入和刪除操作
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int status;
/*status爲函數的狀態代碼,如OK*/
#define MAXSIZE 30
typedef int ElemType;
typedef struct
{
ElemType data[MAXSIZE];
int length;
}SqList;
/*線性表順序存儲結構的查找操作*/
status GetElem(SqList L, int i, ElemType* e);
/*在第i個數據元素前插入操作*/
status ListInsert(SqList* L, int i, ElemType e);
/*刪除操作*/
status ListDelete(SqList* L, int i, ElemType* e);
int main(void)
{
/*隨機生成一個順序表*/
time_t ts;
srand((unsigned int)time(&ts)); //生成一個隨機數種子
SqList* L = (SqList*)malloc(sizeof(SqList));
if (MAXSIZE == 0)
{
L->length = 0;
}
for (int i = 0; i < 14; i++)
{
L->data[i] = rand() % 100 + 1; //給順序表各數據元素賦值
L->length = i + 1;
}
/*打印線性表*/
printf("\n-------------順序表各元素的值爲--------------\n");
for (int i = 1; i <= L->length; i++)
{
printf("[%d]%-5d",i, L->data[i - 1]); //依次打印順序表各數據元素的值
}
printf("\n===============請選擇你要進行的操作=================\n");
printf(" 1 : 查找一個元素 |\n");
printf(" 2 : 插入一個元素 |\n");
printf(" 3 : 刪除一個元素 |\n");
printf(" 0 : 退出程序 |\n");
printf("******************************************************\n");
int select;
int Elem = 0;
while (1)
{
scanf("%d", &select);
if (select == 0)
{
break;
}
switch (select)
{
case 1:
{
int i, *e=&Elem;
printf("請輸入你要查找元素的位置:");
scanf("%d", &i);
if (GetElem(*L, i, e))
{
printf("\n你要查找位置元素的值爲%d\n", *e);
printf("順序表爲:\n");
}
else
{
printf("查找失敗,請重新選擇功能進行操作\n");
printf("順序表爲:\n");
}
break;
}
case 2:
{
int i, e=0;
printf("請輸入你要在哪個位置前插入數據元素:");
scanf("%d", &i);
getchar();
printf("\n請輸入你要插入數據元素的值:");
scanf("%d", &e);
if (ListInsert(L, i, e))
{
printf("新的表爲\n");
}
else
{
printf("插入失敗,請重新選擇功能進行操作\n");
printf("順序表爲:\n");
}
break;
}
case 3:
{
int i, * e=&Elem;
printf("請輸入你要刪除哪個位置的數據元素:");
scanf("%d", &i);
if (ListDelete(L, i, e))
{
printf("你刪除的元素爲%d\n", *e);
printf("新的表爲\n");
}
else
{
printf("刪除失敗,請重新選擇功能進行操作\n");
printf("順序表爲:\n");
}
break;
}
default:
printf("選擇錯誤,請重新選擇"); break;
}
for (int i = 1; i <= L->length; i++)
{
printf("[%d]%-5d", i, L->data[i - 1]); //依次打印順序表各數據元素的值
}
putchar('\n');
printf("\n=============請重新選擇你要進行的操作===============\n");
printf(" 1 : 查找一個元素 |\n");
printf(" 2 : 插入一個元素 |\n");
printf(" 3 : 刪除一個元素 |\n");
printf(" 0 : 退出程序 |\n");
printf("******************************************************\n");
}
system("pause");
return 0;
}
/*在順序表中,如果1<=i<=ListLength(L),則用e返回第i個數據元素的值*/
status GetElem(SqList L, int i, ElemType* e)
{
if (i<1 || L.length == 0 || i>L.length) //當 i 小於 1 ,或者循序表爲空, i 大於順序表長度時,返回錯誤
{
return ERROR; //i<1,順序表元素都是從1開始的,沒有第0個元素,i大於順序表長度時,找不到,順序表當前長度爲空時,也找不到
}
*e = L.data[i - 1];
return OK;
}
/*插入操作時,先將插入位置後面的數據元素向後移動一位,然後再插入*/
status ListInsert(SqList* L, int i, ElemType e)
{
if (L->length == MAXSIZE) //順序表滿時
{
return ERROR;
}
if (i<1 || L->length == 0 || i>L->length + 1) //同理當順序表爲空或者i不在範圍時,返回錯誤
{
return ERROR;
}
if (i <= L->length) //當插入位置不在表尾時
{
for (int j = L->length + 1; j > i; j--) //使數據元素向後移一位
{
L->data[j - 1] = L->data[j - 2];
}
}
L->data[i - 1] = e; //在第i個位置插入元素
L->length++; //順序表當前長度加1
return OK;
}
/*刪除先找到刪除的位置,然後將刪除位置的數據元素的data賦值給*e,再將各數據向前移動一位,並且順序表當前長度減1*/
status ListDelete(SqList* L, int i, ElemType* e)
{
if (L->length == 0 || i > L->length || i < 1) //同上
{
return ERROR;
}
if (i < L->length)
{
*e = L->data[i - 1];
for (int j = i; j < L->length; j++)
{
L->data[j - 1] = L->data[j];
}
}
L->length--;
return OK;
}
程序運行如下
線性表順序結構的優缺點
優點
無須爲表示表中元素之間邏輯關係而增加額外存儲空間,並且可以快速存取表中任意位置元素
缺點
插入和刪除需要移動大量元素,線性表的長度變化較大時,難以確定存儲空間容量容易造成存儲空間碎片
總結
從上面的程序中我們可以看出來,順序表的讀取和存儲時間複雜度爲O(1),但是插入和刪除時間複雜度爲O(n),因此當需要大量的讀取和存儲時候,通常採用順序表結構
常見的遊戲開發中,由於每天要大量讀取賬號信息和存儲新用戶信息,賬號信息則是用順序結構。