【數據結構】線性表之順序表

順序表


一、簡介

順序表是在計算機內存中以數組的形式保存的線性表,是指用一組地址連續的存儲單元依次存儲數據元素的線性結構,邏輯結構與存儲位置吻合。

順序表分爲靜態順序表動態順序表兩種。二者差異如下:

相同點:
內存空間連續, 數據順序存儲

不同點:
(1)容量大小
靜態順序表再創建順序表的時候容量已經確定爲MAX_SIZE,不可以更改,而動態順序表的容量是由malloc函數動態開闢,當容量不夠用時可以增加容量capacity

(2)二者所佔內存空間的位置不同。
靜態定義一個順序表, 順序表所佔的內存空間開闢在內存的靜態區, 即所謂的函數棧上, 隨着函數調用的結束, 這塊內存區域會被系統自動回收;而動態生成一個順序表, 順序表所佔的內存空間開闢在內存的動態區, 即所謂的堆內存上, 這塊區域不會隨着函數調用的結束被系統自動回收, 而是需要程序員主動去釋放它.

靜態、動態順序表優缺點:

靜態順序表:
操作簡單, 不用malloc函數,不用手動釋放其內存空間, 不存在內存泄漏

動態順序表:
可以動態開闢內存空間, 操作靈活, 相比於靜態開闢空間也可以少空間浪費

二、靜態順序表

1、結構定義

靜態順序表不可擴展空間,使用前需要定義最大空間大小MAX_SIZE。表結構定義需要記錄兩項數據:

  • 順序表申請的存儲容量;
  • 順序表的長度,也就是表中存儲數據元素的個數;
#define MAX_SIZE    100
#define ERROR       0

typedef int ElemType;
typedef struct SeqList {
    ElemType data[MAX_SIZE];
    int length;
}SeqList, *pSeqList;

2、初始化

void InitSeqList(SeqList *seq)
{
    assert(nullptr != seq);
    memset(seq->data, 0, sizeof(ElemType)*MAX_SIZE);
    seq->length = 0;
}

3、插入元素

3.1 頭插

頭插,總是將新元素插入到順序表的首地址位置,需要將順序表的length個元素往後移動一個位置,時間複雜度O(n)。

void InsertFront(SeqList *seq, ElemType e)
{
    assert(nullptr != seq);
    if (seq->length == MAX_SIZE)
        printf("SeqList is FULL!\n");

    for (int i = seq->length; i > 0; --j)
        seq->data[i] = seq->data[i-1];
    seq->data[0] = e;
    seq->length++;
}
3.2 尾插

尾插,總是從順序表的末尾插入,沒有元素的移動,時間複雜度O(1)

void InsertBack(SeqList *seq, ElemType e)
{
    assert(nullptr != seq);
    if (seq->length == MAX_SIZE)
    {
         printf("SeqList is FULL!\n");
         return;
    }
    seq->data[seq->length++] = e;
}
3.3 任意指定位置插入

實現思路:

1.通過遍歷,找到數據元素要插入的位置

2.將要插入位置元素以及後續的元素整體向後移動一個位置

3.將元素放到騰出來的位置(即指定位置)

void Insert(SeqList seq, ElemType e, int pos)
{
    assert(nullptr != seq);
    if (seq->length == MAX_SIZE)
    {
        printf("SeqList is FULL!\n");
        return;
    }

    if (seq->length == 0)
    {
        printf("SeqList is Empty!\n");
        p = 1;
    }

    for (int i = seq->length; i >= pos; --i)
        seq->data[i] = seq->data[i-1];
    seq->data[p-1] = e;
    seq->length++;
}

4、刪除元素

4.1 頭刪

總是從順序表的首地址開始刪除元素,將其後的length-1個元素都需要向前移動一位置, 時間複雜度爲O(n)

void DeleteFront(SeqList *seq)
{
    assert(nullptr != seq);
    if (seq->length == 0)
    {
        printf("SeqList is Empty!\n");
        return;
    }

    for (int i = 0; i < seq->length; ++i)
    {
        seq->data[i] = seq->data[i+1];
    }
    seq->length--;
}
4.2 尾刪

尾刪,總是從順序表的尾部開始刪除元素,沒有元素的移動,時間複雜度爲O(1)

void DeleteBack(SeqList *seq)
{
    assert(nullptr != seq);
    if (seq->length == 0)
    {
        printf("SeqList is Empty!\n");
        return;
    }
    seq->length--;
}
4.3 任意指定位置刪除

只需找到目標元素,並將其後續所有元素整體前移 1 個位置即可。(後續元素整體前移一個位置,會直接將目標元素刪除,可間接實現刪除元素的目的)

void Delete(SeqList *seq, int pos)
{
    assert(nullptr != seq);
    if (seq->length == 0)
    {
        printf("SeqList is Empty!\n");
        return;
    }

    if (pos > seq->length || p < 1)
    {
        printf("ERROR!\n")
    }

    for (int i = pos-1; i < seq->length; ++i)
        seq->data[i] = seq->data[i+1];
    seq->length--;
}

5、查找

簡單粗暴的方法就是遍歷表數據,逐一進行比較判斷。實現如下:

int Find(SeqList *seq, ElemType value)
{
    assert(nullptr != seq);
    for (int i = 0; i < seq->length; ++i)
    {
        if (seq->data[i] == value)
            return i;
    }

    return -1;
}

除此之外,可以對錶數據先進行排序,然後再進行二分查找等方式。

6、排序(升序)

// 排序
void BubbleSort(SeqList *seq)
{
	assert(nullptr != seq);
	for (int i = 1; i < seq->length; ++i)
	{
		for (j = 0; j < seq->length - i; ++j)
		{
			if (seq->data[j] > seq->data[j+1])
			{
				int tmp = seq->data[j];
				seq->data[j] = seq->data[j+1];
				seq->data[j+1] = tmp;
			}
		}
	}
}

上述代碼是簡單等冒泡排序方式,當中還有可優化方案以及其它一些排序方法。

7、逆序反轉

void Reverse(SeqList *seq)
{
    assert(nullptr != seq);
    int start = 0;
    int end = seq->length-1;
    while (start != end)
    {
        int temp = seq->data[start];
        seq->data[start] = seq->data[end];
        seq->data[end] = temp;
        start++;
        end--;
    }
}

三、動態順序表

相比與靜態順序表,動態順序表支持擴容機制。即動態開闢一塊新的空間(一般爲原空間的兩倍),將原空間的數據拷貝到新的空間,然後讓array指針指向新的空間並且釋放舊空間。

1、結構定義

#define LIST_INT_SIZE   100  //線性表存儲空間的初始分配量
#define LIST_INCREMENT  100  //線性表存儲空間的分配增量

typedef int ElemType;
typedef struct
{
    ElemType *data;         //存儲空間基址
    int length;             //當前長度
    int capacity;           //當前分配的存儲容量(等於靜態順序表MAX_SIZE)
}SqList;

2、初始化和銷燬

//初始化
void Init(SeqList *seq)
{
	seq->capacity = LIST_INT_SIZE;
	seq->data = (ElemType*)malloc(sizeof(ElemType) * seq->capacity);
	assert(pSeq->parray);

	pSeq->length = 0;
}

//銷燬
void Destroy(SeqList *seq)
{
	free(seq->data);

	pSeq->capacity = 0;
	pSeq->data = nullptr;
	pSeq->length = 0;
}

3、擴容判斷

void IsNeedExpand(SeqList *seq)
{
	//擴容條件
	if (seq->length < seq->capacity)
		return;

	//擴容
	seq->capacity *= 2;

	//1.申請新空間
	ElemType *newData = (ElemType*)malloc(sizeof(ElemType) * seq->capacity);
	assert(newData);
	//2.數據搬移
	for (int i = 0; i < seq->size; i++)
    {
		newData[i] = seq->data[i];
	}
	// 3. 釋放舊空間,關聯新空間
	free(seq->data);
	seq->data = newData;
}

4、其它操作

插入、刪除、查找等操作基本和靜態順序表差不多,額外需要注意等是所有的插入都需要擴容判斷處理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章