線性表:2.線性表的順序存儲結構--順序表及C語言實現

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

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

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

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

圖1 順序表結構示意圖

順序表的實現方法

順序表中存放數據的特點和數組這種數據類型完全吻合,所以順序表的實現使用的是數組。
數組實現順序表的存儲結構時,一定要注意預先申請足夠大的內存空間,避免因存儲空間不足,造成數據溢出,導致不必要的程序錯誤甚至崩潰。

順序表的存儲結構

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

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

順序表的創建

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

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

順序表查找元素

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

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

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

實現代碼:

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

順序表中更改元素

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

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

順序表插入元素

插入數據元素,無非三種情況:
  1. 在表頭插入
  2. 在表的中間某個位置插入
  3. 直接尾隨順序表,作爲表的最後一個元素

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

實現代碼:
  1. //插入函數,其中,elem爲插入的元素,add爲插入到順序表的位置
  2. table addTable(table t,int elem,int add)
  3. {
  4. //判斷插入本身是否存在問題(如果插入元素位置比整張表的長度+1還大(如果相等,是尾隨的情況),
  5. //或者插入的位置本身不存在,程序作爲提示並自動退出)
  6. if (add>t.length+1||add<1) {
  7. printf("插入位置有問題");
  8. return t;
  9. }
  10. //做插入操作時,首先需要看順序表是否有多餘的存儲空間提供給插入的元素,如果沒有,需要申請
  11. if (t.length==t.size) {
  12. t.head=(int *)realloc(t.head, (t.size+1)*sizeof(int));
  13. if (!t.head) {
  14. printf("存儲分配失敗");
  15. return t;
  16. }
  17. t.size+=1;
  18. }
  19. //插入操作,需要將從插入位置開始的後續元素,逐個後移
  20. for (int i=t.length-1; i>=add-1; i--) {
  21. t.head[i+1]=t.head[i];
  22. }
  23. //後移完成後,直接將所需插入元素,添加到順序表的相應位置
  24. t.head[add-1]=elem;
  25. //由於添加了元素,所以長度+1
  26. t.length++;
  27. return t;
  28. }

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

順序表刪除元素

在數組中刪除元素時,只需將該元素所在位置後的所有數據元素整體前移 1 個位置即可。
前移的過程中被刪除元素被後一個元素覆蓋掉,間接實現了刪除操作。
實現代碼:
  1. table delTable(table t,int add){
  2. if (add>t.length || add<1) {
  3. printf("被刪除元素的位置有誤");
  4. exit(0);
  5. }
  6. //刪除操作
  7. for (int i=add; i<t.length; i++) {
  8. t.head[i-1]=t.head[i];
  9. }
  10. t.length--;
  11. return t;
  12. }

完整的程序

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define Size 4
  4. typedef struct Table{
  5. int * head;
  6. int length;
  7. int size;
  8. }table;
  9. table initTable(){
  10. table t;
  11. t.head=(int*)malloc(Size*sizeof(int));
  12. if (!t.head)
  13. {
  14. printf("初始化失敗");
  15. exit(0);
  16. }
  17. t.length=0;
  18. t.size=Size;
  19. return t;
  20. }
  21. table addTable(table t,int elem,int add)
  22. {
  23. if (add>t.length+1||add<1) {
  24. printf("插入位置有問題");
  25. return t;
  26. }
  27. if (t.length>=t.size) {
  28. t.head=(int *)realloc(t.head, (t.size+1)*sizeof(int));
  29. if (!t.head) {
  30. printf("存儲分配失敗");
  31. }
  32. t.size+=1;
  33. }
  34. for (int i=t.length-1; i>=add-1; i--) {
  35. t.head[i+1]=t.head[i];
  36. }
  37. t.head[add-1]=elem;
  38. t.length++;
  39. return t;
  40. }
  41. table delTable(table t,int add){
  42. if (add>t.length || add<1) {
  43. printf("被刪除元素的位置有誤");
  44. exit(0);
  45. }
  46. for (int i=add; i<t.length; i++) {
  47. t.head[i-1]=t.head[i];
  48. }
  49. t.length--;
  50. return t;
  51. }
  52. int selectTable(table t,int elem){
  53. for (int i=0; i<t.length; i++) {
  54. if (t.head[i]==elem) {
  55. return i+1;
  56. }
  57. }
  58. return -1;
  59. }
  60. table amendTable(table t,int elem,int newElem){
  61. int add=selectTable(t, elem);
  62. t.head[add-1]=newElem;
  63. return t;
  64. }
  65. void displayTable(table t){
  66. for (int i=0;i<t.length;i++) {
  67. printf("%d",t.head[i]);
  68. }
  69. printf("\n");
  70. }
  71. int main(){
  72. table t1=initTable();
  73. for (int i=1; i<=Size; i++) {
  74. t1.head[i-1]=i;
  75. t1.length++;
  76. }
  77. printf("原順序表:\n");
  78. displayTable(t1);
  79. printf("刪除元素1:\n");
  80. t1=delTable(t1, 1);
  81. displayTable(t1);
  82. printf("在第2的位置插入元素5:\n");
  83. t1=addTable(t1, 5, 2);
  84. displayTable(t1);
  85. printf("查找元素3的位置:\n");
  86. int add=selectTable(t1, 3);
  87. printf("%d\n",add);
  88. printf("將元素3改爲6:\n");
  89. t1=amendTable(t1, 3, 6);
  90. displayTable(t1);
  91. return 0;
  92. }
輸出結果:
原順序表:
1234
刪除元素1:
234
在第2的位置插入元素5:
2534
查找元素3的位置:
3
將元素3改爲6:
2564

順序表的優缺點

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

但是由於數組本身的限制,在向順序表中新增或者刪除數據元素時,如果被操作位置後續有很多數據元素,後續所有的數據元素都需要前移,最後雖然實現了功能,但是程序總體效率不高。
發佈了49 篇原創文章 · 獲贊 65 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章