嵌入式数据结构—学习笔记 顺序表

本文知识点来源:

01线性表之顺序表_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV1ee4y1q77b?p=3&vd_source=01c0a0b4e215da5cc9a422b60e2ca405

一.线性表相关概念

线性表:线性表是包含若干数据元素的一个线性序列

线性表L可用二元组形式描述:L= (D,R)

1.1 线性表特征:

  1. 有序性:线性表中的数据元素按照一定的顺序排列,每个元素都有一个前驱(除第一个元素)和/或后继(除最后一个元素)。

  2. 有限性:线性表由有限个数据元素组成,这意味着可以确切地计算出表中元素的数量。

  3. 数据类型一致性:线性表中所有元素都属于同一数据类型,或者说是同质的。

  4. 灵活性:线性表可以为空(即不含任何元素),也可以根据需要动态地增加或删除元素(在动态数组或链表等实现方式中)。

  5. 访问方式:线性表支持两种基本的访问操作,即按位置(索引)访问和顺序访问。具体实现时,数组支持快速的随机访问,而链表更适合顺序访问。

  6. 操作集:常见的线性表操作包括但不限于初始化、判断是否为空、查找、插入、删除、遍历等。

  7. 两种主要实现形式:线性表可以通过数组或链表来实现。数组实现的线性表称为静态数组或顺序表,特点是随机访问快但插入删除效率较低;链表实现的线性表则便于插入删除操作,但随机访问效率相对较低。

二.顺序表相关函数实现

2.1定义顺序表

// 定义一种整型数据类型,命名为data_t,方便后续使用及表示数据元素
typedef int data_t;

// 定义常量N,其值为128,作为静态数组的默认大小
#define N 128

// 定义一个结构体sqlist,包含两个成员:
// data:一个大小为N的数据元素数组,数组元素类型为data_t
// last:记录当前线性表中最后一个元素的实际位置(索引),初始化时应为-1表示空表
typedef struct{
    data_t data[N];
    int last;
} sqlist;

// 定义sqlink为指向sqlist结构体的指针类型,便于通过指针操作线性表
typedef sqlist *sqlink;

2.2 初始化顺序表

/**
 * 初始化一个sqlink列表。
 *
 * 该函数动态分配内存以创建一个sqlink列表,并对其进行初始化。
 * 列表初始化后,最后一个元素的索引被设置为-1,表示列表为空。
 *
 * @return 初始化后的sqlink列表指针。如果内存分配失败,返回NULL。
 */
sqlink list_init()
{
    sqlink L;
    // 为sqlink列表分配内存
    L = (sqlink)malloc(sizeof(sqlist));
    // 检查内存分配是否成功
    if (L = NULL)
    {
        printf("list malloc error\n");
        return NULL;
    }
    // 清零列表内存,确保所有字段都被初始化为0
    memset(L, 0, sizeof(sqlist));
    // 初始化列表的last字段为-1,表示列表为空
    L->last = -1;
    return L;
}

该函数用于初始化一个sqlink类型的链表。首先通过malloc函数为链表结构体sqlist分配内存空间,并将指针赋值给L。然后检查内存分配是否成功,如果失败则输出错误信息并返回NULL。接着使用memset函数将链表内存清零,确保所有字段都被初始化为0。最后将链表的last字段初始化为-1,表示链表为空,然后返回链表指针L

2.3 清空顺序表

/**
 * 清空链表
 *
 * 本函数用于清空一个链表,将链表的所有元素置为空,并将链表的尾指针重置为-1。
 * 如果链表为空,则函数会打印提示信息并返回-1;否则,函数会成功清空链表并返回0。
 *
 * @param L 链表的头指针
 * @return 如果链表为空,则返回-1;否则返回0。
 */
int list_clear(sqlink L)
{
    /* 检查链表是否为空 */
    if (L == NULL)
    {
        /* 如果链表为空,则打印提示信息并返回错误码 */
        printf("list is null\n");
        return -1;
    }
    /* 将链表头部结构体的所有字节置为0,以清空链表 */
    memset(L, 0, sizeof(sqlist));
    /* 将链表的尾指针重置为-1,表示链表为空 */
    L->last = -1;

    /* 清空操作成功,返回0 */
    return 0;
}

该函数用于清空一个链表。首先检查链表是否为空,如果为空则打印提示信息并返回错误码-1。如果链表不为空,则将链表头部结构体的所有字节置为0,以清空链表,并将链表的尾指针重置为-1,表示链表为空。最后返回0,表示清空操作成功。

2.4 判断链表是否为空

/**
 * 检查链表是否为空
 *
 * 本函数通过检查链表头指针是否为NULL来确定链表是否为空。如果链表为空,即头指针为NULL,
 * 则函数会打印一条消息并返回-1;如果链表不为空,则返回0。
 *
 * @param L 链表的头指针
 * @return 如果链表为空,则返回-1;否则返回0。
 */
int list_empty(sqlink L)
{
    // 检查链表头指针是否为NULL
    if (L == NULL)
    {
        // 打印消息并返回,表示链表为空
        printf("list is null\n");
        return -1;
    }
    // 返回0,表示链表不为空
    return 0;
}

2.5  链表长度

/**
 * 求链表长度
 *
 * @param L 链表的首节点指针
 * @return 如果链表为空,则返回-1;否则返回L->last+1
 */
int list_length(sqlink L)
{
    if (L == NULL)
    {
        printf("list is null\n");
        return -1;
    }
    return (L->last + 1);
}

2.6 查找表中元素

/**
 * 在有序链表中定位元素的位置。
 *
 * 本函数通过遍历链表寻找给定元素,如果找到,则返回其位置;如果链表为空,或未找到元素,则返回-1。
 * 请注意,这个函数假设链表是按照数据值的顺序排列的。
 *
 * @param L 链表的头指针,类型为sqlink。
 * @param e 需要查找的元素,类型为data_t。
 * @return 如果找到元素,则返回其在链表中的位置(从0开始);如果链表为空或未找到元素,则返回-1。
 */
int list_locate(sqlink L, data_t e)
{
    /* 检查链表是否为空 */
    if (L == NULL)
    {
        printf("list is null\n");
        return -1;
    }

    /* 初始化索引变量 */
    int i = 0;

    /* 遍历链表,查找元素 */
    for (i = 0; i <= L->last; i++)
    {
        /* 如果找到匹配的元素,则返回其位置 */
        if (L->data[i] == e)
        {
            return i;
        }
    }

    /* 如果遍历结束仍未找到元素,则返回-1 */
    return -1;
}

 2.7 插入元素

/**
 * 在单向链表中插入一个元素
 * @param L 链表的头指针
 * @param pos 插入位置的索引
 * @param e 要插入的元素
 * @return 如果插入成功,返回0;如果链表已满,返回-1;如果位置错误,返回-1。
 */
int list_insert(sqlink L, int pos, data_t e)
{
    /* 检查链表是否已满 */
    if (L->last == N - 1)
    {
        printf("list is full");
        return -1;
    }

    /* 检查插入位置是否有效 */
    if (0 <= pos && pos <= L->last)
    {
        /* 将插入位置之后的元素向后移动 */
        for (int i = L->last; i >= pos; i--)
        {
            L->data[i + 1] = L->data[i];
        }

        /* 在指定位置插入新元素 */
        L->data[pos] = e;
        L->last++;
    }
    else
    {
        printf("pos is error");
        return -1;
    }
    return 0;
}

 2.8 打印链表

/**
 * 函数功能:显示链表中的所有元素
 * 参数 L:链表的头指针
 * 返回值:如果链表为空或未初始化,则返回-1;否则返回0。
 */
int list_show(sqlink L)
{
    /* 检查链表是否为空或未初始化 */
    if (L == NULL || L->last == -1)
    {
        /* 如果是,则输出提示信息并返回-1 */
        printf("list is null\n");
        return -1;
    }
    /* 遍历链表,打印每个元素 */
    for (int i = 0; i <= L->last; i++)
    {
        printf("%d ", L->data[i]);
    }
    puts("");
    return 0;
}

 2.9 删除链表

/**
 * 删除单链表中指定位置的元素
 *
 * @param L 单链表的头指针
 * @param pos 要删除的元素的位置
 * @return 如果删除成功返回0,如果链表为空或位置不正确返回-1。
 *
 * 该函数首先检查链表是否为空,然后检查指定的位置是否有效。
 * 如果位置有效,它会通过移动元素将指定位置的元素删除。
 * 最后,它会更新链表的长度。
 */
int list_delete(sqlink L, int pos)
{
    /* 检查链表是否为空 */
    if (L->last == -1)
    {
        printf("list is null\n");
        return -1;
    }
    /* 检查指定的位置是否有效 */
    if (pos < 0 || pos > L->last)
    {
        printf("pos is error\n");
        return -1;
    }
    /* 将指定位置后的元素向前移动以覆盖删除的元素 */
    for (int i = pos; i < L->last; i++)
    {
        L->data[i] = L->data[i + 1];
    }
    /* 减少链表的长度 */
    L->last--;

    return 0;
}

 2.10 合并两个顺序表

/**
 * 合并两个有序链表
 * @param L1 第一个有序链表的头结点
 * @param L2 第二个有序链表的头结点
 * @return 返回0,表示合并操作完成
 *
 * 该函数的目的是将第二个有序链表L2中的所有元素合并到第一个有序链表L1中。
 * 合并过程中,保证L1仍然保持有序。函数通过遍历L2中的每个元素,然后在L1中查找
 * 相同的元素。如果L1中不存在相同的元素,则将该元素插入到L1的末尾。
 */
int list_merge(sqlink L1, sqlink L2)
{
    int i = 0; // 初始化索引变量i,用于遍历L2链表中的元素

    // 遍历L2链表,直到最后一个元素
    while (i <= L2->last)
    {
        // 在L1中查找当前元素是否存在
        int ret = list_locate(L1, L2->data[i]);

        // 如果L1中不存在当前元素,则将其插入到L1的末尾
        if (ret == -1)
        {
            list_insert(L1, L2->data[i], L1->last + 1);
        }
        i++; // 移动到下一个元素
    }
    return 0; // 合并操作完成,返回0
}

该函数用于将链表L2中的所有元素合并到链表L1中,如果L1中已经存在该元素,则不进行任何操作;如果L1中不存在该元素,则将其插入到L1的末尾。函数首先初始化一个索引变量i,用于遍历L2链表中的元素。然后通过循环遍历L2链表,对于每个元素,使用list_locate函数在L1中查找是否存在相同的元素。如果L1中不存在该元素,则使用list_insert函数将其插入到L1的末尾。最后,函数返回0,表示合并操作完成。

2.11 清除顺序表中重复元素

/**
 * 清理列表中重复的元素。
 *
 * 该函数遍历列表,如果发现有重复的元素,则删除重复元素,保持列表中元素的唯一性。
 *
 * @param L 指向sqlink列表的指针。
 * @return 始终返回0,目前未使用返回值来指示函数执行状态。
 */
int list_purge(sqlink L)
{
    // 如果列表为空,则直接返回,无需进行清理。
    if (L->last == 0)
        return 0;

    // 双重循环遍历列表,外层循环遍历每个元素,内层循环比较该元素与后面元素的重复情况。
    for (int i = 0; i < L->last; i++)
    {
        for (int j = i + 1; j < L->last; j++)
        {
            // 如果发现重复元素,则删除重复元素,并调整内层循环的索引。
            if (L->data[i] == L->data[j])
            {
                list_delete(L, j);
                j--;
            }
        }
    }
    return 0;
}

该函数用于清除列表中的重复元素。它通过双重循环遍历列表,外层循环遍历每个元素,内层循环比较该元素与后面元素的重复情况。如果发现重复元素,则删除重复元素,并调整内层循环的索引。如果列表为空,则直接返回。函数返回0,表示操作完成。

 2.12 修改指定元素

/**
 * 修改列表中指定位置的元素
 *
 * 本函数用于修改单向链表中指定位置的元素。如果位置无效(小于0或大于链表长度),函数将不进行任何修改并返回-1。
 * 否则,函数将用新的元素e替换链表中索引为i的元素,并返回0表示修改成功。
 *
 * @param L 链表的头指针,类型为sqlink。
 * @param i 需要修改的元素的位置索引,从0开始计数。
 * @param e 新的元素值,类型为data_t。
 * @return 如果修改成功,返回0;如果位置索引无效,返回-1。
 */
int list_alter(sqlink L, int i, data_t e)
{
    /* 检查索引是否有效 */
    if (i < 0 || i > L->last)
        return -1;
    /* 替换链表中指定位置的元素 */
    L->data[i] = e;
    return 0;
}

三 sqlist.h sqlist.c

sqlist.h:

#ifndef _SQLIST_H_
#define _SQLIST_H_

typedef int data_t;
#define N 128

typedef struct{
	data_t data[N];
	int last;
}sqlist, *sqlink;


sqlink list_init();
int list_clear(sqlink L);
int list_empty(sqlink L);
int list_length(sqlink L);
int list_locate(sqlink L, data_t e);
int list_insert(sqlink L, int i, data_t e);
int list_show(sqlink L);
int list_free(sqlink L);
int list_delete(sqlink L, int pos);
int list_merge(sqlink L1, sqlink L2);
int list_purge(sqlink L);
int list_locate_(sqlink L, data_t e);
int list_alter(sqlink L, int i, data_t e);

#endif

 sqlist.c

#include "sqlist.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/**
 * 初始化一个sqlink列表。
 *
 * 该函数动态分配内存以创建一个sqlink列表,并对其进行初始化。
 * 列表初始化后,最后一个元素的索引被设置为-1,表示列表为空。
 *
 * @return 初始化后的sqlink列表指针。如果内存分配失败,返回NULL。
 */
sqlink list_init()
{
    sqlink L;
    // 为sqlink列表分配内存
    L = (sqlink)malloc(sizeof(sqlist));
    // 检查内存分配是否成功
    if (L = NULL)
    {
        printf("list malloc error\n");
        return NULL;
    }
    // 清零列表内存,确保所有字段都被初始化为0
    memset(L, 0, sizeof(sqlist));
    // 初始化列表的last字段为-1,表示列表为空
    L->last = -1;
    return L;
}
/**
 * 清空链表
 *
 * 本函数用于清空一个链表,将链表的所有元素置为空,并将链表的尾指针重置为-1。
 * 如果链表为空,则函数会打印提示信息并返回-1;否则,函数会成功清空链表并返回0。
 *
 * @param L 链表的头指针
 * @return 如果链表为空,则返回-1;否则返回0。
 */
int list_clear(sqlink L)
{
    /* 检查链表是否为空 */
    if (L == NULL)
    {
        /* 如果链表为空,则打印提示信息并返回错误码 */
        printf("list is null\n");
        return -1;
    }
    /* 将链表头部结构体的所有字节置为0,以清空链表 */
    memset(L, 0, sizeof(sqlist));
    /* 将链表的尾指针重置为-1,表示链表为空 */
    L->last = -1;

    /* 清空操作成功,返回0 */
    return 0;
}

/**
 * 检查链表是否为空
 *
 * 本函数通过检查链表头指针是否为NULL来确定链表是否为空。如果链表为空,即头指针为NULL,
 * 则函数会打印一条消息并返回-1;如果链表不为空,则返回0。
 *
 * @param L 链表的头指针
 * @return 如果链表为空,则返回-1;否则返回0。
 */
int list_empty(sqlink L)
{
    // 检查链表头指针是否为NULL
    if (L == NULL)
    {
        // 打印消息并返回,表示链表为空
        printf("list is null\n");
        return -1;
    }
    // 返回0,表示链表不为空
    return 0;
}

/**
 * 求链表长度
 *
 * @param L 链表的首节点指针
 * @return 如果链表为空,则返回-1;否则返回L->last+1
 */
int list_length(sqlink L)
{
    if (L == NULL)
    {
        printf("list is null\n");
        return -1;
    }
    return (L->last + 1);
}
/**
 * 在有序链表中定位元素的位置。
 *
 * 本函数通过遍历链表寻找给定元素,如果找到,则返回其位置;如果链表为空,或未找到元素,则返回-1。
 * 请注意,这个函数假设链表是按照数据值的顺序排列的。
 *
 * @param L 链表的头指针,类型为sqlink。
 * @param e 需要查找的元素,类型为data_t。
 * @return 如果找到元素,则返回其在链表中的位置(从0开始);如果链表为空或未找到元素,则返回-1。
 */
int list_locate(sqlink L, data_t e)
{
    /* 检查链表是否为空 */
    if (L == NULL)
    {
        printf("list is null\n");
        return -1;
    }

    /* 遍历链表,查找元素 */
    for (int i = 0; i <= L->last; i++)
    {
        /* 如果找到匹配的元素,则返回其位置 */
        if (L->data[i] == e)
        {
            return i;
        }
    }

    /* 如果遍历结束仍未找到元素,则返回-1 */
    return -1;
}

/**
 * 在单向链表中插入一个元素
 * @param L 链表的头指针
 * @param pos 插入位置的索引
 * @param e 要插入的元素
 * @return 如果插入成功,返回0;如果链表已满,返回-1;如果位置错误,返回-1。
 */
int list_insert(sqlink L, int pos, data_t e)
{
    /* 检查链表是否已满 */
    if (L->last == N - 1)
    {
        printf("list is full");
        return -1;
    }

    /* 检查插入位置是否有效 */
    if (0 <= pos && pos <= L->last)
    {
        /* 将插入位置之后的元素向后移动 */
        for (int i = L->last; i >= pos; i--)
        {
            L->data[i + 1] = L->data[i];
        }

        /* 在指定位置插入新元素 */
        L->data[pos] = e;
        L->last++;
    }
    else
    {
        printf("pos is error");
        return -1;
    }
    return 0;
}

/**
 * 函数功能:显示链表中的所有元素
 * 参数 L:链表的头指针
 * 返回值:如果链表为空或未初始化,则返回-1;否则返回0。
 */
int list_show(sqlink L)
{
    /* 检查链表是否为空或未初始化 */
    if (L == NULL || L->last == -1)
    {
        /* 如果是,则输出提示信息并返回-1 */
        printf("list is null\n");
        return -1;
    }
    /* 遍历链表,打印每个元素 */
    for (int i = 0; i <= L->last; i++)
    {
        printf("%d ", L->data[i]);
    }
    puts("");
    return 0;
}

/**
 * 删除链表
 *
 * 本函数用于删除一个链表。链表的头指针通过参数 L 传递进来。
 * 如果链表不存在(即 L 为 NULL),则函数不进行任何操作并返回 -1。
 * 如果链表存在,则释放链表的内存空间,并将 L 设置为 NULL,表示链表已被删除。
 *
 * @param L 链表的头指针
 * @return 如果链表不存在返回 -1,否则返回 0 表示删除成功。
 */
int list_free(sqlink L)
{
    if (L == NULL)
    {
        return -1;
    }
    free(L);
    L = NULL;
    return 0;
}

/**
 * 删除单链表中指定位置的元素
 *
 * @param L 单链表的头指针
 * @param pos 要删除的元素的位置
 * @return 如果删除成功返回0,如果链表为空或位置不正确返回-1。
 *
 * 该函数首先检查链表是否为空,然后检查指定的位置是否有效。
 * 如果位置有效,它会通过移动元素将指定位置的元素删除。
 * 最后,它会更新链表的长度。
 */
int list_delete(sqlink L, int pos)
{
    /* 检查链表是否为空 */
    if (L->last == -1)
    {
        printf("list is null\n");
        return -1;
    }
    /* 检查指定的位置是否有效 */
    if (pos < 0 || pos > L->last)
    {
        printf("pos is error\n");
        return -1;
    }
    /* 将指定位置后的元素向前移动以覆盖删除的元素 */
    for (int i = pos; i < L->last; i++)
    {
        L->data[i] = L->data[i + 1];
    }
    /* 减少链表的长度 */
    L->last--;

    return 0;
}

/**
 * 合并两个有序链表
 * @param L1 第一个有序链表的头结点
 * @param L2 第二个有序链表的头结点
 * @return 返回0,表示合并操作完成
 *
 * 该函数的目的是将第二个有序链表L2中的所有元素合并到第一个有序链表L1中。
 * 合并过程中,保证L1仍然保持有序。函数通过遍历L2中的每个元素,然后在L1中查找
 * 相同的元素。如果L1中不存在相同的元素,则将该元素插入到L1的末尾。
 */
int list_merge(sqlink L1, sqlink L2)
{
    int i = 0; // 初始化索引变量i,用于遍历L2链表中的元素

    // 遍历L2链表,直到最后一个元素
    while (i <= L2->last)
    {
        // 在L1中查找当前元素是否存在
        int ret = list_locate(L1, L2->data[i]);

        // 如果L1中不存在当前元素,则将其插入到L1的末尾
        if (ret == -1)
        {
            list_insert(L1, L2->data[i], L1->last + 1);
        }
        i++; // 移动到下一个元素
    }
    return 0; // 合并操作完成,返回0
}

/**
 * 清理列表中重复的元素。
 *
 * 该函数遍历列表,如果发现有重复的元素,则删除重复元素,保持列表中元素的唯一性。
 *
 * @param L 指向sqlink列表的指针。
 * @return 始终返回0,目前未使用返回值来指示函数执行状态。
 */
int list_purge(sqlink L)
{
    // 如果列表为空,则直接返回,无需进行清理。
    if (L->last == 0)
        return 0;

    // 双重循环遍历列表,外层循环遍历每个元素,内层循环比较该元素与后面元素的重复情况。
    for (int i = 0; i < L->last; i++)
    {
        for (int j = i + 1; j < L->last; j++)
        {
            // 如果发现重复元素,则删除重复元素,并调整内层循环的索引。
            if (L->data[i] == L->data[j])
            {
                list_delete(L, j);
                j--;
            }
        }
    }
    return 0;
}

/**
 * 修改列表中指定位置的元素
 *
 * 本函数用于修改单向链表中指定位置的元素。如果位置无效(小于0或大于链表长度),函数将不进行任何修改并返回-1。
 * 否则,函数将用新的元素e替换链表中索引为i的元素,并返回0表示修改成功。
 *
 * @param L 链表的头指针,类型为sqlink。
 * @param i 需要修改的元素的位置索引,从0开始计数。
 * @param e 新的元素值,类型为data_t。
 * @return 如果修改成功,返回0;如果位置索引无效,返回-1。
 */
int list_alter(sqlink L, int i, data_t e)
{
    /* 检查索引是否有效 */
    if (i < 0 || i > L->last)
        return -1;
    /* 替换链表中指定位置的元素 */
    L->data[i] = e;
    return 0;
}

  • 28
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值