資料結構 - 線性表 - 順序表

DevBobcorn發表於2024-03-31

前言

最近剛剛開始複習408資料結構,就想著把每個章節的程式碼部分彙總記錄一下,寫成一組文章,便於日後的複習和參考。

本系列整體的框架和課後習題參考了王道的複習指導。在本系列的每篇文章中,我會先把該章節所對應資料結構的基礎程式碼放上來,然後附上參考資料習題對應的題目程式碼。

線性表介紹

第一章的緒論沒有涉及到具體資料結構的程式編寫,我們在這裡就暫時跳過。從第二章中的線性表開始介紹。首先貼一下本章的整體框架:

線性表
|
|--順序儲存
|  |--順序表
|
|--鏈式儲存
|  |--單連結串列(指標實現)
|  |--雙連結串列(指標實現)
|  |--迴圈連結串列(指標實現)
|  |--靜態連結串列(陣列實現)

線性表(Linear List)是資料結構的一種,一個線性表是n個具有相同特性的資料元素的有限序列。線性表按照儲存結構可分為順序表和各種連結串列,本篇文章介紹其中順序表的實現與其操作演算法的編寫。

順序表程式碼實現

#include <stdio.h>
#include <iostream>
#include <limits.h>
using namespace std;

#define INIT_SIZE 10
#define DEBUG 1

typedef int ElemType;

typedef struct {
    ElemType* data;
    int length;
    int capacity;
} SeqList;

void init(SeqList &l) {
    l.data = (ElemType*) malloc(sizeof (ElemType) * INIT_SIZE);
    l.length = 0;
    l.capacity = INIT_SIZE;
}

void expand(SeqList &l, int target_size) {
    // Re-allocate a larger area for the list
    auto new_cap = l.capacity << 1;
    while (target_size > new_cap) {
        new_cap = new_cap << 1;
    }
    l.capacity = new_cap;

    if (DEBUG) {
        cout << "Expanding list to " << new_cap << " bytes" << endl;
    }

    auto new_data = (ElemType*) malloc(sizeof (ElemType) * new_cap);
    // Copy the array contents
    memcpy(new_data, l.data, sizeof (ElemType) * l.length);
    // Free previously used memory
    free(l.data);
    l.data = new_data;
}

void insert(SeqList &l, ElemType value, int pos) {
    if (pos > l.length || pos < 0) { // It is ok to insert at [l.length]
        cout << "Trying to insert at " << pos << " (Len: " << l.length << ")" << endl;
        
        return;
    }

    int new_len = l.length + 1;

    if (new_len > l.capacity) {
        expand(l, new_len);
    }

    for (int i = l.length - 1; i >= pos; i--) {
        l.data[i + 1] = l.data[i];
    }

    l.data[pos] = value;
    l.length = new_len;
}

void remove(SeqList &l, int pos) {
    if (pos >= l.length || pos < 0) { // It is NOT ok to remove at [l.length]
        cout << "Trying to remove at " << pos << " (Len: " << l.length << ")" << endl;
        
        return;
    }

    int new_len = l.length - 1;

    for (int i = pos; i < new_len; i++) {
        l.data[i] = l.data[i + 1];
    }

    l.length = new_len;
}

void print(SeqList &l) {
    for (int i = 0; i < l.length; i++) {
        cout << l.data[i] << ' ';
    }
    cout << endl;
}

void print_size(SeqList &l) {
    cout << "Len: " << l.length << ", Cap: "<< l.capacity << endl;
}

順序表綜合應用

[01] 從順序表中刪除具有最小值的元素(假設唯一)並由函式返回被刪元素的值。空出的位置由最後一個元素填補,若順序表為空,則顯示出錯資訊並退出。

// Chap 2. 01
ElemType remove_min(SeqList &l) {
    if (l.length == 0) {
        cout << "List is empty" << endl;
    }

    ElemType cur_min = INT_MAX;
    auto min_pos = -1;

    for (int i = 0; i < l.length; i++) {
        if (l.data[i] < cur_min) {
            min_pos = i;
            cur_min = l.data[i];
        }
    }

    l.data[min_pos] = l.data[l.length - 1];
    l.length -= 1;

    return cur_min;
}

[02] 設計一個高效演算法,將順序表L的所有元素逆置,要求演算法的空間複雜度為O(1)。

// Chap 2. 02
void rev(SeqList &l) {
    int i = 0;

    // 右移一個bit就是除以2
    // 奇數的話直接把0.5抹掉,正中間的元素不需要動
    while (i < (l.length >> 1)) {
        auto t = l.data[i];
        l.data[i] = l.data[l.length - 1 - i];
        l.data[l.length - 1 - i] = t;

        i++;
    }
}

[03] 對長度為n的順序表L,編寫一個時間複雜度為O(n)、空間複雜度為O(1)的演算法,該演算法刪除所有值為x的資料元素。

// Chap 2. 03
void remove_all(SeqList &l, ElemType value) {
    int rm_cnt = 0;

    for (int i = 0; i < l.length; i++) {
        if (l.data[i] == value) {
            rm_cnt++;
        } else {
            l.data[i - rm_cnt] = l.data[i];
        }
    }

    l.length -= rm_cnt;
}

相關文章