No.
2023-06-02
  • Jan
  • Feb
  • Mar
  • Apr
  • May
  • Jun
  • Jul
  • Aug
  • Sep
  • Oct
  • Nov
  • Dec
  • Sun
  • Mon
  • Tue
  • Wed
  • Thu
  • Fri
  • Sat
  • 28
  • 29
  • 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

抽象数据类型(ADT)-表

抽象数据类型(ADT)

抽象数据类型(Abstract Data Type)是特定的数据及在这些数据上的操作组成的集合,像表,栈,队列,图等和它们上面的操作都是抽象数据类型,这篇我们讲表。

形如 A0, A1, …, An-1 的数据结构叫表,这个表大小为 n。大小为 0 的表为空表。对于任一非空表,Ai-1 是 Ai 的前驱,Ai+1 是 Ai 的后继。

表实现

表可以用数组、链表、双链表、数组游标等方式实现,这里我们介绍其数组,链表和数组游标的实现方式。

链表接口

我们使用 #define ELEMENT_TYPE int 来定义表中需要操作的数据类型,使用 typedef int POSITION; 来定义数据的位置,进而实现以下接口:

//表数据结构
typedef struct Table_Node {
    ELEMENT_TYPE data;
    POSITION next;
}tableNode;

//表头数据结构
typedef struct Table_Head {
    tableNode* first;
    unsigned int capacity;
    unsigned int size;
}tableHead;

//通用接口
unsigned int getSize(tableHead* head);
void insert(tableHead* head, POSITION preNode, ELEMENT_TYPE data);
void delete(tableHead* head, ELEMENT_TYPE value);
POSITION find(tableHead* head, ELEMENT_TYPE value);
int initTableHead(tableHead* head, unsigned int capacity);
void disposeTable(tableHead* head);
ELEMENT_TYPE getValue(tableHead* head, unsigned int index);

其中可通用的接口有:

unsigned int getSize(tableHead* head)
{
    if (head == NULL)
        return 0;
    return head->size;
}

表的数组实现

表的数组实现像 c++ 中的 vector 一样,访问元素时速度比较快,但是插入和删除时速度较慢,因为它要将插入/删除的元素后的项全部进行移动。具体接口实现如下:

void insert(tableHead* head, POSITION preNode, ELEMENT_TYPE data)
{
    if (head == NULL || preNode >= (int)head->size || head->size >= head->capacity)
        return ;
    for (int i = head->size - 1; i > preNode && i >= 0; i--) {
        head->first[i + 1].data = head->first[i].data;
    }
    head->first[preNode + 1].data = data;
    head->size++;
}

void delete(tableHead* head, ELEMENT_TYPE value)
{
    if (head == NULL)
        return ;
    POSITION index = find(head, value);
    if (index == -1)
        return ;
    for (int i = index; i < head->size - 1; i++) {
        head->first[i].data = head->first[i + 1].data;
    }
    head->size--;
}

POSITION find(tableHead* head, ELEMENT_TYPE value)
{
    if (head == NULL || head->first == NULL)
        return -1;
    for (int i = 0; i < head->size; i++) {
        if (head->first[i].data == value)
            return i;
    }
    return -1;
}

int initTableHead(tableHead* head, unsigned int capacity)
{
    if (head == NULL)
        return -1;
    head->first = (tableNode*) malloc(sizeof(tableNode) * capacity);
    if (head->first == NULL)
        return -1;
    head->capacity = capacity;
    head->size = 0;
    return 0;
}

void disposeTable(tableHead* head)
{
    if (head == NULL || head->first == NULL)
        return ;
    free(head->first);
    head->capacity = 0;
    head->size = 0;
}

ELEMENT_TYPE getValue(tableHead* head, unsigned int index)
{
    if (head == NULL || index >= head->size)
        return 0;
    return head->first[index].data;
}

表的链表实现

链表实现时表的插入和删除操作就快很多了,因为它只需要修改两个指针即可。但是链表按索引访问的速度会比较慢,每次访问都要进行遍历操作。而且链表插入和删除时要进行内存的申请和释放。实现如下:

void insert(tableHead* head, POSITION preNode, ELEMENT_TYPE data)
{
    if (head == NULL)
        return ;
    tableNode* node = (tableNode*) malloc(sizeof(tableNode));
    if (node == NULL)
        return ;
    node->data = data;
    if (preNode == NULL) {
        node->next = head->first;
        head->first = node;
    } else {
        node->next = preNode->next;
        preNode->next = node;
    }
    head->size++;
}

void delete(tableHead* head, ELEMENT_TYPE value)
{
    if (head == NULL)
        return ;
    POSITION preNode = NULL;
    POSITION node = head->first;
    for (; node != NULL; node = node->next) {
        if (node->data == value)
            break;
        preNode = node;
    }
    if (node == NULL)
        return ;
    if (preNode == NULL) {
        head->first = node->next;
    } else {
        preNode->next = node->next;
    }
    free(node);
    head->size--;
}

POSITION find(tableHead* head, ELEMENT_TYPE value)
{
    for (POSITION node = head->first; node != NULL; node = node->next) {
        if (node->data == value)
            return node;
    }
    return NULL;
}

int initTableHead(tableHead* head, unsigned int capacity)
{
    if (head == NULL)
        return -1;
    head->first = NULL;
    head->size = 0;
    head->capacity = capacity;
    return 0;
    
}

void disposeTable(tableHead* head)
{
    if (head == NULL)
        return ;
    for (tableNode* node = head->first; node != NULL;) {
        tableNode* next = node->next;
        free(node);
        node = next;
    }
    head->first = NULL;
    head->size = 0;
}

ELEMENT_TYPE getValue(tableHead* head, unsigned int index)
{
    if (head == NULL || index >= head->size)
        return 0;
    unsigned int idx = 0;
    for (tableNode* node = head->first; node != NULL; node = node->next) {
        if (index == idx)
            return node->data;
        idx++;
    }
    return 0;
}

表的数组游标实现

表的数组游标实现就是在每个数组项后添加下一个元素的数组游标,相当于把数组当做链表的形式来操作。在初始化表时会申请一定数量的数组项,将它以链表形式存储在一个未使用元素链表上,当插入一个元素时,会从未使用元素链表上取一项,相当于链表实现中的 malloc,然后将其挂载到已使用的链表上。当删除时,将其从已使用链表上摘除,挂载到未使用链表上,相当于 free。具体实现:

void insert(tableHead* head, POSITION preNode, ELEMENT_TYPE data)
{
    if (head == NULL || head->freeHead == head->capacity)
        return ;
    int index = head->freeHead;
    head->freeHead = head->first[index].next;
    head->first[index].data = data;
    if (preNode >= head->capacity) {
        head->first[index].next = head->usedHead;
        head->usedHead = index;
    } else {
        head->first[index].next = head->first[preNode].next;
        head->first[preNode].next = index;
    }
    head->size++;
}

void delete(tableHead* head, ELEMENT_TYPE value)
{
    if (head == NULL || head->first == NULL)
        return ;
    int preIndex = head->capacity;
    int index = head->usedHead;
    for (; index < head->capacity;) {
        if (head->first[index].data == value)
            break;
        preIndex = index;
        index = head->first[preIndex].next;
    }
    if (index == head->capacity)
        return ;
    if (preIndex == head->capacity)
        head->usedHead = head->first[index].next;
    else
        head->first[preIndex].next = head->first[index].next;
    head->first[index].next = head->freeHead;
    head->freeHead = index;
    head->size--;
}

POSITION find(tableHead* head, ELEMENT_TYPE value)
{
    if (head == NULL || head->first == NULL)
        return -1;
    for (int i = head->usedHead; i < head->capacity;) {
        if (head->first[i].data == value)
            return i;
        i = head->first[i].next;
    }
    return head->capacity;
}

int initTableHead(tableHead* head, unsigned int capacity)
{
    if (head == NULL)
        return -1;
    head->first = (tableNode*) malloc(sizeof(tableNode) * capacity);
    if (head->first == NULL)
        return -1;
    head->capacity = capacity;
    for (int i = 0; i < capacity; i++) {
        head->first[i].next = i+1;
    }
    head->size = 0;
    head->usedHead = capacity;
    head->freeHead = 0;
    return 0;
}

void disposeTable(tableHead* head)
{
    if (head == NULL || head->first == NULL)
        return ;
    free(head->first);
    head->size = 0;
    head->capacity = 0;
}

ELEMENT_TYPE getValue(tableHead* head, unsigned int index)
{
    if (head == NULL || index >= head->size)
        return 0;
    unsigned int idx = 0;
    for (int i = head->usedHead; i < head->capacity;) {
        if (index == idx)
            return head->first[i].data;
        idx++;
        i = head->first[i].next;
    }
    return 0;
}

参考

<数据结构与算法分析-C语言实现> P3