静态链表的定义与基本操作(C语言版)(数据结构与算法)

1. 静态链表

  • 静态链表是使用数组来模拟链表结构的一种数据结构,用数组的方式实现的链表。
  • 它与传统链表的区别在于,静态链表使用数组保存节点,每个节点包括数据域和游标(指向下一个节点的位置)。

1.1 单链表与静态链表的区别:

  • 单链表: 各个结点在内存中星罗棋布、散落天涯
  • 静态链表:分配一整片连续的内存空间, 各个结点集中安置。

1.2 静态链表的优点

  1. 不需要像动态链表那样频繁地进行内存分配和释放,可以节省内存管理的开销。
  2. 可以提前分配一定大小的静态存储空间,避免了动态分配内存的不确定性和运行时开销。
  3. 实现简单,不需要使用指针,减少了指针操作的复杂性和内存占用。

1.3 静态链表的不足

  1. 大小固定,不支持动态扩展和缩小。
  2. 需要提前分配一定大小的存储空间,可能造成空间的浪费或不足。
  3. 插入和删除操作需要重建链表的链接关系,有一定的时间开销。

在这里插入图片描述
在静态链表中如果要表示,这个结点是最后一个结点,游标的值可以设为 -1, 表示之后已经没有其他结点了。

用代码定义一个静态链表

在这里插入图片描述

#define MaxSize 10   //静态链表的最大长度
typedef struct			//静态链表结构类型的定义
{
	ElemType data;    	//存储数据元素
	int next; 			//下一个元素的数组下标
}SLinkList[MaxSize];

#define MaxSize 10   //静态链表的最大长度

在这里插入图片描述

2. 静态链表的基本操作

2.1 静态链表的节点结构定义

//形式一
struct Node {
    int data; // 数据域
    int next; // 游标,指向下一个节点在数组中的位置
};
//形式二
typedef struct Stack{
    Elemtype data;   //数据域
    int cur;   //游标
}StackLink,*PtrStackLink;  
  • 这段代码定义了一个静态链表的结构体 Stack,包含两个成员变量 data 和 cur。其中 data 是存储数据的元素,可以根据实际需要进行定义,cur 是指向下一个元素的指针。

  • 然后通过 typedef 将 Stack 重命名为 StackLink,并定义了一个指向 StackLink 结构体类型的指针 PtrStackLink。

  • 这样可以使用 StackLink 来创建静态链表的节点,使用 PtrStackLink 来创建指向静态链表节点的指针。

//例如,可以使用以下方式创建一个静态链表节点:
StackLink node;
node.data = 10;
node.cur = 1;

PtrStackLink p;
p = &node;

其中:

PtrStackLink p;
p = &node;

这段代码创建了一个指向静态链表节点的指针 p,然后将其指向 node。
可以通过 p 来访问和操作这个静态链表节点的成员变量。

例如,可以使用以下方式访问静态链表节点的 data 成员变量:

p->data = 10;
p->cur = 1;
//这样就可以通过指针 p 操作静态链表节点的数据和指针。
//需要注意的是,如果对指针 p 进行重新赋值,则指向的节点也会相应改变。

请添加图片描述

2.2 静态链表的初始化

const int MAX_SIZE = 100; // 静态链表的最大容量
Node space[MAX_SIZE]; // 静态链表的空间

// 初始化静态链表
void initList() {
    for (int i = 0; i < MAX_SIZE - 1; i++) {
        space[i].next = i + 1; // 每个节点的 next 指向下一个节点
    }
    space[MAX_SIZE - 1].next = 0; // 最后一个节点的 next 设置为 0 表示链表结束
}

2.3 静态链表的节点插入

  1. 查找插入位置:

遍历静态链表,找到要插入位置的前一个节点。可以使用一个游标来遍历链表,初始时指向链表的头节点。

  1. 分配新节点:

在静态链表的空闲位置上分配一个新节点,为新节点赋值。

  1. 插入节点:

将新节点的下一个节点指向前一个节点的下一个节点,然后将前一个节点的下一个节点指向新节点的位置。

// 在静态链表的头部插入节点
bool insertNode(int data) {
    int newNodeIndex = space[0].next; // 获取可用节点的索引(即可用数组空间的下标)
    if (newNodeIndex == 0)   //判断备用链表的第一个结点下标是否为0
    { 
        return false; // 下标为0,链表已满,插入失败
    }
    // 若备用链表第一个结点空闲,则征用该结点,同时更新备用链表的头结点指向,将此被征用的空闲结点的下一空闲结点填补上来。
    space[0].next = space[newNodeIndex].next; 

    space[newNodeIndex].data = data; // 设置插入节点的数据
    space[newNodeIndex].next = space[1].next; // 插入结点的 next 指向原先的头结点

    space[1].next = newNodeIndex; // 头节点指向插入节点
    return true;
}

2.4 静态链表的节点删除

  1. 找到要删除结点的前一个结点的位置。

  2. 将被删除结点的下一个结点的下标保存下来。

  3. 修改被删除结点的前一个结点的指针域,使其指向被删除结点的下一个结点。

  4. 将被删除结点的下标加入备用链表。

// 删除指定数据的节点
bool deleteNode(int data) {
    int prev = 1; // 记录当前节点的前一个节点
    int curr = space[1].next; // 从头节点开始查找

    while (curr != 0) {
        if (space[curr].data == data) {
            break; // 找到要删除的节点,跳出循环
        }
        prev = curr; // 未找到,则继续向下遍历
        curr = space[curr].next;
    }

    if (curr == 0) {
        return false; // 没有找到要删除的节点,删除失败
    }

    space[prev].next = space[curr].next; // 前一个节点的 next 指向要删除节点的 next
    space[curr].next = space[0].next; // 被删除的节点的 next 指向当前可用节点
    space[0].next = curr; // 更新可用节点的索引

    return true;
}

在这里插入图片描述

2.5 静态链表的遍历

// 打印静态链表
void printList() {
    int curr = space[1].next; // 从头节点开始遍历
    while (curr != 0) {
        std::cout << space[curr].data << " ";
        curr = space[curr].next;
    }
    std::cout << std::endl;
}

2.6 按值查找结点

在静态链表中查找数据可以通过遍历链表的方式来完成。由于静态链表没有指针来直接跳转到下一个节点,所以需要使用游标来遍历链表。

以下是一种在静态链表中查找数据的示例算法:

  1. 遍历链表,从链表头部开始,通过头节点的索引获取第一个节点的索引。
  2. 遍历链表中的每个节点,判断节点的数据是否与目标数据相等。
  3. 如果相等,找到了目标数据,返回节点的索引。
  4. 如果不相等,获取当前节点的下一个节点,更新当前节点的索引为下一个节点的索引。
  5. 若遍历完整个链表(即当前节点的索引为-1),仍未找到目标数据,则返回-1表示未找到。

这是一个简单的线性查找算法,时间复杂度为O(n),其中n是链表中节点的数量。

// 按值查找节点
int findNodeByValue(int value) {
    int curr = space[1].next; // 从头节点开始遍历
    while (curr != 0) {
        if (space[curr].data == value) {
            return curr; // 找到节点,返回索引
        }
        curr = space[curr].next;
    }
    return 0; // 没找到节点,返回 0
}

在这里插入图片描述

2.7 按索引查找结点

// 按索引查找节点的值
int getNodeValueByIndex(int index) {
    if (index < 0 || index >= MAX_SIZE) {
        return -1; // 索引非法,返回 -1 
    }
    return space[index].data;
}

2.8 获取链表长度

// 获取静态链表长度
int getListLength() {
    int count = 0;
    int curr = space[1].next; // 从头节点开始遍历
    while (curr != 0) {
        count++;
        curr = space[curr].next;
    }
    return count;
}

静态链表的优点:

  1. 相比于动态链表,静态链表的存储空间是预先分配好的,不需要频繁地进行内存申请和释放,因此在一些内存有限或者对内存分配效率有要求的场景下,静态链表可能更为适用。
  2. 静态链表在存储空间上是连续的,可以提高数据访问的效率,尤其是在对元素进行遍历、查找和索引访问等操作时,相对于动态链表具有一定的性能优势。

静态链表的缺点:

  1. 静态链表的长度是固定的,无法随意扩展或缩小,一旦达到最大长度,就无法再插入新节点。
    静态链表在插入和删除节点时,需要进行额外的操作来维护节点间的连接关系,可能会增加一定的编程复杂性。
  2. 静态链表的每个节点需预先分配固定大小的存储空间,可能会造成空间的浪费,特别是在某些节点存储的数据量较小的情况下。

3. 知识回顾

在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数据结构与算法分析》实验报告 姓名 学号_ _____ __年 __月__ __日 上机题目:以静态链表为存储结构,编写给定权值{7,19,2,6,32,3}构造哈夫曼树的 算法。 (输出以存储结构表示或以树型显示(90度旋转)) 需求分析 1. 输入数据必须为int的整形数据,其数值范围为:-~47 2. 输出的数据格式为:%d 3. 测试数据的数据为:{7,19,2,6,32,3} 详细设计 1. 该程序采用顺序表的存储结构,其数据结构定义如下: #define n 6 #define m 2*n-1 #define max 100typedef struct {int data; int lchild,rchild,prnt; }hufmtree; 所用数据类型中每个操作的伪码算法如下: 创建哈夫曼树 Program hufm(hufmtree t[m]) FOR i=0;i<m;i++ TO t[i].data=0; t[i].lchild=0; t[i].rchild=0; t[i].prnt=0; End FOR 输入结点值 FOR i=n;i<m;i++ TO p1=0;p2=0; small1=max;small2=max FOR j=0;j<=i-1;j++ TO IFt[j].prnt?=0 IF(t[j].data<small1) small2=small1; small1=t[j].data; p2=p1; p1=j;} ELSE IF(t[j].data<small2) small2=t[j].data; p2=j; t[p1].prnt=i+1; t[p2].prnt=i+1; t[i].lchild=p1+1; t[i].rchild=p2+1; t[i].data=t[p1].data+t[p2].data; END IF END FOR END Hufman 调试分析 1. 调试过程中主要遇到哪些问题?是如何解决的? 开始的时候main函数的数据结构类型定义的与主函数不同,而且缺少返回值,导致最 后的结果陷入死循环,通过看书,向同学询问,得以解决。 2. 经验和体会 哈夫曼树又称最优二叉树,此次实验创建哈夫曼树算法,虽然依旧犯了不少错误,但 仍解决了。在其中学习了很多,对树有了更深的了解。 测试结果 附件 见 058詹奇.Cpp ----------------------- 数据结构与算法分析实验报告全文共3页,当前为第1页。 数据结构与算法分析实验报告全文共3页,当前为第2页。 数据结构与算法分析实验报告全文共3页,当前为第3页。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值