当前位置: 首页 > news >正文

济南房产信息网站官网快速优化seo

济南房产信息网站官网,快速优化seo,个人网站可以做信息网站吗,阳江seo网站推广文章目录 🍉前言🍉基本概念🍉链表的分类🍌单链表节点的结构🍌创建节点🍌打印链表🍌插入和删除🥝尾插🥝头插🥝尾删🥝头删🥝指定位置之前…

文章目录

  • 🍉前言
  • 🍉基本概念
  • 🍉链表的分类
    • 🍌单链表节点的结构
    • 🍌创建节点
    • 🍌打印链表
    • 🍌插入和删除
      • 🥝尾插
      • 🥝头插
      • 🥝尾删
      • 🥝头删
      • 🥝指定位置之前插入
      • 🥝指定位置之后插入
      • 🥝删除指定节点
    • 🍌销毁
  • 🍉源码
    • 🍌头文件:声明部分
    • 🍌源文件:功能实现部分

🍉前言

喜茶的果汁茶有这样的一句宣传语:一半果汁一半茶。用这个来形容单链表那可是再合适不过了一一 一半数据一半指针。
在这里插入图片描述

🍉基本概念

链表是一种数据结构,采用链式存储一一在内存中不是连续存储的,各元素的逻辑顺序是通过链表中的指针链接次序实现的。它包含数据域指针域,分别保存数据和下一个节点的地址。
在这里插入图片描述
如果要创建节点,一般是在堆上申请。


🍉链表的分类

链表结构多种多样,有三种分类,这些分类进行排列组合共有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;
}
}

我这次弄了两个指针:prevptailptail经过循环最终指向最后一个节点;而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;}
}
http://www.dt0577.cn/news/3849.html

相关文章:

  • wordpress custom css手机网站排名优化软件
  • 做兼职上什么网站营业推广案例
  • 今天合肥刚刚发生的重大新闻百度seo2022
  • 昆山兼职做网站2024新闻热点摘抄
  • 静态企业网站下载成都专业的整站优化
  • 最珠海app下载官网专业优化网站排名
  • 推广方案是什么太原seo软件
  • 怎么帮公司做网站建设营销案例100例
  • 做企业官网好吗2023网站seo
  • 卖酒的网站做线下怎么做裤子seo标题优化关键词
  • 小型网站制作免费seo诊断
  • 深圳招转行网站开发实习生真的吗本地服务推广平台哪个好
  • 公司做两个网站网络推广方案范文
  • 昆山网站建设详细方案成都疫情最新消息
  • 做暧暧小视频有声音的网站2021年网络营销案例
  • 乌海网站建设北京百度推广公司
  • google建站如何开展网络营销
  • 个人博客html代码青岛关键词优化报价
  • wordpress更改语言设置网站关键词优化wang
  • 汕头网站推广seo怎样做好竞价推广
  • 网站权重等级指数平台
  • 自己做电台直播的网站seo优化方法
  • 网站建设与网页设计课程设计东莞营销外包公司
  • 网站建设草图方案网站指数查询
  • jsp书城网站开发苏州关键词优化搜索排名
  • 杭州哪家做外贸网站好怎么才能在百度上做引流呢
  • 商洛做网站多少钱公司网页设计模板
  • 哪家公司建网站最好查收录
  • 网站英文版建设品牌宣传的推广
  • 移动端网站建设软文营销是什么