静态链表的C++语言描述实现模板

5 阅读25分钟

前言

数据结构中静态链表的C++语言描述实现模板,有详细的步骤解析及使用示例。


代码仓库


static_link_list.cpp

// 头文件————————————————————
#include <iostream>
using namespace std;

// 自定义数据类型————————————————————
typedef int ELEM_TYPE; // 数据的(数据)类型 依据数据的实际类型定义

// 结构体
// 定义
// 结点
typedef struct LNode
{
    ELEM_TYPE data;     // 数据
    struct LNode *next; // 指向直接后继结点的指针
} LNode;
// struct LNode:结构体数据类型的名称
// 如创建一个结点的语句:struct LNode node;
// 若结构体数据类型内存在指向该结构体的指针数据类型,则必须完整命名结构体数据类型的名称
// 如结点结构体LNode内存在指向该结构体的指针数据类型next,则命名语句为:typedef struct LNode而不是typedef struct
// LNode:typedef给结构体数据类型起的别名,可简化语句
// 如创建一个结点的语句:LNode node;
// 结构体数据类型的名称和别名尽量一致,以方便记忆、使用

// 函数声明————————————————————
void use_example();                                                  // 使用示例
void create_list_head(LNode *&head, ELEM_TYPE value[], int count);   // 创建  头插法
void create_list_tail(LNode *&head, ELEM_TYPE value[], int count);   // 创建  尾插法
LNode *find_node(LNode *head, ELEM_TYPE elem);                       // 按值查找
LNode *get_node(LNode *head, int i);                                 // 按序查找
void insert_node(LNode *&head, int posi, LNode *node);               // 插入  前插
void insert_node_exten(LNode *&head, int posi, LNode *node);         // 插入  扩展:前插转后插
void delele_node(LNode *&head, int posi);                            // 删除  前删
void delele_node_exten(LNode *&head, int posi);                      // 删除  前删转后删
int get_length(LNode *head);                                         // 获取表长
void clear_list(LNode *&head);                                       // 清空
void merge_list_increase(LNode *head1, LNode *head2, LNode *&head3); // 合并两递增表成递增表
void merge_list_reduce(LNode *head1, LNode *head2, LNode *&head3);   // 合并两递增表成递减表

// 主函数————————————————————
int main()
{
    use_example(); // 使用示例

    return 0;
}

// 函数定义————————————————————
// 使用示例
void use_example()
{
    // 创建
    struct LNode *head; // 头指针,指向头结点或第一个结点
    // 或LNode *head;
    // 一般头指针变量名为链表表名    这里用head见明知意,表示是头指针

    ELEM_TYPE value[3]; // 数据数组
    value[0] = 100;
    value[1] = 200;
    value[2] = 300;

    create_list_tail(head, value, 3); // 创建    尾插法  元素在表中的排列:100,200,300

    // 按值查找
    LNode *node; // 结点指针

    node = find_node(head, 100); // 按值查找

    cout << node->data << endl; // 输出:100

    // 按序查找
    node = get_node(head, 2); // 按序查找

    cout << node->data << endl; // 输出:200

    // 插入  前插
    node = (LNode *)malloc(sizeof(LNode)); // 创建新结点   结点指针指向新结点
    node->data = 400;                      // 初始化新结点的数据域

    insert_node(head, 3, node); // 插入 前插 元素在表中的排列:100,200,400,300

    // 插入  扩展:前插转后插
    node = (LNode *)malloc(sizeof(LNode)); // 创建新结点   结点指针指向新结点
    node->data = 500;                      // 初始化新结点的数据域

    insert_node_exten(head, 3, node); // 插入    扩展:前插转后插    元素在表中的排列:100,200,500,400,300

    LNode *p;       // 移动结点指针
    p = head->next; // 初始化移动结点指针 指向第一个结点(头结点的下一个结点)

    while (p != NULL) // 遍历
    {
        cout << p->data << endl; // 输出:100,200,500,400,300

        p = p->next; // 移动结点指针
    }

    // 删除  前删
    delele_node(head, 3); // 删除  前删  元素在表中的排列:100,200,400,300

    // 删除  前删转后删
    delele_node_exten(head, 3); // 删除  前删转后删  元素在表中的排列:100,200,300

    p = head->next; // 初始化移动结点指针 指向第一个结点(头结点的下一个结点)

    while (p != NULL) // 遍历
    {
        cout << p->data << endl; // 输出:100,200,300

        p = p->next; // 移动结点指针
    }

    // 获取表长
    int list_length = 0; // 表长

    list_length = get_length(head);

    cout << list_length << endl; // 输出:3

    // 清空
    clear_list(head);

    list_length = get_length(head);

    cout << list_length << endl; // 输出:0

    // 合并两递增表成递增表
    struct LNode *head1; // 递增表1的头指针,指向头结点或第一个结点

    ELEM_TYPE value1[3]; // 数据数组1
    value1[0] = 100;
    value1[1] = 300;
    value1[2] = 500;

    create_list_tail(head1, value1, 3); // 创建    尾插法  元素在表中的排列:100,300,500

    struct LNode *head2; // 递增表2的头指针,指向头结点或第一个结点

    ELEM_TYPE value2[3]; // 数据数组2
    value2[0] = 200;
    value2[1] = 400;
    value2[2] = 600;

    create_list_tail(head2, value2, 3); // 创建    尾插法  元素在表中的排列:200,400,600

    struct LNode *head3; // 新递增表的头指针,指向头结点或第一个结点

    merge_list_increase(head1, head2, head3); // 合并两递增表成递增表

    p = head3->next;  // 移动结点指针    指向新递增表的第一个结点
    while (p != NULL) // 遍历
    {
        cout << p->data << endl; // 输出:100,200,300,400,500,600

        p = p->next; // 移动结点指针
    }

    // 合并两递增表成递减少表
    create_list_tail(head1, value1, 3); // 创建    尾插法  元素在表中的排列:100,300,500
    create_list_tail(head2, value2, 3); // 创建    尾插法  元素在表中的排列:200,400,600
    clear_list(head3);                  // 清空

    merge_list_reduce(head1, head2, head3); // 合并两递增表成递减表

    p = head3->next;  // 移动结点指针    指向新递减表的第一个结点
    while (p != NULL) // 遍历
    {
        cout << p->data << endl; // 输出:600,500,400,300,200,100

        p = p->next; // 移动结点指针
    }

    return;
}

// 函数定义————————————————————
// 创建  头插法
// 时间复杂度:O(n)
// 注意:参数使用*&,指向指针的引用数据类型
// 若只使用*,指针数据类型,在函数体中并未进行解引用调用修改*head的值,使用malloc()修改的是head的值
// 所以是值拷贝,函数返回后数据丢失。地址拷贝需要引用数据类型的配合
void create_list_head(LNode *&head, ELEM_TYPE value[], int count) // 参数:头指针,数据数组,数据数量
{
    head = (LNode *)malloc(sizeof(LNode)); // 创建头结点,头指针指向头结点
    head->next = NULL;                     // 初始化头结点的指针域

    LNode *node; // 新结点指针

    for (int i = 0; i < count; i++) // 循环创建链表
    {
        node = (LNode *)malloc(sizeof(LNode)); // 创建新结点,新结点指针指向新结点
        node->data = value[i];                 // 初始化新结点的数据域

        node->next = head->next; // 初始化新结点的指针域
        head->next = node;
        // 插入到链表的头结点和前一新结点之间
        // 链表描述:头结点->当前新结点->前一新结点->...->空
        // 若无头结点:head=node;
    }

    return;
}

// 创建  尾插法
// 时间复杂度:O(n)
void create_list_tail(LNode *&head, ELEM_TYPE value[], int count) // 参数:头指针,数据数组,数据数量
{
    head = (LNode *)malloc(sizeof(LNode)); // 创建头结点,头指针指向头结点
    head->next = NULL;                     // 初始化头结点的指针域

    LNode *tail; // 尾指针,尾指针始终指向最后一个结点
    tail = head; // 初始化尾指针  指向最后一个结点(头结点)

    LNode *node; // 新结点指针

    for (int i = 0; i < count; i++) // 循环创建链表
    {
        node = (LNode *)malloc(sizeof(LNode)); // 创建新结点,新结点指针指向新结点
        node->data = value[i];                 // 初始化新结点的数据域

        tail->next = node; // 插入到链表的头结点和前一新结点之间
        tail = tail->next; // 移动尾指针
        // 或者tail=node;
    }

    tail->next = NULL; // 尾指针指向空   创建完成
    // 链表描述:头结点->...->前一新结点->当前新结点->空

    return;
}

// 按值查找
// 时间复杂度:O(n)
LNode *find_node(LNode *head, ELEM_TYPE elem) // 参数:头指针,元素  返回值:结点指针
{
    LNode *p;       // 移动结点指针
    p = head->next; // 初始化移动结点指针 指向第一个结点(头结点的下一个结点)

    while (p != NULL) // 遍历
    {
        if (p->data == elem) // 匹配
        {
            break; // 退出查找
        }

        p = p->next; // 若不匹配,移动结点指针
    }

    return p; // 若找到返回结点指针,否则返回空指针
}

// 按序查找
// 时间复杂度:O(n)
LNode *get_node(LNode *head, int i) // 参数:头指针,元素查找的位置   返回值:结点指针
{
    LNode *p;       // 移动结点指针
    p = head->next; // 初始化移动结点指针 指向第一个结点(头结点的下一个结点)

    if (i < 0) // 不存在负数的位置
    {
        return NULL; // 返回空指针
    }

    if (i == 0) // 第0个位置是头结点
    {
        return head; // 返回头指针
    }

    for (int j = 1; j < i; j++) // 遍历
    {

        if (p != NULL) // 不匹配
        {
            p = p->next; // 移动结点指针
        }
        else // 匹配
        {
            break; // 退出查找
        }
    }

    return p; // 若找到返回结点指针,否则返回空指针
}

// 插入  前插
// 时间复杂度:O(n)
// 因为需遍历找到元素插入位置之前的结点
void insert_node(LNode *&head, int posi, LNode *node) // 参数:头指针,元素插入的位置,结点
{
    LNode *p;                     // 移动结点指针
    p = get_node(head, posi - 1); // 初始化移动结点指针 指向第posi-1个位置的结点

    node->next = p->next; // 插入 在第posi-1个位置的结点之后,占据第posi个位置,在原第posi个位置的结点之前
    p->next = node;

    return;
}

// 插入  扩展:前插转后插
// 时间复杂度:O(1)
// 直接在元素插入位置之后的结点插入,再交换结点数据
void insert_node_exten(LNode *&head, int posi, LNode *node) // 参数:头指针,元素插入的位置,结点
{
    LNode *p;                 // 移动结点指针
    p = get_node(head, posi); // 初始化移动结点指针 指向第posi个位置的结点

    node->next = p->next; // 插入 在第posi个位置的结点之后
    p->next = node;

    ELEM_TYPE temp; // 临时数据

    temp = p->data; // 交换第posi个位置的结点和新结点的数据
    p->data = node->data;
    node->data = temp;

    return;
}

// 删除  前删
// 时间复杂度:O(n)
// 因为需遍历找到元素删除位置之前的结点
void delele_node(LNode *&head, int posi) // 参数:头指针,元素删除的位置
{
    LNode *p;                     // 移动结点指针
    p = get_node(head, posi - 1); // 初始化移动结点指针 指向第posi-1个位置的结点

    LNode *q;    // 被删结点指针
    q = p->next; // 初始化被删结点指针 指向第posi个位置的结点

    p->next = q->next; // 修改指针指向   第posi-1个位置的结点的指针指向第posi+1个位置的结点
    // 或者p->next=p->next->next;

    free(q); // 删除被删结点指针指向的结点,释放内存
}

// 删除  前删转后删
// 时间复杂度:O(1)
// 交换结点数据,直接删除元素删除位置之后的结点
void delele_node_exten(LNode *&head, int posi) // 参数:头指针,元素删除的位置
{
    LNode *p;                 // 移动结点指针
    p = get_node(head, posi); // 初始化移动结点指针 指向第posi个位置的结点

    LNode *q;    // 被删结点指针
    q = p->next; // 初始化被删结点指针 指向第posi+1个位置的结点

    p->data = q->data; // 交换第posi个位置和第posi+1个位置结点的数据 此时需删除的是第posi+1个位置的结点
    // 或者p->data=p->next->data
    p->next = q->next; // 修改指针指向   第posi个位置的结点的指针指向第posi+2个位置的结点
    // 或者p->next=p->next->next;

    free(q); // 删除被删结点指针指向的结点,释放内存
}

// 获取表长
int get_length(LNode *head) // 参数:头指针  返回值:表长
{
    LNode *p;       // 移动结点指针
    p = head->next; // 初始化移动结点指针 指向第一个结点(头结点的下一个结点)

    int length = 0; // 表长

    while (p != NULL) // 遍历 有结点
    {
        length++;    // 表长递增
        p = p->next; // 移动指针
    }

    return length; // 返回表长
}

// 清空
void clear_list(LNode *&head) // 参数:头指针
{
    LNode *p;       // 被删结点指针
    p = head->next; // 初始化被删结点指针 指向第一个结点(头结点的下一个结点)

    LNode *q; // 记录结点指针 指向被删结点的下一个结点

    while (p != NULL) // 遍历    有结点
    {
        q = p->next; // 记录被删结点的下一个结点

        free(p); // 删除被删结点指针指向的结点,释放内存

        p = q; // 移动指针    指向下一个被删结点
    }

    head->next = NULL; // 头结点的指针域置空 当退出遍历时已无数据结点
}

// 合并两递增表成递增表
// 思路:遍历两递增表,比较结点的数值域数据,使用尾插法创建新递增表
// 参数:递增表1的头指针,递增表2的头指针,新递增表的头指针
void merge_list_increase(LNode *head1, LNode *head2, LNode *&head3)
{
    LNode *p;        // 移动结点指针1
    p = head1->next; // 初始化移动结点指针1 指向递增表1的第一个结点(头结点的下一个结点)

    LNode *q;        // 移动结点指针2
    q = head2->next; // 初始化移动结点指针2 指向递增表2的第一个结点(头结点的下一个结点)
    free(head2);     // 释放递增表2的头结点  因为已用移动结点指针2记录递增表2的第一个结点,数据不会丢失

    head3 = head1;      // 初始化新递增表的头指针 指向递增表1的头结点,用递增表1的头结点作为新递增表的头结点
    head3->next = NULL; // 初始化新递增表头结点的指针域,置空  因为已用移动结点指针1记录递增表1的第一个结点,数据不会丢失

    LNode *tail;  // 新递增表的尾指针    始终指向新递增表的终端结点
    tail = head3; // 初始化新递增表的尾指针  初始时新递增表为空,尾指针指向头结点

    while ((p != NULL) && (q != NULL)) // 遍历两递增表
    {
        if (p->data <= q->data) // 比较结点的数值域数据
        {
            tail->next = p; // 将数据域数值小的结点插入新递增表表尾

            p = p->next;       // 递增表1的当前结点已插入新递增表,更新移动结点指针1
            tail = tail->next; // 更新新递增表的尾指针  始终指向新递增表的终端结点
        }
        else // 同理
        {
            tail->next = q;

            q = q->next;
            tail = tail->next;
        }
    }

    tail->next = NULL;
    // 可以没有,因为最后还要将未插入的结点插入新递增表表尾,未插入结点的终端结点指针域为空

    if (p != NULL) // 若递增表2插入完成,递增表1还有未插入结点,则将剩余结点插入新递增表尾部
    {
        tail->next = p;
    }
    if (q != NULL) // 若递增表1合并完成,递增表2还有未插入结点,则将剩余结点插入新递增表尾部
    {
        tail->next = q;
    }

    return;
}

// 合并两递增表成递减表
// 思路:遍历两递增表,比较结点的数值域数据,使用头插法创建新递减表
// 参数:递增表1的头指针,递增表2的头指针,新递增表的头指针
void merge_list_reduce(LNode *head1, LNode *head2, LNode *&head3)
{
    LNode *p;        // 移动结点指针1
    p = head1->next; // 初始化移动结点指针1 指向递增表1的第一个结点(头结点的下一个结点)

    LNode *q;        // 移动结点指针2
    q = head2->next; // 初始化移动结点指针2 指向递增表2的第一个结点(头结点的下一个结点)
    free(head2);     // 释放递增表2的头结点  因为已用移动结点指针2记录递增表2的第一个结点,数据不会丢失

    head3 = head1;      // 初始化新递减表的头指针 指向递增表1的头结点,用递增表1的头结点作为新递减表的头结点
    head3->next = NULL; // 初始化新递减表头结点的指针域  因为已用移动结点指针1记录递增表1的第一个结点,数据不会丢失

    LNode *temp; // 临时结点指针 指向当前要插入新递减表表头的结点

    while ((p != NULL) && (q != NULL)) // 遍历两递增表
    {
        if (p->data <= q->data) // 比较结点的数值域数据
        {
            temp = p;    // 将临时结点指针指向数据域数值小的结点
            p = p->next; // 递增表1的当前结点已被临时结点指针记录,更新移动结点指针1

            temp->next = head3->next; // 将临时结点指针指向头结点的下一个结点
            head3->next = temp;       // 将头指针指向当前要插入新递减表表头的结点
        }
        else // 同理
        {
            temp = q;
            q = q->next;

            temp->next = head3->next;
            head3->next = temp;
        }
    }

    if (p != NULL) // 若递增表2插入完成,递增表1还有未插入结点,则取剩余结点一个个插入新递减表表头
    {
        temp = p;
        p = p->next;

        temp->next = head3->next;
        head3->next = temp;
    }
    if (q != NULL) // 若递增表1插入完成,递增表2还有未插入结点,则取剩余结点一个个插入新递减表表头
    {
        temp = q;
        q = q->next;

        temp->next = head3->next;
        head3->next = temp;
    }

    return;
}

sLinkList.cpp

//头文件————————————————————
#include <iostream>
using namespace std;

//宏————————————————————
#define MAX_SIZE 100 //最大大小
//为防止插入数据时溢出,通常数组建立得大一些

//自定义数据类型————————————————————
typedef int ELEM_TYPE; //数据的(数据)类型 依据数据的实际类型定义

//结构体
//定义
//静态链表/数组
typedef struct
{
    ELEM_TYPE data;    //数据
    int cur;           //游标 存储直接后继位置的数组下标
} SLinkList[MAX_SIZE]; //结构体数组
//数组中存在未被使用和已使用的位置/结点
//约定:
//数组的第一个和最后一个位置不存数据
//数组的第一个位置(数组下标为0)的游标存储未被使用位置中第一个位置的数组下标
//当数组位置的游标无直接后继位置时,存储数组下标0
//数组的最后一个位置(数组下标为MAX_SIZE-1)存储已被使用位置中第一个位置的数组下标  类比单链表的头结点
//数组为空时,数组的最后一个位置(数组下标为MAX_SIZE-1)存储数组下标0

//函数声明————————————————————
void use_example();                                               //使用示例
void init_list(SLinkList &sLinkList);                             //初始化
int get_length(SLinkList sLinkList);                              //获取表长
int allot(SLinkList &sLinkList);                                  //分配
bool insert_list(SLinkList &sLinkList, int posi, ELEM_TYPE elem); //插入
void release(SLinkList &sLinkList, int del_cur);                  //释放
bool delete_list(SLinkList &sLinkList, int posi);                 //删除
void traverse(SLinkList sLinkList);                               //遍历

//主函数————————————————————
int main()
{
    use_example(); //使用示例

    return 0;
}

//函数定义————————————————————
//使用示例
void use_example()
{
    //创建
    SLinkList sLinkList;

    //初始化
    init_list(sLinkList);

    //插入  默认成功,未接收返回值
    insert_list(sLinkList, 1, 300);
    insert_list(sLinkList, 1, 200);
    insert_list(sLinkList, 1, 100);
    //元素在数组中排列:300,200,100
    //元素在链表中排列:100,200,300

    for (int i = 1; i < 4; i++)
    {
        cout << sLinkList[i].data << endl; //输出:300,200,100
    }

    int move_cur; //移动游标

    move_cur = MAX_SIZE - 1;

    while (1) //遍历
    {
        move_cur = sLinkList[move_cur].cur; //移动移动游标

        cout << sLinkList[move_cur].data << endl; //输出:100,200,300

        if (sLinkList[move_cur].cur == 0) //移动游标到数组的最后一个位置,输出数据后退出
        {
            break;
        }
    }

    //获取表长
    int length; //表长

    length = get_length(sLinkList); //获取表长

    cout << length << endl; //输出:3

    //删除  默认成功,未接收返回值
    delete_list(sLinkList, 2); //删除

    //注意:
    //删除只是修改元素删除位置的前一个位置的游标存储的数组下标,并将元素删除位置置为未被使用位置
    //是在逻辑上删除,但在物理上元素删除位置仍存储原数据
    //元素在数组中排列:300,200,100
    //元素在链表中排列:100,300

    for (int i = 1; i < 4; i++)
    {
        cout << sLinkList[i].data << endl; //输出:300,200,100
    }

    //遍历
    traverse(sLinkList); //遍历 输出:100,300

    return;
}

//函数定义————————————————————
//初始化
void init_list(SLinkList &sLinkList) //参数:数组(还未形成静态链表)
{
    for (int i = 0; i < MAX_SIZE; i++) //遍历各数组位置
    {
        sLinkList[i].cur = i + 1; //游标存储数组下一位置的数组下标
    }
    //由代码逻辑,初始化后若直接使用插入操作,一般按照数组下标1-MAX_SIZE-1的位置依次插入元素

    sLinkList[MAX_SIZE - 1].cur = 0;
    //遍历后数组最后一个位置的游标存储:sLinkList[MAX_SIZE-1].cur = MAX_SIZE
    //约定:数组为空时,数组的最后一个位置(数组下标为MAX_SIZE-1)存储数组下标0
    //修改数组最后一个位置的游标存储

    return;
}

//获取表长
int get_length(SLinkList sLinkList) //参数:数组    返回值:表长
{
    int length = 0; //表长
    int move_cur;   //移动游标   当前数组位置的数组下标

    move_cur = sLinkList[MAX_SIZE - 1].cur; //初始化移动游标    存储数组已被使用位置中第一个位置的数组下标
    //约定:数组的最后一个位置(数组下标为MAX_SIZE-1)存储已被使用位置中第一个位置的数组下标  类比单链表的头结点

    while (move_cur != 0) //遍历
    {
        move_cur = sLinkList[move_cur].cur; //更新移动游标   存储数组已被使用位置中下一个位置的数组下标

        length++; //表长+1
    }

    return length; //返回表长
}

//静态链表需要模拟动态链表的存储空间分配,需要时申请
//思路:将数组中未被使用的位置链成链表,需要时取第一个位置,记录游标存储下一个未被使用的位置
//分配
int allot(SLinkList &sLinkList) //参数:数组    返回值:数组未被使用位置中第一个位置的数组下标
{
    int posi; //数组未被使用位置中第一个位置的数组下标

    posi = sLinkList[0].cur; //获取数组未被使用位置中第一个位置的数组下标

    //约定:数组的第一个位置(数组下标为0)的游标存储未被使用位置中第一个位置的数组下标
    //约定:当数组位置的游标无直接后继位置时,存储数组下标0
    //从数组的第一个位置(数组下标为0)的游标获取未被使用位置中第一个位置的数组下标
    //若存在数组未被使用位置中第一个位置,值>0,不存在,值==0

    //数组未被使用位置中第一个位置的游标存储数组未被使用位置中第二个位置或者0(已不存在数组未被使用位置)
    //修改数组的第一个位置(数组下标为0)的游标存储下一个数组未被使用位置的数组下标
    if (sLinkList[0].cur != 0)
    {
        sLinkList[0].cur = sLinkList[posi].cur;
    }

    return posi; //返回数组未被使用位置中第一个位置的数组下标
}

//插入
//参数:数组,元素插入的位置,元素 返回值:操作失败返回false,成功返回true
bool insert_list(SLinkList &sLinkList, int posi, ELEM_TYPE elem)
{
    int length; //表长

    length = get_length(sLinkList); //获取表长

    if (posi < 1 || posi > length + 1) //元素插入的位置不合法
    {
        return false;
    }
    //元素的合法插入位置为数组已被使用位置的第一个位置到最后一个位置的后一个位置

    int unused_cur; //数组未被使用位置中第一个位置的数组下标

    unused_cur = allot(sLinkList); //获取数组未被使用位置中第一个位置的数组下标

    //约定:数组的第一个位置(数组下标为0)的游标存储未被使用位置中第一个位置的数组下标
    //约定:当数组位置的游标无直接后继位置时,存储数组下标0
    // allot()从数组的第一个位置(数组下标为0)的游标获取未被使用位置中第一个位置的数组下标
    //若存在数组未被使用位置中第一个位置,值>0,不存在,值==0
    if (unused_cur != 0)
    {
        sLinkList[unused_cur].data = elem; //赋值数据域

        //插入需要修改数组位置的游标域  类比链表的插入
        int move_cur; //移动游标    当前数组位置的数组下标

        //需要查找当前元素插入位置的前一个位置和后一个位置
        //约定:数组的最后一个位置(数组下标为MAX_SIZE-1)存储已被使用位置中第一个位置的数组下标  类比单链表的头结点
        //从数组的最后一个位置(数组下标为MAX_SIZE-1)按照游标存储的数组下标依次遍历1-posi-1个位置
        move_cur = MAX_SIZE - 1;

        for (int i = 1; i <= posi - 1; i++) //遍历  查找当前元素插入位置的前一个位置
        {
            move_cur = sLinkList[move_cur].cur; //移动移动游标
        }
        //遍历后,move_cur是元素插入位置的前一个位置的数组下标

        sLinkList[unused_cur].cur = sLinkList[move_cur].cur;
        // 1.链接当前元素插入位置的后一个位置
        //当前元素插入位置的游标存储的是当前元素插入位置的前一个位置的游标存储的数组下标
        //类比s->next=p->next;

        sLinkList[move_cur].cur = unused_cur;
        // 2.链接当前元素插入位置的前一个位置
        //当前元素插入位置的前一个位置的游标存储的是当前元素插入位置的的数组下标
        //类比p->next=s;

        return true;
    }

    return false;
}

//释放
void release(SLinkList &sLinkList, int del_cur) //参数:数组,元素删除的位置
{
    //约定:数组的第一个位置(数组下标为0)的游标存储未被使用位置中第一个位置的数组下标
    //思路:头插法
    sLinkList[del_cur].cur = sLinkList[0].cur; //元素删除位置的游标记录数组未使用位置中第一个位置的数组下标

    sLinkList[0].cur = del_cur; //数组的第一个位置(数组下标为0)的游标记录元素删除位置的数组下标

    return;
}

//删除
//注意:
//删除只是修改元素删除位置的前一个位置的游标存储的数组下标,并将元素删除位置置为未被使用位置
//是在逻辑上删除,但在物理上元素删除位置仍存储原数据
//参数:数组,元素删除的位置    返回值:操作失败返回false,成功返回true
bool delete_list(SLinkList &sLinkList, int posi)
{
    int length; //表长

    length = get_length(sLinkList); //获取表长

    if (posi < 1 || posi > length) //元素删除的位置不合法
    {
        return false;
    }
    //元素的合法删除位置为数组已被使用位置的第一个位置到最后一个位置

    //删除需要修改数组位置的游标域  类比链表的删除
    int move_cur; //移动游标    当前数组位置的数组下标

    //需要查找元素删除位置的前一个位置和后一个位置
    //约定:数组的最后一个位置(数组下标为MAX_SIZE-1)存储已被使用位置中第一个位置的数组下标  类比单链表的头结点
    //从数组的最后一个位置(数组下标为MAX_SIZE-1)按照游标存储的数组下标依次遍历1-posi-1个位置
    move_cur = MAX_SIZE - 1;

    for (int i = 1; i <= posi - 1; i++) //遍历  查找元素删除位置的前一个位置
    {
        move_cur = sLinkList[move_cur].cur; //移动移动游标
    }
    //遍历后,move_cur是元素删除位置的前一个位置的数组下标

    int del_cur; //元素删除位置的数组下标

    del_cur = sLinkList[move_cur].cur;
    //使用元素删除位置的前一个位置的数组下标定位元素删除位置的前一个位置
    //使用元素删除位置的游标定位元素删除位置的数组下标

    sLinkList[move_cur].cur = sLinkList[del_cur].cur;
    //链接当前元素删除位置的前一个位置和后一个位置
    //使用元素删除位置的前一个位置的数组下标定位元素删除位置的前一个位置
    //使用元素删除位置的的数组下标定位元素删除位置
    //使用元素删除位置的的游标定位元素删除位置的后一个位置
    //使用元素删除位置的前一个位置的游标记录元素删除位置的后一个位置
    //类比:p->next=p->next->next;

    release(sLinkList, del_cur); //释放空间

    return true;
}

//遍历
void traverse(SLinkList sLinkList) //参数:数组
{
    int move_cur; //移动游标

    move_cur = MAX_SIZE - 1;

    while (1) //遍历
    {
        move_cur = sLinkList[move_cur].cur; //移动移动游标

        cout << sLinkList[move_cur].data << endl; //输出:100,200,300

        if (sLinkList[move_cur].cur == 0) //移动游标到数组的最后一个位置,输出数据后退出
        {
            break;
        }
    }

    return;
}

总结

  • 静态链表是为给没有指针的高级语言设计的一种实现单链表的方法
  • 静态链表的操作在链表的基础上多了一层抽象、不易理解。作者也极难选择用词,以能够将其含义通俗易懂的表述出来,还望理解
  • 许多参考资料中往往只有对静态链表定义的代码示例,少有对操作的代码示例。静态链表少有使用,但其设计思想是巧妙的。我们需理解学习,扩展思维的同时也以备不时之需
  • 作者使用规范的变量命名、提供详细的步骤解析及使用示例,应用C++语言将其整合成模板,以帮助理解记忆。

作者的话

  • 作者:夜悊
  • 版权所有,转载请注明出处,谢谢~
  • 如果文章对你有帮助,请点个赞或加个粉丝吧,你的支持就是作者的动力~
  • 文章在描述时有疑惑的地方,请留言,定会一一耐心讨论、解答
  • 文章在认识上有错误的地方, 敬请批评指正
  • 望读者们都能有所收获

参考资料

《大话数据结构》作者:程杰