數據結構——線性表(順序實現)


    好好學習基礎知識,出人頭地就靠它了,內外兼修。(好吧,我現在內外都不行)寫這篇文章的目的就是為了,鞏固剛學完的線性表,個人能力有限,若有不當之處,望指出。

線性表

  好了,扯完了,說正事:

  1、定義

    線性表是一種及其常用的並且最簡單的一種數據結構。簡單來說,線性表就是集合里的元素的有限排列。(在這里我把集合定義為具有相同屬性的元素,會有些狹義)

  在線性表中數據元素之間的關系是一對一的關系,即除了第一個和最后一個數據元素之外,其它數據元素都是首尾相接的(注意,這句話只適用大部分線性表,而不是全部。比如,循環鏈表邏輯層次上也是一種線性表(存儲層次上屬於鏈式存儲),但是把最后一個數據元素的尾指針指向了首位結點)[百度百科]

  怎么說呢,畢竟數據結構畢竟是邏輯結構,邏輯上符合線性結構的特征即可,存儲結構能實現就行。線性表的很重要!很重要!很重要!后面的棧,隊列,串等都是基於線性表的基礎上實現的,所以說一定要學好線性表

  2、線性表的特點:

    對於任意的的非空線性表或者線性結構有:

      1、存在唯一一個被稱為 ”第一個“的元素

      2、存在唯一一個被稱為 ”最后一個“的元素

      3、出第一個元素之外,每一個元素都存在一個后繼

      4、除最后一個元素之外,每一個元素都存在一個前驅

  3、基本操作

    1、Create(*L)創建空表

    2、InitEmpty(*L)初始化

    3、getLength(*L)獲取長度

    4、Insert(*L)插入元素

    5、Remove(*L)移除元素

    6、IsEmpty(*L)空表檢測

    7、IsFulled(*L)表滿檢測(順序表常用,鏈式表基本不用)

    8、Delete(*L)刪除表

    9、getElemt(*L)獲取元素

    10、Traverse(*L)遍歷輸出所有元素

    11、Clear(*L)清除所有元素

 

   4 、實現

    好了最麻煩的事情開始了,數據結構在計算機上的的映射。眾所周知,線性表有兩種實現方法,一種是順序表,另一種是鏈式表,這兩種結構實現最大的不同在於前者邏輯關系無需存儲空間,而后者則需要用額外的空間(順便記錄一下,指針大小只由環境有關(嚴格意義上說和CPU的位數有關)本篇只實現順序結構)。

    1、順序表:

      先說一下概念:用一組地址連續的存儲單元依次存儲線性表的數據元素,這種存儲結構的線性表稱為順序表。

    很明顯對於順序表來說:邏輯上相鄰的數據元素,物理次序也是相鄰的。 

      特點:

        對表中任意元素訪問時間復雜度都為常數級。

        尾部插入元素時間復雜度也為常量級。

  下面是重點!!!

    說明一下,講解的時候實現語言為C,后續會貼上C++和Python

      實現:

   敲~~~鍵盤,敲~~鍵盤!敲~~~鍵盤,敲~~鍵盤!

  結構定義:  

 1 #define YWZLIST_INIT_SIZE 8
 2 #define INC_SIZE 3 //空間增量的大小
 3 
 4 typedef int ElemType;
 5 typedef struct listnode
 6 {
 7     ElemType *base;
 8     int capacity; //順序表容量
 9     int size;     //表的大小
10 } YWZlist;

 

  函數聲明:

 1 bool IsEmpty(YWZlist *list);                         //空表檢測
 2 bool IsFull(YWZlist *list);                          //表滿檢測
 3 bool Inc(YWZlist *list);                             //增加順序表的容量
 4 void InitSeqlist(YWZlist *list);                     //初始化順序表
 5 void Push_back(YWZlist *list, ElemType x);           //在順序表的末尾插入元素
 6 void Push_front(YWZlist *list, ElemType x);          //在順序表的頭部插入元素
 7 void Show_list(YWZlist *list);                       //顯示順序表中的元素
 8 void Pop_back(YWZlist *list);                        //刪除順序表最后一個元素
 9 void Pop_front(YWZlist *list);                       //刪除順序表第一個元素
10 void Insert_pos(YWZlist *list, int pos, ElemType x); //在順序表的選定位置上插入數據
11 int Find(YWZlist *list, ElemType key);               //在順序表中查找元素key的下標
12 int Length(YWZlist *list);                           //求順序表的長度
13 void Delete_pos(YWZlist *list, int pos);             //刪除順序表中特定位置的數據元素
14 void Delete_val(YWZlist *list, int key);             //刪除順序表中值為key的數據元素
15 void Sort(YWZlist *list);                            //冒泡排序
16 void Reverse(YWZlist *list);                         //逆置順序列表
17 void Clear(YWZlist *list);                           //清除順序表中的所有元素
18 void Destroy(YWZlist *list);                         //摧毀順序表
19 void Merge(YWZlist *lt, YWZlist *la, YWZlist *lb);   //合並兩個順序列表

    上面一共19個函數,空表和表滿函數就不寫了

  1、初始化:

    思路很明確,先開辟一個最小的數組空間,給capacity賦值

void InitYWZlist(YWZlist *list) //初始化順序表
{
    //開辟初始空間,開辟失敗返回空
    list->base = (ElemType *)malloc(sizeof(ElemType) * YWZLIST_INIT_SIZE);

    if (list->base == NULL)
    {
        printf("內存不足!\n");
        return;
    }

    list->capacity = YWZLIST_INIT_SIZE - 1;
    list->size = -1;
}
初始化順序表

  2、擴容:

    順序表擴容不比鏈表,順序表需要重新開劈空間需要用到realloc函數,來看一下函數原型

    _CRTIMP void* __cdecl __MINGW_NOTHROW	realloc	(void*, size_t);  //傳進去的是要改變大小的指針

    嗯,兩個參數,一個指針,一個大小,由於要新開辟一塊空間,所以我們不能用原指針來指向新開辟的空間,我i們需要一個新的之指針,下一步當然是計算需要多少空間,這就和malloc函數一樣了,自己琢磨琢磨,記得改變capacity的值,貼代碼:

bool Inc(YWZlist *list)
{
    //重新分配內存空間
    ElemType *newspace = (ElemType *)realloc(list, sizeof(ElemType) * (list->capacity + INC_SIZE));
    if (newspace == NULL)
    {
        printf("內存空間已滿,無法再分配內存空間!\n");
        return false;
    }

    list->base = newspace;
    list->capacity += INC_SIZE;

    return true;
}
擴容

   3、尾部插入:

     順序表的尾部插入無比的簡單,因為物理上相鄰,所以訪問最后一個元素的時間復雜度為常量級。插入元素我們不僅需要考慮表是否表滿,同時我們還需要檢測是否可以開辟新的空間。只有當表滿且內存不足才能返回異常。

void Push_back(YWZlist *list, ElemType x)
{
    //當表滿且無法擴容
    if (IsFull(list) && !Inc(list))
    {
        printf("順序表容量已滿,無法再在表尾繼續插入新元素!\n");
        return;
    }

    list->base[list->size] = x;
    list->size++;
}
尾部插入

  4、頭部插入

     這個就麻煩了,因為物理相鄰,所以我們得把所有元素都向后移。(這也是鏈式表出現的原因之一)當然啦,打印錯誤的條件和尾部插入相同。

void Push_front(YWZlist *list, ElemType x)
{
  if (IsFull(list) && !Inc(list))
  {
    printf("順序表容量已滿,無法再在表尾繼續插入新元素!\n");
    return;
  }

  for (int i = list->size; i > 0; i--)
  {
    list->base[i] = list->base[i - 1];
  }
  list->base[0] = x;
  list->size++;
}
頭部插入

  5、第i個索引插入

    說完尾部和頭部,我們來類推一下第i個元素刪除的。

    第一步,都不用想肯定是先找到第i個元素,

    第二步,將n-i+1個元素個元素向后移動,

    第三步,插入元素

    第四步,size++

  按着這個思路貼上代碼

void Insert_pos(YWZlist *list, int pos, ElemType x) 
{
  if (pos < 0 || pos > list->size)
  {
    printf("插入位置不合法,無法插入元素!\n");
    return;
  }
  if (list->size >= list->capacity && !Inc(list))
  {
    printf("順序表容量已滿,無法在插入新的元素!\n");
    return;
  }
  for (int i = list->size; i > pos; i--)
  {
    list->base[i] = list->base[i - 1];
  }
  list->base[pos] = x;
  list->size++;
}
任意位置插入

  6、遍歷

    這個直接貼代碼,太簡單了,不做作說明

void Show_list(YWZlist *list)
{
  for (int i = 0; i < list->size; i++)
  {
    printf("%d ", list->base[i]);
  }
  printf("\n");
}
遍歷

  7、尾部刪除

    刪除其實沒有想象中的那么難,就是把size--,因為你下次插入就是直接覆蓋了,和原先的值沒有任何關系。當然你得先判斷一下表是否為空

void Pop_back(YWZlist *list) 
{
  if (list->size == 0)
  {
    printf("順序表已空,無法再在表尾刪除元素!\n");
    return;
  }
  list->size--;
}
尾部刪除

  8、頭部刪除

    個人覺得無論是鏈表還是順序表,只要會了插入,刪除類推一下就會了。打個比方順序表的頭部插入是,一個個往后推,那么刪除就是一個個往前移。當然一個是判斷表滿一個是判斷表空

void Pop_front(YWZlist *list) 
{
  if (list->size == 0)
  {
    printf("順序表已空,無法再在表頭刪除元素!\n");
    return;
  }
  for (int i = 0; i < list->size - 1; i++)
  {
    list->base[i] = list->base[i + 1];
  }
  list->size--;
}
頭部刪除

  9、指定位置刪除

    類推吧,

    第一步,找到第i個元素

    第二部,將從第i+1到第n個元素向前移動1

    第三步,size--;

   是不是和插入很像,所以有些代碼是很像的

void Delete_pos(YWZlist *list, int pos) 
{
  if (pos < 0 || pos >= list->size)
  {
    printf("刪除位置不合法,無法刪除元素!\n");
    return;
  }
  for (int i = pos; i < list->size - 1; i++)
  {
    list->base[i] = list->base[i + 1];
  }
  list->size--;
}
刪除指定位置的元素

  10、按元素刪除

    這個是我覺得,一般人找東西,都只記得叫什么名字而不是第幾個,所以寫了一個按元素刪除的。其實思路和指定位置刪除一樣找到最前端的一樣的元素后插入即可(有bug,當表中存在重復元素的時候,只會添加到最前面那個)

void Delete_val(YWZlist *list, int key) //刪除順序表中值為key的數據元素
{
  int pos = Find(list, key);
  if (pos == -1)
  {
    printf("順序表中沒有這個元素!\n");
    return;
  }
  Delete_pos(list, pos);
}
按元素刪除

  11、元素查找

    不打算說,貼代碼

int Find(YWZlist *list, ElemType key) //在順序表中查找元素key的下標
{
  for (int i = 0; i < list->size; i++)
  {
    if (list->base[i] == key)
      return i;
  }
  return -1;
}
元素查找

  12、返回長度

    size中記錄了表長,直接return就好了

int Length(YWZlist *list) 
{
  return list->size;
}
表長

  13、排序

    冒泡,不在這里做說明,自己去看。其實可以直接調用qsort函數的寫一個compare函數就行了

void Sort(YWZlist *list) 
{
  for (int i = 0; i < list->size - 1; i++)
  { //排序的趟數(例如5個數據需要比較4趟)
    for (int j = 0; j < list->size - 1 - i; j++)
    { //每一趟比較中的比較次數(例如5個數據在第0趟需要比較4次)
      if (list->base[j] > list->base[j + 1])
      {
        ElemType temp = list->base[j];
        list->base[j] = list->base[j + 1];
        list->base[j + 1] = temp;
      }
    }
  }
}
冒泡排序

  14、順序表逆序

  要么寫個for二分表只執行到n/2,要么寫個while,大小指針(不是真正意義上的指針)小指針大於大指針的時候停止

void Reverse(YWZlist *list) //逆置順序列表
{
  if (list->size == 0 || list->size == 1)
  {
    return;
  }
  int low = 0, high = list->size - 1;
  while (low < high)
  {
    ElemType temp = list->base[low];
    list->base[low] = list->base[high];
    list->base[high] = temp;
    low++;
    high--;
  }
}
順序表逆序

  15、清空表

  無比簡單,就一句size = -1;

void Clear(YWZlist *list) 
{
  list->size = 0;
}
清除順序表中的所有元素

  16、刪除表

  free完畢之后,將所有成員重置

void Destroy(YWZlist *list) 
{
  free(list->base);
  list->base = NULL;
  list->capacity = 0;
  list->size = 0;
}
摧毀順序表

  17、合並兩個順序表

  這個就比較麻煩了

  第一步:開辟足夠的空間

  第二步:讓La與Lb比較,誰大/小存誰,直到一個表結束

  第三步:將余下的元素都,存入表Lc中。

  第四步:計算表長。

void Merge(YWZlist *lt, YWZlist *la, YWZlist *lb) 
{
  lt->capacity = la->size + lb->size;
  lt->base = (ElemType *)malloc(sizeof(ElemType) * lt->capacity);
  //assert(lt->base != NULL);
  int ia = 0, ib = 0, ic = 0;
  while (ia < la->size && ib < lb->size)
  {
    if (la->base[ia] < lb->base[ib])
    {
      lt->base[ic++] = la->base[ia++];
    }
    else
    {
      lt->base[ic++] = lb->base[ib++];
    }
  }
  while (ia < la->size)
  {
    lt->base[ic++] = la->base[ia++];
  }
  while (ib < lb->size)
  {
    lt->base[ic++] = lb->base[ib++];
  }
  lt->size = la->size + lb->size;
  Show_list(lt);
}
合並兩個順序列表

  本篇只實現順序表


免責聲明!

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



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