數據結構4:順序表(線性表的順序存儲結構)及C語言實現


邏輯結構上呈線性分布的數據元素在實際的物理存儲結構中也同樣相互之間緊挨着,這種存儲結構稱為線性表的順序存儲結構。

也就是說,邏輯上具有線性關系的數據按照前后的次序全部存儲在一整塊連續的內存空間中,之間不存在空隙,這樣的存儲結構稱為順序存儲結構。

使用順序存儲結構存儲的數據,第一個元素所在的地址就是這塊存儲空間的首地址。通過首地址,可以輕松訪問到存儲的所有的數據,只要首地址不丟,數據永遠都能找着(一根繩上的螞蚱,要有就都有)。

使用線性表的順序存儲結構生成的表,稱為順序表。

                             
圖1 順序表結構示意圖

順序表的實現方法

順序表中存放數據的特點和數組這種數據類型完全吻合,所以順序表的實現使用的是數組。

數組實現順序表的存儲結構時,一定要注意預先申請足夠大的內存空間,避免因存儲空間不足,造成數據溢出,導致不必要的程序錯誤甚至崩潰。

順序表的存儲結構

在建立順序表時,除了預先申請內存空間,還需要實時記錄順序表的長度和順序表本身申請的內存大小,便於后期對順序表中的數據元素進行調取。
 

所以,要自定義順序表的結構:

typedef struct Table{
int * head;//聲明了一個名為head的長度不確定的數組,也叫“動態數組”
int length;//記錄當前順序表的長度
int size;//記錄順序表分配的存儲容量
}table;

順序表的創建

順序表的建立,也就是順序表進行初始化,在預先申請內存空間的同時,給變量size和length賦初值:

table initTable()
{   table t;   t.head
=(int*)malloc(Size*sizeof(int));  //構造一個空的順序表,動態申請存儲空間   if (!t.head)   //如果申請失敗,作出提示並直接退出程序   {       printf("初始化失敗");     exit(0);   }   t.length=0;     //空表的長度初始化為0   t.size=Size;    //空表的初始存儲空間為Size   return t; }

 

順序表查找元素

在數組中查找某個數據元素時,可以采取多種查找算法,例如二分查找、插值查找、斐波那契查找算法等。

具體的查找算法以及各自的時間復雜度后續章節會介紹。

根據順序表中存儲的數據的特點,選擇合適的算法。這里,采用順序查找算法(普通的遍歷算法)。

實現代碼:

//查找函數,其中,elem表示要查找的數據元素的值
int selectTable(table t, int elem)
{   
for (int i=0; i<t.length; i++)
  {     
if (t.head[i] == elem)
    {       
return i+1;     }   }   return -1;//如果查找失敗,返回-1 }

順序表中更改元素

順序表中更改數據元素,最簡單直接的方式就是:調用查找算法找到該數據元素的位置,直接在該位置上更改。

實現代碼:

//更改函數,其中,elem為要更改的元素,newElem為新的數據元素
table amendTable(table t, int elem, int newElem)
{   
int add = selectTable(t, elem);   t.head[add-1] = newElem;  //由於返回的是元素在順序表中的位置,所以-1就是該元素在數組中的下標   return t; }

 

順序表插入元素

插入數據元素,無非三種情況:

  1. 在表頭插入
  2. 在表的中間某個位置插入
  3. 直接尾隨順序表,作為表的最后一個元素


無論在順序表的什么位置插入數據元素,解決辦法都是:找到要插入的位置,將后續數據元素整體向后移動一個位置,最后直接在騰出來的位置上插入數據元素。

實現代碼:

//插入函數,其中,elem為插入的元素,add為插入到順序表的位置
table addTable(table t, int elem, int add)
{
  //判斷插入本身是否存在問題(如果插入元素位置比整張表的長度+1還大(如果相等,是尾隨的情況),
  //或者插入的位置本身不存在,程序作為提示並自動退出)
  if (add>t.length+1 || add<1) 
  {     printf(
"插入位置有問題");     return t;   }   //做插入操作時,首先需要看順序表是否有多余的存儲空間提供給插入的元素,如果沒有,需要申請   if (t.length == t.size)
  {     t.head
=(int *)realloc(t.head, (t.size+1)*sizeof(int));     if (!t.head)
    {       printf(
"存儲分配失敗");       return t;     }     t.size += 1;   }   //插入操作,需要將從插入位置開始的后續元素,逐個后移   for (int i=t.length-1; i>=add-1; i--)
  {     t.head[i
+1] = t.head[i];   }   //后移完成后,直接將所需插入元素,添加到順序表的相應位置   t.head[add-1] = elem;   //由於添加了元素,所以長度+1   t.length++;   return t; }

 

注意:在此程序中,當數組存儲空間不足時,使用realloc函數每次額外多申請 1 個int型的存儲空間,這么做還不是最優。最好的辦法就是每次發現空間不夠時,多申請幾個內存空間,這么做的好處是:在后續做插入操作過程中不需要每次都運行realloc函數,提高了程序的運行效率。

順序表刪除元素

在數組中刪除元素時,只需將該元素所在位置后的所有數據元素整體前移 1 個位置即可。

前移的過程中被刪除元素被后一個元素覆蓋掉,間接實現了刪除操作。

實現代碼:

table delTable(table t,int add)
{   
if (add>t.length || add<1)
  {     printf(
"被刪除元素的位置有誤");     exit(0);   }   //刪除操作   for (int i=add; i<t.length; i++)
  {     t.head[i
-1]=t.head[i];   }   t.length--;   return t; }

 

完整的程序

#include <stdio.h>
#include <stdlib.h>
#define Size 4
typedef struct Table
{   
int * head;   int length;   int size; }table;
table initTable()
{   table t;   t.head
=(int*)malloc(Size*sizeof(int));   if (!t.head)   {     printf("初始化失敗");     exit(0);   }   t.length = 0;   t.size = Size;   return t; }
table addTable(table t,
int elem,int add) {   if (add>t.length+1 || add<1)
  {     printf(
"插入位置有問題");     return t;   }   if (t.length >= t.size)
  {     t.head
= (int *)realloc(t.head, (t.size+1)*sizeof(int));     if (!t.head)
    {       printf(
"存儲分配失敗");     }     t.size += 1;   }   for (int i=t.length-1; i>=add-1; i--)
  {     t.head[i
+1]=t.head[i];   }   t.head[add-1] = elem;   t.length++;   return t; }
table delTable(table t,
int add)
{   
if (add>t.length || add<1)
  {     printf(
"被刪除元素的位置有誤");     exit(0);   }   for (int i=add; i<t.length; i++)
  {     t.head[i
-1] = t.head[i];   }   t.length--;   return t; }
int selectTable(table t,int elem)
{   
for (int i=0; i<t.length; i++)
  {     
if (t.head[i]==elem)
    {       
return i+1;     }   }   return -1; }
table amendTable(table t,
int elem, int newElem)
{   
int add = selectTable(t, elem);   t.head[add-1] = newElem;   return t; }
void displayTable(table t)
{   
for (int i=0; i<t.length; i++)
  {     printf(
"%d",t.head[i]);   }   printf("\n"); }
int main()
{   table t1
= initTable();   for (int i=1; i<=Size; i++)
  {     t1.head[i
-1] = i;     t1.length++;   }   printf("原順序表:\n");   displayTable(t1);   printf("刪除元素1:\n");   t1=delTable(t1, 1);   displayTable(t1);   printf("在第2的位置插入元素5:\n");   t1 = addTable(t1, 5, 2);   displayTable(t1);   printf("查找元素3的位置:\n");   int add = selectTable(t1, 3);   printf("%d\n",add);   printf("將元素3改為6:\n");   t1 = amendTable(t1, 3, 6);   displayTable(t1);   return 0; }
輸出結果: 原順序表:
1234 刪除元素1: 234 在第2的位置插入元素5: 2534 查找元素3的位置: 3 將元素3改為6: 2564

順序表的優缺點

順序表實現的基礎,完全借用了數組這一數據類型,優點是在對數據進行遍歷時,數據在連續的物理空間中存放,查找的速度比較快。

但是由於數組本身的限制,在向順序表中新增或者刪除數據元素時,如果被操作位置后續有很多數據元素,后續所有的數據元素都需要前移,最后雖然實現了功能,但是程序總體效率不高。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM