数据结构:单链表
文章目录
- 🍉前言
- 🍉基本概念
- 🍉链表的分类
- 🍌单链表节点的结构
- 🍌创建节点
- 🍌打印链表
- 🍌插入和删除
- 🥝尾插
- 🥝头插
- 🥝尾删
- 🥝头删
- 🥝指定位置之前插入
- 🥝指定位置之后插入
- 🥝删除指定节点
- 🍌销毁
- 🍉源码
- 🍌头文件:声明部分
- 🍌源文件:功能实现部分
🍉前言
喜茶的果汁茶有这样的一句宣传语:一半果汁一半茶。用这个来形容单链表那可是再合适不过了一一 一半数据一半指针。
🍉基本概念
链表是一种数据结构,采用链式存储
一一在内存中不是连续存储的,各元素的逻辑顺序是通过链表中的指针链接次序实现的。它包含数据域
和指针域
,分别保存数据和下一个节点的地址。
如果要创建节点,一般是在堆上申请。
🍉链表的分类
链表结构多种多样,有三种分类,这些分类进行排列组合共有8种:
①单向或双向
区分单双向就看各节点之间的指向
是否是双向的
,比如下面这个就是双向链表。
②带头或不带头
就是有没有带头节点,头节点的数据域一般是存放这个链表的基本信息,其指针域指向第一个节点。>>③循环或非循环
尾节点的指针指向头节点就可以形成一个循环。
我们最常用的是这两种:无头单向非循环链表和带头双向循环链表,本文要讲的是单链表。
🍌单链表节点的结构
typedef int SLTypeDate;
typedef struct SListNode {SLTypeDate data;struct SListNode* next;
}SLNode;
这个结构体里面有一个同类型的结构体指针
,这种现象叫做结构体的自引用
,往下看你就会知道这个指针的妙处了。
Q:使用 typedef 对结构体重命名之后,可以在结构体内部使用新的名字吗?
A:不可以,因为编译器是向下编译的,上面的语句相当于:
struct SListNode {SLTypeDate data;struct SListNode* next;
};
typedef int SLTypeDate SLNode;
🍌创建节点
刚才已经在头文件里面定义了一个结构体类型SLNode(链表节点),那么现在来建立一些节点,用于后面测试相应的函数。
(原文件 test.c)
void SLTest() {SLNode* Node1 = (SLNode*)malloc(sizeof(SLNode));Node1->data = 1;SLNode* Node2 = (SLNode*)malloc(sizeof(SLNode));Node1->data = 2;SLNode* Node3 = (SLNode*)malloc(sizeof(SLNode));Node1->data = 3;SLNode* Node4 = (SLNode*)malloc(sizeof(SLNode));Node1->data = 4;
}
Node1->next = Node2;Node2->next = Node3;Node3->next = Node4;Node4->next = NULL;
建立了四个节点,但是它们现在彼此之间还没有联系,就是孤零零的四个节点,此时我们用next指针把它们给串联起来。整体效果图就是这样:
某个节点的指针域保存的是下一个节点的地址
🍌打印链表
我们来写一个函数打印链表中存放的数据。
void SLPrint(SLNode* phead) {SLNode* pcur = phead;while (pcur) {printf("%d ->", pcur->data);pcur = pcur->next; //pcur原先指向某个节点,现在让它指向下一个节点}printf("NULL\n");
}
这里解释下为什么要用一个pcur保存第一个节点的地址:因为你如果用phead的话,那到时phead在循环中就会不断往前走,直到它为NULL,也就是说,循环结束以后phead就是空指针了,那你就再也找不到链表中各个节点的地址了。
所以我们不难看出链表是通过地址的赋值,从上一个节点走到下一个节点
接下来也是和顺序表差不多,有头插、尾插的操作,执行插入操作时我们需要申请新节点,为了避免重复,让代码简洁一点,我们可以写一个申请新节点的函数。
SLNode* SLBuyNode(SLTypeDate x) { //申请一个节点,并将想要存储的数据存进去SLNode* node = (SLNode*)malloc(sizeof(SLNode));node->data = x;node->next = NULL;return node;
}
🍌插入和删除
老规矩,还是分为头插和尾插还有随便插,先来写个尾插
🥝尾插
我们首先得找到最后一个节点,然后把它的next指针改为node的地址,代码如下,该说的基本都在注释讲了,主要来说一下 while 循环的终止条件。
如果循环终止条件是pcur == NULL 的话,那就跑过头了,此时pcur就成空指针。
void SLPushBack(SLNode** pphead,SLTypeDate x) {assert(pphead);SLNode* node = SLBuyNode(x);if (*pphead == NULL) { //phead为空说明此时链表为空,那就直接插入*pphead = node; //让node是第一个节点的地址return;}//若不为空,则先找到链表的最后一个节点,再插入SLNode* pcur = *pphead; //老样子,用临时变量pcur去走循环while (pcur->next) {pcur = pcur->next;}//此时pcur指向最后一个节点pcur->next = node;
}
如果你指针部分的知识学得很扎实,那你一眼就可以看出这段代码有问题了。
这样写问题出在哪里呢?问题在于你传的参数,你觉得把实参传给这个phead是传值调用还是传址调用?显然是传值,因为实参虽然是节点的地址,但是它本质上是一个值,你传过来给 phead 的只是一个值而已,任你怎么改变phead,都与实参无关。而在这个函数中,如果你进行尾插时链表为空,那node的值
只给到了phead
,无法影响到实参。
要解决问题的话,改为传二级指针
就ok了。
而如果链表不为空,那就没啥影响了,因为此时你只需改变 next ,无需改变实参。传值确实没问题,但是为了形式上的统一(避免一下子是一级指针,一下子又是二级指针),所以也传二级指针。
phead 是一级指针,那么二级指针我们就记为pphead,把原本的phead改为 *pphead就ok了。同时我们需要对 pphead 进行断言,因为它为空的话那就不能解引用。
所以正确的代码如下:
void SLPushBack(SLNode** pphead,SLTypeDate x) {assert(pphead); //进行断言,防止传过来的指针为空SLNode* node = SLBuyNode(x);if (*pphead == NULL) { //phead为空说明此时链表为空,那就直接插入*pphead = node; //让node是第一个节点的地址return;}//若不为空,则先找到链表的最后一个节点,再插入SLNode* pcur = *pphead; //老样子,用临时变量pcur去走循环while (pcur->next) {pcur = pcur->next;}//此时pcur指向最后一个节点pcur->next = node;
}
🥝头插
头插就很简单了,不用考虑顺序表为不为空。
void SLPushFront(SLNode** pphead, SLTypeDate x) {assert(pphead);SLNode* node = SLBuyNode(x);node->next = *pphead;*pphead = node;
}
🥝尾删
尾删需要完成2个任务:①释放掉最后一个节点的空间;②将倒数第二个节点的 next 指针置为空。
最后一个节点这个好找,那倒数第二个呢?请看下面代码:
void SLPopBack(SLNode** pphead,SLTypeDate x) {assert(pphead);assert(*pphead); //如果节点地址为空,那么说明节点为空(为空说明没有指向),而节点为空时显然不能删除SLNode* prev = NULL;SLNode* ptail = *pphead;if ((*pphead)->next == NULL) {free(*pphead);*pphead = NULL;return;}while (ptail->next) {prev = ptail;ptail = ptail->next;}prev->next = ptail->next;free(ptail);ptail = NULL;
}
}
我这次弄了两个指针:prev
和ptail
,ptail
经过循环最终指向最后一个节点;而prev
则是指向倒数第二个节点。可以看到,每次循环我在把下一个节点地址赋给 ptail
之前,就把当下ptail
保存到prev
,这样在最后一次循环时 prev
就在倒二了。
然后 if 语句里面的是只有一个节点的情况,为什么要单独把它拿出来讨论呢?因为如果没有这个 if 语句,那么此时 prev 就是NULL,它不能解引用去访问节点里面的 next,更何况此时还是一个空节点。
释放最后一个节点之后,一定要把它的地址置为空,你如果不置空,在尾删这个函数里面确实没问题,但是在打印链表的函数中,循环的终止条件是pcur为空,当pcur走到被删掉的节点时,因为地址不为空,所以会把这里的东西打印出来。
释放掉某块空间,只是把里面的数据给释放掉,但是那块空间仍然存在
🥝头删
头删的思路就是:把第一个节点释放掉,把 phead(*pphead) 给到原先的第二个节点
void SLPopFront(SLNode** pphead) {assert(pphead);assert(*pphead);SLNode* pcur = *pphead;*pphead = (*pphead)->next;free(pcur);pcur = NULL;
}
这里 pcur 可以不置为空,但出于代码规范,所以置空。
🥝指定位置之前插入
既然要在某位置前插入,那就得先找到这个位置,怎么找呢?当然是循环遍历了,先来写下找到我们想要的节点的函数:
SLNode* SLFindNode(SLNode** pphead, SLTypeDate x) {assert(pphead);SLNode* pcur = *pphead;while (pcur) {if (pcur->data == x) {return pcur;}pcur = pcur->next;}return NULL; //找不到就返回空指针
}
然后可以写插入的函数了:
void SLInsert(SLNode** pphead, SLNode* pos, SLTypeDate x) {assert(pphead);assert(*pphead);assert(pos);SLNode* node = SLFindNode(*pphead,pos);SLNode* prev = *pphead;if (*pphead = pos) { //只有一个节点或插入位置位于第一个节点之前的情况node->next = *pphead;*pphead = node;return;}//多个节点的情况while (prev->next != pos) {prev = prev->next;}node->next = pos;prev->next = node;
}
注:你画图就会发现只有一个节点or插入第一个节点之前的位置,这两种情况下 pos 和 *pphead 都指向第一个节点。
🥝指定位置之后插入
这个就比上面那个简单多了,因为现在pos节点已知,那就少了遍历的过程。(pos后面的节点可以通过 next 找到,但是pos之前的节点只能遍历得到)
不过这次需要注意的插入的次序,比如现在节点node和pos已知,插入时,应该先让node的next指向第三个节点,然后再让 pos 的 next 指向 node,如果反过来的话,那第三个节点可就找不到了(即pos->next)
这个函数的实现很简单的,你自己尝试一下。
🥝删除指定节点
如图,要删除pos这个节点
首先我们得找到pos前面的节点 prev,然后让prev->next = pos->next,完事之后这个pos就没啥“利用价值”了,把它 free 掉。
不过前面做了这么多个接口之后,你应该也会考虑到一些特殊情况,比如要删除的pos就是第一个节点。
这种情况下我们就先弄一个pcur 保存第一个节点的地址,然后*pphead = (*pphead)->next,再把pcur指向的空间free掉并置空。
这里有一个要注意的点:解引用操作符优先度比“->”低,所以要用括号给括起来,不然会报错。
void SLErase(SLNode** pphead, SLNode* pos) {assert(*pphead);assert(pphead);assert(pos);if (*pphead == pos) {*pphead = (*pphead)->next;free(pos);pos = NULL;return;}SLNode* prev = *pphead;while (prev->next != pos) {prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;
}
🍌销毁
使用完之后,就要把链表给销毁了。
void SLDestroy(SLNode** pphead) {assert(pphead);SLNode* pcur = *pphead;while (*pphead) {pcur = *pphead;*pphead = (*pphead)->next;free(pcur);pcur = NULL;}
}
🍉源码
🍌头文件:声明部分
#pragma once
#include<stdlib.h>
#include<stdio.h>
#include<assert.h>typedef int SLTypeDate;
typedef struct SListNode {SLTypeDate data;struct SListNode* next;
}SLNode;void SLPrint(SLNode* phead);void SLPushBack(SLNode** pphead, SLTypeDate x);void SLPushFront(SLNode** pphead, SLTypeDate x);void SLPopFront(SLNode** pphead);void SLPopBack(SLNode** pphead);void SLInsert(SLNode** pphead, SLNode* pos, SLTypeDate x);SLNode* SLFindNode(SLNode** pphead, SLTypeDate x);void SLInsertAfter(SLNode* pos, SLTypeDate x);void SLErase(SLNode** pphead, SLNode* pos);void SLDestroy(SLNode** pphead);
🍌源文件:功能实现部分
#include"SList.h"void SLPrint(SLNode* phead) {SLNode* pcur = phead;while (pcur) {printf("%d ->", pcur->data);pcur = pcur->next;}printf("NULL\n");
}SLNode* SLBuyNode(SLTypeDate x) { //申请一个节点,并将想要存储的数据存进去SLNode* node = (SLNode*)malloc(sizeof(SLNode));node->data = x;node->next = NULL;return node;
}void SLPushBack(SLNode** pphead,SLTypeDate x) {assert(pphead);SLNode* node = SLBuyNode(x);if (*pphead == NULL) { //phead为空说明此时链表为空,那就直接插入*pphead = node; //让node是第一个节点的地址return;}//若不为空,则先找到链表的最后一个节点,再插入SLNode* pcur = *pphead; //老样子,用临时变量pcur去走循环while (pcur->next) {pcur = pcur->next;}//此时pcur指向最后一个节点pcur->next = node;
}void SLPushFront(SLNode** pphead, SLTypeDate x) {assert(pphead);SLNode* node = SLBuyNode(x);node->next = *pphead;*pphead = node;
}void SLPopBack(SLNode** pphead) {assert(pphead);assert(*pphead); //如果节点地址为空,那么说明节点为空(为空说明没有指向),而节点为空时显然不能删除SLNode* prev = NULL;SLNode* ptail = *pphead;if ((*pphead)->next == NULL) {free(*pphead);*pphead = NULL;return;}while (ptail->next) {prev = ptail;ptail = ptail->next;}free(ptail);ptail = NULL;
}void SLPopFront(SLNode** pphead) {assert(pphead);assert(*pphead);SLNode* pcur = *pphead;*pphead = (*pphead)->next;free(pcur);//pcur = (*pphead)->next;//free(*pphead);//*pphead = pcur;pcur = NULL;
}SLNode* SLFindNode(SLNode** pphead, SLTypeDate x) {assert(pphead);SLNode* pcur = *pphead;while (pcur) {if (pcur->data == x) {return pcur;}pcur = pcur->next;}return NULL; //找不到就返回空指针
}//在指定位置前插入
void SLInsert(SLNode** pphead, SLNode* pos, SLTypeDate x) {assert(pphead);assert(*pphead);assert(pos);SLNode* node = SLFindNode(*pphead,pos);SLNode* prev = *pphead;if (*pphead = pos) { //只有一个节点或插入位置位于第一个节点之前的情况node->next = *pphead;*pphead = node;return;}//多个节点的情况while (prev->next != pos) {prev = prev->next;}node->next = pos;prev->next = node;
}void SLInsertAfter(SLNode* pos, SLTypeDate x) {assert(pos);SLNode* node = SLBuyNode(x);node->next = pos->next;pos->next = node;
}void SLErase(SLNode** pphead, SLNode* pos) {assert(*pphead);assert(pphead);assert(pos);if (*pphead == pos) {*pphead = (*pphead)->next;free(pos);pos = NULL;return;}SLNode* prev = *pphead;while (prev->next != pos) {prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}void SLDestroy(SLNode** pphead) {assert(pphead);SLNode* pcur = *pphead;while (*pphead) {pcur = *pphead;*pphead = (*pphead)->next;free(pcur);pcur = NULL;}
}
相关文章:

数据结构:单链表
文章目录 🍉前言🍉基本概念🍉链表的分类🍌单链表节点的结构🍌创建节点🍌打印链表🍌插入和删除🥝尾插🥝头插🥝尾删🥝头删🥝指定位置之前…...

官媒代运营:让大众倾听品牌的声音
在当今数字时代,媒体的影响力和多样性远远超出了以往的范畴。品牌和企业越来越依赖媒体来传播信息、建立声誉以及与大众互动。而媒体矩阵成为了现代品牌传播的关键策略,使大众能够倾听品牌的声音。媒体矩阵:多元化的传播渠道 媒体矩阵是指利…...
postgresql 实现计算日期间隔排除周末节假日方案
前置条件:需要维护一张节假日日期表。例如创建holiday表保存当年假期日期 CREATE TABLE holiday (id BIGINT(10) ZEROFILL NOT NULL DEFAULT 0,day TIMESTAMP NULL DEFAULT NULL,PRIMARY KEY (id) ) COMMENT假期表 COLLATEutf8mb4_0900_ai_ci ;返回日期为xx日xx时x…...

金融工作怎么做?低代码如何助力金融行业
10月30日至31日,中央金融工作会议在北京举行。金融是国民经济的“血脉”,是国家核心竞争力的重要组成部分。会议指出,党的十八大以来,在党中央集中统一领导下,金融系统有力支撑经济社会发展大局,坚决打好防…...

基于springboot实现智慧外贸平台系统【项目源码+论文说明】计算机毕业设计
基于springboot实现智慧外贸平台系统演示 摘要 网络的广泛应用给生活带来了十分的便利。所以把智慧外贸管理与现在网络相结合,利用java技术建设智慧外贸平台,实现智慧外贸的信息化。则对于进一步提高智慧外贸管理发展,丰富智慧外贸管理经验能…...

带头+双向+循环链表
前言: 前面我们已经学习了单链表的结构及其功能特点,也了解了单链表在实现一些功能时出现的一些缺点,比如在删除某个节点前面一个节点时就需要再开一个变量来存放前面一个节点的信息,这样就显得不灵活,为了使链表实现功…...

Leetcode_2:两数相加
题目描述: 给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。 请你将两个数相加,并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外ÿ…...

Pytorch实战教程(一)-神经网络与模型训练
0. 前言 人工神经网络 (Artificial Neural Network, ANN) 是一种监督学习算法,其灵感来自人类大脑的运作方式。类似于人脑中神经元连接和激活的方式,神经网络接受输入,通过某些函数在网络中进行传递,导致某些后续神经元被激活,从而产生输出。函数越复杂,网络对于输入的数…...

【MySQL】手把手教你centos7下载MySQL
centos7下载MySQL 前言正式开始卸载不需要的环境(如果你之前没有安装过数据库相关的东西可以跳过)下载mysql登录mysql登陆⽅法⼀【不⾏就下⼀个】登陆⽅法⼆【不⾏就下⼀个】登录方式三 前言 安装和卸载MySQL都用系统的root权限,更方便一点&…...
openlayers
OpenLayers使用_openlayers中文官网-CSDN博客...

力扣每日一道系列 --- LeetCode 88. 合并两个有序数组
📷 江池俊: 个人主页 🔥个人专栏: ✅数据结构探索 ✅LeetCode每日一道 🌅 有航道的人,再渺小也不会迷途。 文章目录 思路1:暴力求解思路2:原地合并 LeetCode 88. 合并两个有序数组…...

Android Studio(项目收获)
取消按钮默认背景色 像按钮默认背景色为深蓝色,即使使用了background属性指定颜色也不能生效。 参考如下的解决方法: 修改/res/values/themes.xml中的指定内容如下: <style name"Theme.TianziBarbecue" parent"Theme.Mater…...
MQ写满的情况如何处理?
**MQ(Message Queue)**写满的情况通常指消息队列中的存储空间已经被用尽,无法再接收新的消息。处理MQ写满的情况涉及到多个方面,包括监控、调整配置、增加资源、以及处理积压消息等。下面是一些处理MQ写满的 常见方法:…...
点名(缺失的数字),剑指offer,力扣
目录 我们直接看题解吧: 审题目事例提示: 方法: 解题思路(二分法): 代码: 方法二:直接遍历 题目地址 LCR 173. 点名 - 力扣(LeetCode) 今天刷点名(…...

云安全—Dashboard 攻击面
0x00 前言 众所周知,如果只是一味的REST接口或者命令行话的操作方式,就会变相的提高操作门款,并且不会有很好的呈现方式,所以就有了web ui的方式,也就是Dashboar面板,本篇主要讨论一下关于Dashboar面板的概…...

FCOS难点记录
FCOS 中有计算 特征图(Feature map中的每个特征点到gt_box的左、上、右、下的距离) 1、特征点到gt_box框的 左、上、右、下距离计算 x coords[:, 0] # h*w,2 即 第一列y coords[:, 1] l_off x[None, :, None] - gt_boxes[..., 0][:, No…...

java通过FTP跨服务器动态监听读取指定目录下文件数据
背景: 1、文件数据在A服务器(windows)(不定期在指定目录下生成),项目应用部署在B服务器(Linux); 2、项目应用在B服务器,监听A服务器指定目录,有新…...

5G边缘计算网关的功能及作用
5G边缘计算网关具有多种功能。 首先,它支持智能云端控制,可以通过5G/4G/WIFI等无线网络将采集的数据直接上云,实现异地远程监测控制、预警通知、报告推送和设备连接等工作。 其次,5G边缘计算网关可以采集各种数据,包…...

阿里云AIGC小说生成【必得京东卡】
任务步骤 此文真实可靠不做虚假宣传,绝对真实,可截图为证。 领取任务 链接(复制到wx打开):#小程序://ITKOL/1jw4TX4ZEhykWJd 教程实践 打开函数计算控制台 应用->创建应用->人工智能->通义千问 AI 助手-…...

数据结构之AVL树
map/multimap/set/multiset这几个容器有个共同点是: 其底层都是按照二叉搜索树来实现的,但是普通的二叉搜索树有其自身的缺陷, 假如往树中插入的元素有序或者接近有序, 二叉搜索树就会退化成单支树, 时间复杂度会退化成O(N),因此map、set等关联式容器的底层结构是对二叉树进行了…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...

初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...
Java毕业设计:WML信息查询与后端信息发布系统开发
JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发,实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构,服务器端使用Java Servlet处理请求,数据库采用MySQL存储信息࿰…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...