【数据结构】线性表

文章目录

  • 前言
  • 线性表的定义和基本操作
    • 1.线性表的定义
    • 2.线性表的基本操作
  • 顺序表的定义
    • 1.静态分配方式
    • 2.动态分配方式
  • 顺序表的插入和删除
    • 1.顺序表的插入
    • 2.顺序表的删除
  • 顺序表的查找
    • 1.按位查找(简单)
    • 2.按值查找
  • 单链表的定义
    • 1.代码定义一个单链表
    • 2.不带头节点的单链表
    • 3.带头节点的单链表
  • 单链表的插入和删除
    • 1.按位序插入带头节点
    • 2.按位序插入不带头节点
    • 3.指定结点的后插操作
    • 4.指定结点的前插操作
    • 5.指定结点的删除操作
  • 总结


前言

提示:这里可以添加本文要记录的大概内容:

前言
数据结构是计算机科学中一个重要的基础概念,它研究的是如何组织和管理计算机中的数据。线性表是一种常见的数据结构,它由一组具有相同数据类型的元素组成,这些元素之间存在着线性关系

线性表具有许多重要的应用,例如:

  • 存储和管理数据
  • 实现各种算法
  • 构建其他数据结构
    在本文中,我们将对线性表的概念、特点、实现和应用进行详细的介绍。

提示:以下是本篇文章正文内容,下面案例可供参考

线性表的定义和基本操作

知识总览
在这里插入图片描述

注意:存储结构不同,运算的实现方式也会不同

1.线性表的定义

线性表定义的是数据结构中的逻辑结构
在这里插入图片描述
对于图片中关于,如果所有整数按递增次序排列,是线性表吗

答案是否定的,因为从定义中我们可以知道,有限二字,然后整数是可以无穷无尽的

2.线性表的基本操作

这个图大家看看就行
在这里插入图片描述
这里建议大家把其中的每个操作都动手写一写,或者在编译器上多写一下,熟悉几遍
在这里插入图片描述

对于为什么没有说明各个参数的具体类型?

数据结构书籍中没有说明各个参数的具体类型,是为了提高书籍的通用性、简洁性、灵活性和易读性,并强调对数据结构思想和原理的理解。读者可以通过阅读代码示例和查阅相关资料来了解具体的参数类型。

最后来一个总结的图
在这里插入图片描述

顺序表的定义

在这里插入图片描述
顺序表——用顺序存储的方式实现线性表顺序存储。把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中,元素之间的关系由存储单元的邻接关系来体现。

在这里插入图片描述

1.静态分配方式

我们需要知道顺序表的静态分配方式使用一个数组来实现的,数组的长度一旦确定就无法改变!!!
在这里插入图片描述
关于图片中把各个数据元素的值设为默认值(可省略的原因)?

因为在顺序表结构中有一个length字段,表示的是顺序表中已有元素的个数,因此只要我们初始化了length就行,不根据length来遍历顺序表的行为都是非法的!!!

在这里插入图片描述
代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#define MAXSIZE 10//定义顺序表的最大空间typedef struct
{int data[MAXSIZE];//利用静态分配方式实现顺序表int length;//顺序表中有效元素的个数
}Sqlist;//类型重命名,取名为Sqlist//初始化一个顺序表
void InitList(Sqlist& L)//利用引用传参
{for (int i = 0; i < MAXSIZE; i++)//这里对数组元素的初始化不进行也可以,因为到时候遍历利用的也是length字段的值去遍历的{L.data[i] = 0;//将数组中的值都初始化为0}L.length = 0;
}int main()
{Sqlist L;InitList(L);return 0;
}

思考:
在这里插入图片描述

这里需要注意,length在初始化的时候不能被省略,如果省略了后果很严重,省略了那么length就是一个随机值

改进后的写法:

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#define MAXSIZE 10//定义顺序表的最大空间typedef struct
{int data[MAXSIZE];//利用静态分配方式实现顺序表int length;//顺序表中有效元素的个数
}Sqlist;//类型重命名,取名为Sqlist//初始化一个顺序表
void InitList(Sqlist& L)//利用引用传参
{//for (int i = 0; i < MAXSIZE; i++)//这里对数组元素的初始化不进行也可以,因为到时候遍历利用的也是length字段的值去遍历的//{//	L.data[i] = 0;//将数组中的值都初始化为0//}L.length = 0;
}//遍历顺序表
void Print(const Sqlist&L)
{for (int i = 0; i < L.length; i++){cout << L.data[i] << " ";}cout << endl;
}
int main()
{Sqlist L;InitList(L);Print(L);return 0;
}

如果数组存满了,那么我们会想到两种解决办法,1.开始的时候就开辟一个足够大的空间,2.进行扩容

对于第一种方法,显然是不可行的,首先程序所需的空间我们在事前是很难预料的,其次如果开辟的空间很大,但是最后使用的空间很小,就会造成空间上很大的浪费!!!对于第二种方法,由于我们采用的是静态分配,空间的大小一定确定就无法更改,所以这里的扩容也进行不下去

在这里插入图片描述

2.动态分配方式

通过之前的内容我们可以知道,静态分配方式实现的顺序表对于扩容的时候很困难,那么由此就引出了动态分配方式实现的顺序表,也就是利用数组指针来实现!!!

注意,王道这里用的都是c语言的malloc函数,但是我本人习惯用C++的new关键字,对于两者的内容可以看我的博客内容!!!

在这里插入图片描述
结构体定义:

#define Elemtype int //这里我把类型设置为int类型
typedef struct
{Elemtype* data;int capacity;int length;
}SqList;

在这里插入图片描述
动态分配相关代码实现:

#include <iostream>
using namespace std;
#define InitSize 10
#define Elemtype int //这里我把类型设置为int类型
typedef struct
{Elemtype* data;int capacity;int length;
}SeqList;void InitList(SeqList& L)
{L.data = new Elemtype[InitSize];L.capacity = InitSize;L.length = 0;
}void IncreaseSize(SeqList& L, int len)//在原来的基础上多增加len个空间
{Elemtype* tmp = L.data;L.data = new Elemtype[len + L.capacity];memcpy(L.data, tmp,L.length*sizeof(Elemtype));//这里由于元素类型是int类型也就是内置类型,所以我用的是memcpy函数,自定义类型慎用!!!!delete[] tmp;//释放原空间L.capacity += len;
}
int main()
{SeqList L;InitList(L);IncreaseSize(L, 5);return 0;
}

扩容前容量是10
在这里插入图片描述

扩容后可以看到容量增加到了15
在这里插入图片描述

同时需要注意增容操作往往需要拷贝数据,拷贝数据需要比较大的时间开销

顺序表的特点!!!
在这里插入图片描述
小总结:
在这里插入图片描述

顺序表的插入和删除

1.顺序表的插入

知识总览
在这里插入图片描述
插入操作介绍
在这里插入图片描述
插入操作的代码实现
在这里插入图片描述

void InsertSeqList(SeqList& L,int pos,int num)//在指定位序下插入元素num
{for (int i = L.length; i >= pos; i--){L.data[i] = L.data[i - 1];}L.data[pos - 1] = num;L.length += 1;
}

对边界情况和异常情况的检查,提高代码的健壮性!!!
在这里插入图片描述

#include <iostream>
using namespace std;
#define MAXSIZE 10
typedef struct
{int data[MAXSIZE];//这里类型用intint length;
}SeqList;//初始化
void InitSeqList(SeqList& L)
{L.length = 0;
}
bool InsertSeqList(SeqList& L,int pos,int num)//在指定位序下插入元素num
{if (pos<1 || pos>L.length + 1){return false;}if (MAXSIZE == L.length)//此时空间已满,不能继续插入{return false;}for (int i = L.length; i >= pos; i--){L.data[i] = L.data[i - 1];}L.data[pos - 1] = num;L.length += 1;return true;
}//遍历顺序表
void Print(const SeqList&L)
{for (int i = 0; i < L.length; i++){cout << L.data[i] << " ";}cout << endl;
}int main()
{SeqList L;InitSeqList(L);InsertSeqList(L, 1, 100);Print(L);return 0;
}

插入操作的时间复杂度
在这里插入图片描述
时间复杂度这里图上说的很清楚,个人觉得没有必要继续阐述了

2.顺序表的删除

顺序表的删除操作
在这里插入图片描述
可以看到,删除操作是需要把后面元素前移的,这里和插入操作恰好相反!!!
在这里插入图片描述

完整代码!!!

#include <iostream>
using namespace std;
#define MAXSIZE 10
typedef struct
{int data[MAXSIZE];//这里类型用intint length;
}SeqList;//初始化
void InitSeqList(SeqList& L)
{L.length = 0;
}
bool InsertSeqList(SeqList& L,int pos,int num)//在指定位序下插入元素num
{if (pos<1 || pos>L.length + 1){return false;}if (MAXSIZE == L.length)//此时空间已满,不能继续插入{return false;}for (int i = L.length; i >= pos; i--){L.data[i] = L.data[i - 1];}L.data[pos - 1] = num;L.length += 1;return true;
}//遍历顺序表
void Print(const SeqList&L)
{for (int i = 0; i < L.length; i++){cout << L.data[i] << " ";}cout << endl;
}bool ListDelete(SeqList& L, int pos, int& e)//删除顺序表pos位置的值,并将被删除元素的值给e
{if (pos<1 || pos>L.length){return false;}e = L.data[pos-1];for (int i = pos; i < L.length; i++){L.data[i-1] = L.data[i];}L.length--;return true;
}
int main()
{SeqList L;int num = 0;InitSeqList(L);InsertSeqList(L, 1, 1);InsertSeqList(L, 2, 2);InsertSeqList(L, 3, 3);Print(L);if(ListDelete(L, 2, num))cout << num << endl;Print(L);return 0;
}

对于图中,如果参数e没加引用符号会怎么样的问题?

如果参数e没加引用符号,那么形参会是实参的拷贝,在main函数中输出的num的值依旧是0

删除操作的时间复杂度
在这里插入图片描述
小总结:
在这里插入图片描述

顺序表的查找

知识总览:
在这里插入图片描述

1.按位查找(简单)

在这里插入图片描述
由于顺序表随机存取的特性,显然他的时间复杂度是O(1),其他的没什么太多可说的,都很好理解
在这里插入图片描述
代码

这里我的参数加了引用,因为引用共用的是同一块空间,如果不加引用就会多一次拷贝,增加空间的开销

int GetElem(const SeqList&L,int i)
{return L.data[i-1];
}

2.按值查找

在这里插入图片描述
在这里插入图片描述

代码:

int LocateElem(const SeqList& L, int e)
{for (int i = 0; i < L.length; i++){if (L.data[i] == e)return i + 1;}return -1;
}

在这里插入图片描述

对于结构体类型的元素或是自定义类型的元素不能使用==运算符,c++中需要使用运算符重载才可以使用,c语言不能使用

解决办法
在这里插入图片描述
注意:
在这里插入图片描述
时间复杂度
在这里插入图片描述
小总结:
在这里插入图片描述

单链表的定义

在这里插入图片描述
带头结点和不带头结点
在这里插入图片描述

这里就是说明链式存储的存储密度比顺序存储低
在这里插入图片描述

1.代码定义一个单链表

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
改进后,单链表的定义

typedef struct LNode
{int data;//数据域struct LNode* next;//指针域
}LNode,*LinkList;
struct LNode
{int data;//数据域struct LNode* next;//指针域
};

在这里插入图片描述
在这里插入图片描述

2.不带头节点的单链表

在这里插入图片描述
代码:

#include <iostream>
using namespace std;
typedef struct LNode
{int data;//数据域struct LNode* next;//指针域
}LNode,*LinkList;//初始化
bool InitList(LinkList& L)
{L = NULL;return true;
}//判断单链表是否为空
bool empty(LinkList L)
{return (L == NULL);
}
int main()
{LinkList L;InitList(L);cout << empty(L) << endl;return 0;
}

3.带头节点的单链表

注意头结点不存储数据
在这里插入图片描述

#include <iostream>
using namespace std;
typedef struct LNode
{int data;//数据域struct LNode* next;//指针域
}LNode,*LinkList;//初始化
bool InitList(LinkList& L)
{L = new LNode;//分配一个头结点,或者用c语言的方式(LNode*)malloc(sizeof(LNode))if (L == NULL)//内存不足,分配失败return false;L->next = NULL;return true;
}//判断单链表是否为空
bool empty(LinkList L)
{return (L ->next== NULL);
}
int main()
{LinkList L;InitList(L);cout << empty(L) << endl;return 0;
}

小结:
在这里插入图片描述

在这里插入图片描述

单链表的插入和删除

在这里插入图片描述
在这里插入图片描述

1.按位序插入带头节点

在这里插入图片描述
在这里插入图片描述

#include <iostream>
using namespace std;
typedef struct LNode
{int data;//数据域struct LNode* next;//指针域
}LNode,*LinkList;//初始化
bool InitList(LinkList& L)
{L = new LNode;//分配一个头结点,或者用c语言的方式(LNode*)malloc(sizeof(LNode))if (L == NULL)//内存不足,分配失败return false;L->next = NULL;return true;
}//在第i个位置插入元素e(带头节点)
bool ListInsert(LinkList& L, int i, int e)
{if (i < 1){return false;}int j = 0;//记录当前是第几个结点LNode* p = L;//临时结点pwhile (p != NULL && j < i-1)//找到第i-1个结点的位置{p = p->next;j++;}if (p == NULL)return false;LNode* s = new LNode;s->data = e;s->next = p->next;p->next = s;return true;
}void Print(LinkList L)
{LNode* p = L;while (p->next != NULL){p = p->next;cout << p->data << " ";}cout << endl;
}//判断单链表是否为空
bool empty(LinkList L)
{return (L ->next== NULL);
}
int main()
{LinkList L;InitList(L);ListInsert(L,1,1);ListInsert(L,2,2);ListInsert(L,3,3);ListInsert(L,4,4);Print(L);return 0;
}

2.按位序插入不带头节点

在这里插入图片描述
在这里插入图片描述
代码:

#include <iostream>
using namespace std;
typedef struct LNode
{int data;//数据域struct LNode* next;//指针域
}LNode, * LinkList;//初始化(不带头节点)
bool InitList(LinkList& L)
{L = NULL;return true;
}//在第i个位置插入元素e(不带头节点)
bool ListInsert(LinkList& L, int i, int e)
{if (i < 1){return false;}if (i == 1){LNode* s = new LNode;s->data = e;s->next = L;L = s;return true;}LNode* p = L;int j = 1;while (p != NULL && j < i - 1){p = p->next;j++;}if (p == NULL)return false;LNode* s = new LNode;s->data = e;s->next = p->next;p->next = s;return true;
}void Print(LinkList L)
{LNode* p = L;while (p != NULL){cout << p->data << " ";p = p->next;}cout << endl;
}//判断单链表是否为空
bool empty(LinkList L)
{return (L == NULL);
}
int main()
{LinkList L;InitList(L);ListInsert(L, 1, 1);ListInsert(L, 2, 2);ListInsert(L, 3, 3);ListInsert(L, 4, 4);Print(L);return 0;
}

3.指定结点的后插操作

在这里插入图片描述
代码:

bool InsertNExtNode(LNode* p, int e)
{if (p == NULL)return false;LNode* s = new LNode;if (s == NULL)return false;s->data = e;s->next = p->next;p->next = s;return true;
}

4.指定结点的前插操作

在这里插入图片描述
代码:

bool InsertPriorNode(LNode* p, int e)
{if (p == NULL)return false;LNode* s = new LNode;if (s == NULL)return false;s->next = p->next;p->next = s;s->data = p->data;p->data = e;return true;
}

5.指定结点的删除操作

在这里插入图片描述
代码:

bool DeleteNode(LNode* p)
{if (p == NULL)return false;LNode* q = p->next;p->data = q->data;p->next = q->next;delete q;return true;
}

小结:
在这里插入图片描述

总结

在本文中,我们对线性表的概念、特点、实现和应用进行了详细的介绍。

线性表的特点是:

  • 元素之间存在着线性关系
  • 每个元素只有一个前驱和一个后继

线性表的实现方式主要有两种:

  • 顺序表
  • 链表

线性表的应用非常广泛,例如:

  • 存储和管理数据
  • 实现各种算法
  • 构建其他数据结构

通过学习本文,读者应该能够:
理解线性表的概念和特点
掌握线性表的实现方法
了解线性表的应用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/294646.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux和Windows安装PHP依赖管理工具Composer

Composer 是 PHP 的一个依赖管理工具。它允许申明项目所依赖的代码库&#xff0c;会在项目中安装它们。 Composer 不是一个包管理器。是的&#xff0c;它涉及 "packages" 和 "libraries"&#xff0c;但它在每个项目的基础上进行管理&#xff0c;在你项目的…

154 Linux C++ 通讯架构实战9 ,信号功能添加,信号使用sa_sigaction 回调,子进程添加,文件IO详谈,守护进程添加

初始化信号 使用neg_init_signals(); 在nginx.cxx中的位置如下 //(3)一些必须事先准备好的资源&#xff0c;先初始化ngx_log_init(); //日志初始化(创建/打开日志文件)&#xff0c;这个需要配置项&#xff0c;所以必须放配置文件载入的后边&#xff1b;//(4)一些初…

HTML网站的概念

目录 前言&#xff1a; 1.什么是网页&#xff1a; 2.什么是网站&#xff1a; 示例&#xff1a; 3.服务器&#xff1a; 总结&#xff1a; 前言&#xff1a; HTML也称Hyper Text Markup Language&#xff0c;意思是超文本标记语言&#xff0c;同时HTML也是前端的基础&…

STM32重要参考资料

stm32f103c8t6 一、引脚定义图 二、时钟树 三、系统结构图 四、启动配置 &#xff08;有时候不小心短接VCC和GND&#xff0c;芯片会锁住&#xff0c;可以BOOT0拉高试试&#xff08;用跳线帽接&#xff09;&#xff09; 五、最小系统原理图 可用于PCB设计

超过 3550 个最有效的 ChatGPT 提示词(三)

原文&#xff1a;3550 Most Effective ChatGPT Prompts 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 30.Instagram 营销 “我正在寻找一个有效突出我的[产品/服务]的独特特点和优势的 Instagram 故事概念&#xff0c;以创意和迷人的方式向我的[理想客户画像]展示。”…

2024年04月数据库流行度最新排名

点击查看最新数据库流行度最新排名&#xff08;每月更新&#xff09; 2024年04月数据库流行度最新排名 TOP DB顶级数据库索引是通过分析在谷歌上搜索数据库名称的频率来创建的 一个数据库被搜索的次数越多&#xff0c;这个数据库就被认为越受欢迎。这是一个领先指标。原始数…

香港科技大学广州|数据科学与分析学域硕博招生宣讲会—天津大学专场

时间&#xff1a;2024年4月12日&#xff08;星期五&#xff09;14:00 地点&#xff1a;天津大学北洋园校区55楼B204 报名链接&#xff1a;https://www.wjx.top/vm/Q0cKTUI.aspx# 跨学科研究领域 *数据驱动的人工智能和机器学习 *统计学习和建模 工业和商业分析 *特定行业的数…

python对接百度云车牌识别

注册百度智能云&#xff0c;选择产品服务。 https://console.bce.baidu.com/ 每天赠送200次&#xff0c;做开发测试足够了。 在应用列表复制 AppID , API Key ,Secret Key 备用。 SDK下载地址 https://ai.baidu.com/sdk#ocr 下载SDK文件&#xff0c;解压&#xff0c;…

Linux 安装部署高性能缓存服务redis

Linux 系统安装Redis 5 注意事项&#xff1a; 下载Redis 文件包&#xff0c;并上传至linux服务上解压 tar -zxvf redis.tar安装&#xff1a; 编译 make PREFIX/usr/local/redis install配置&#xff1a; redis.conf daemonize yes bind 127.0.0.1 192.168.1.221 supervised…

[网鼎杯 2020 朱雀组]Nmap1

打开题目 在源代码中看到了提示 先随便输入127.0.0.1 那我们试试输入 127.0.0.1 | ls 可以看到 | 被转义符号\所转义 那我们输入 127.0.0.1 /| ls 得到三条反斜线 我们猜测&#xff0c;我们输入的东西是被escapeshellarg和escapeshellcmd处理过后的结果 我们输入的东西必须…

HTML5 和 CSS3 提高

一、HTML5 的新特性 HTML5 的新增特性主要是针对于以前的不足&#xff0c;增加了一些新的标签、新的表单和新的表单属性等。这些新特性都有兼容性问题&#xff0c;基本是 IE9 以上版本的浏览器才支持&#xff0c;如果不考虑兼容性问题&#xff0c;可以大量使用这些新特性。 声明…

Chatgpt掘金之旅—有爱AI商业实战篇|文案写作|(三)

演示站点&#xff1a; https://ai.uaai.cn 对话模块 官方论坛&#xff1a; www.jingyuai.com 京娱AI 一、前言 人工智能&#xff08;AI&#xff09;技术作为当今科技创新的前沿领域&#xff0c;为创业者提供了广阔的机会和挑战。随着AI技术的快速发展和应用领域的不断拓展&…

Mysql 常用SQL语句

1、查看mysql中所有的数据库&#xff0c; show databases; 2、创建库 create database 库名;&#xff08;也可以用 create database if not exists 库名; 表示如果库不存在再创建&#xff09; 例&#xff1a;create database if not exists ecology; 3、删除库 …

试过了,ChatGPT确实不用注册就可以使用了!

看到官网说不用登录也可以直接使用ChatGPT 我们来试一下 直接打开官网 默认是直接进入了chatgpt3.5的聊天界面 之前是默认进的登录页面 聊一下试试 直接回复了&#xff0c;目前属于未登录状态&#xff0c;挺好&#xff01; 来试下ChatGPT4 跳转到了登录页面 目前来看gpt4还…

海外媒体宣发技巧解析从而提升宣发效果

在当今全球化的媒体环境下&#xff0c;海外媒体宣发是企业和品牌推广的重要手段。然而&#xff0c;要在海外市场取得成功&#xff0c;一味地复制国内的宣发策略是行不通的。要想提升宣发效果&#xff0c;就必须了解并掌握一些海外媒体宣发的技巧。世媒讯一家从事海内外媒体的推…

代码随想录训练营day28

第七章 回溯算法part04 1.LeetCode.复原IP地址 1.1题目链接&#xff1a;93.复原IP地址 文章讲解&#xff1a;代码随想录 视频讲解&#xff1a;B站卡哥视频 1.2思路&#xff1a;其实只要意识到这是切割问题&#xff0c;切割问题就可以使用回溯搜索法把所有可能性搜出来&…

Web3 游戏周报(3.24-3.30)

【3.24-3.30】Web3 游戏行业动态&#xff1a; Web3 开发平台 Mirror World 在 Solana 上推出首个游戏 rollup 链 NFT 卡牌游戏 Parallel 完成 3,500 万美元融资&#xff0c;Solana Ventures 等参投 加密游戏开发公司 Gunzilla Games 完成 3,000 万美元融资 Telegram 游戏 No…

Linux环境基础和工具的使用

目录 1、Linux软件包管理器---yum 2、Linux开发工具 2.1、vim基本概念 2.2 vim基本操作 2.3 vim正常模式命令集 2.4 vim末行模式命令集 2.5 简单vim配置 2.5.1 配置文件的位置 3 Linux编译器--gcc/g的使用 3.1 背景知识 3.2 gcc完成 4 Linux调试器--gdb使用 4.1 背…

RK3568驱动指南|第十四篇 单总线-第158章DS18B20编写字符设备驱动框架

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

站群CMS系统

站群CMS系统是一种用于批量建立和管理网站的内容管理系统&#xff0c;它能够帮助用户快速创建大量的网站&#xff0c;并实现对这些网站的集中管理。以下是三个在使用广泛的站群CMS系统&#xff0c;它们各具特色&#xff0c;可以满足不同用户的需求。 1. Z-BlogPHP Z-BlogPHP是…