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

网上购物系统源码关键词优化建议

网上购物系统源码,关键词优化建议,网页截图快捷键ctrl加什么,深圳市宝安区怎么样目录 一、链表的八种结构 二、带头双向循环链表的实现 1.链表的定义 2.链表的接口定义 3.接口的具体实现 三、带头双向循环链表的完整代码 四、顺序表和链表的区别 一、链表的八种结构 我们已经知道链表可以有以下三种分法 而这三种结构又可以排列组合,形成八…

目录

一、链表的八种结构

二、带头双向循环链表的实现

1.链表的定义

2.链表的接口定义

3.接口的具体实现

三、带头双向循环链表的完整代码

四、顺序表和链表的区别


一、链表的八种结构

我们已经知道链表可以有以下三种分法

 

 

而这三种结构又可以排列组合,形成八种结构

其中我们最常见的就是无头单向非循环链表和带头双向循环链表

1.无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。
2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了

二、带头双向循环链表的实现

1.链表的定义

对于这个链表,我们需要使用两个指针来控制,一个是前驱指针,一个是后继指针

typedef int LTDateType;
typedef struct ListNode
{struct ListNode* prev;struct ListNode* next;LTDateType data;
}ListNode;

2.链表的接口定义

如下代码所示,是我们需要实现的接口

//链表的初始化
ListNode* ListCreat();
//链表的打印
void ListPrint(ListNode* phead);
//链表的尾插
void ListPushBack(ListNode* phead, LTDateType x);
//链表的头插
void ListPushFront(ListNode* phead, LTDateType x);
//链表是否为空
bool ListEmpty(ListNode* phead);
//链表的尾删
void ListPopBack(ListNode* phead);
//链表的头删
void ListPopFront(ListNode* phead);
//链表的查找
ListNode* ListFind(ListNode* phead, LTDateType x);
//链表在pos前面的插入
void ListInsert(ListNode* pos, LTDateType x);
//链表在pos位置处的删除
void ListErase(ListNode* pos);
//链表的销毁
void ListDestroy(ListNode* phead);

3.接口的具体实现

1.链表的初始化

ListNode* BuyListNode(LTDateType x)
{ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));if (newnode == NULL){perror("malloc");return NULL;}newnode->data = x;newnode->next = NULL;newnode->prev = NULL;return newnode;
}
//链表的初始化
ListNode* ListCreat()
{ListNode* phead = BuyListNode(-1);phead->next = phead;phead->prev = phead;return phead;
}

如上代码所示,对于这个初始化,他与单链表不同,单链表其实是没有必要去专门做一个初始化函数的。但同时我们需要传二级指针来解决一些问题

对于双向带头循环链表,我们就有必要设置一个初始化函数了。

我们有两种思路去构建这个函数

一种是在主函数中直接定义一个指针,然后通过传这个指针的地址去构建。但是这样我们就需要传二级指针了。

另外一种是我们可以利用返回值,这样我们就直接避免了传参数了。

2.链表的打印

//链表的打印
void ListPrint(ListNode* phead)
{assert(phead);ListNode* cur = phead->next;printf("<=head=>");while (cur != phead){printf("%d<=>", cur->data);cur = cur->next;}printf("\n");
}

如上代码所示,链表的打印与单链表的打印是一样的,没有什么太大的区别,我们可以利用printf来更加形象的表达双向循环的意思

3.链表的尾插

//链表的尾插
void ListPushBack(ListNode* phead, LTDateType x)
{assert(phead);ListNode* newnode = BuyListNode(x);ListNode* tail = phead->prev;tail->next = newnode;newnode->prev = tail;newnode->next = phead;phead->prev = newnode;
}

对于尾插就比单链表简单多了,因为有了头节点,我们就不需要处理是否为空的状态了

4.链表的头插

//链表的头插
void ListPushFront(ListNode* phead, LTDateType x)
{assert(phead);ListNode* newnode = BuyListNode(x);ListNode* first = phead->next;phead->next = newnode;newnode->prev = phead;newnode->next = first;first->prev = newnode;
}

头插和尾插基本上是非常类似的,这也得益于这种链表结构基本是无死角的

5.链表的头删尾删以及是否为空

//链表是否为空
bool ListEmpty(ListNode* phead)
{assert(phead);return phead->next == phead;
}
//链表的尾删
void ListPopBack(ListNode* phead)
{assert(phead);assert(!ListEmpty(phead));ListNode* tail = phead->prev;ListNode* tailPrev = tail->prev;phead->prev = tailPrev;tailPrev->next = phead;free(tail);tail = NULL;
}
//链表的头删
void ListPopFront(ListNode* phead)
{assert(phead);assert(!ListEmpty(phead));ListNode* first = phead->next;ListNode* firstNext = first->next;phead->next = firstNext;firstNext->prev = phead;free(first);first = NULL;
}

对于头删和尾删其实也是一致的思路,我们只需要改变结点即可,值得注意的是,我们需要判断链表是否为空, 如果为空肯定不可以删除

6.链表的查找

//链表的查找
ListNode* ListFind(ListNode* phead,LTDateType x)
{assert(phead);assert(!ListEmpty(phead));ListNode* cur = phead->next;while (cur != phead){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}

这个也是比较简单的,他的思路与单链表的是一样的

7.链表在pos位置之前插入

//链表在pos前面的插入
void ListInsert(ListNode* pos, LTDateType x)
{assert(pos);ListNode* newnode = BuyListNode(x);ListNode* prev = pos->prev;prev->next = newnode;newnode->prev = prev;newnode->next = pos;pos->prev = newnode;
}

这个的话,我们更能体会到他相对于单链表的优势了。我们只需要pos位置即可。然后思路和头插尾插是一致的

8.链表在pos位置的删除

//链表在pos位置处的删除
void ListErase(ListNode* pos)
{assert(pos);ListNode* prev = pos->prev;ListNode* next = pos->next;prev->next = next;next->prev = prev;free(pos);pos = NULL;
}

对于删除,也是与头删尾删思路一致的

9.链表的销毁

//链表的销毁
void ListDestroy(ListNode* phead)
{assert(phead);ListNode* cur = phead->next;while (cur != phead){ListNode* next = cur->next;free(cur);cur = next;}free(phead);phead = NULL;
}

对于链表的销毁思路与单链表也是一致的,只需要循环遍历即可

三、带头双向循环链表的完整代码

List.h

#pragma once
#include<stdio.h>
#include<malloc.h>
#include<assert.h>
#include<stdbool.h>
typedef int LTDateType;
typedef struct ListNode
{struct ListNode* prev;struct ListNode* next;LTDateType data;
}ListNode;//链表的初始化
ListNode* ListCreat();
//链表的打印
void ListPrint(ListNode* phead);
//链表的尾插
void ListPushBack(ListNode* phead, LTDateType x);
//链表的头插
void ListPushFront(ListNode* phead, LTDateType x);
//链表是否为空
bool ListEmpty(ListNode* phead);
//链表的尾删
void ListPopBack(ListNode* phead);
//链表的头删
void ListPopFront(ListNode* phead);
//链表的查找
ListNode* ListFind(ListNode* phead, LTDateType x);
//链表在pos前面的插入
void ListInsert(ListNode* pos, LTDateType x);
//链表在pos位置处的删除
void ListErase(ListNode* pos);
//链表的销毁
void ListDestroy(ListNode* phead);

List.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"List.h"
ListNode* BuyListNode(LTDateType x)
{ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));if (newnode == NULL){perror("malloc");return NULL;}newnode->data = x;newnode->next = NULL;newnode->prev = NULL;return newnode;
}
//链表的初始化
ListNode* ListCreat()
{ListNode* phead = BuyListNode(-1);phead->next = phead;phead->prev = phead;return phead;
}
//链表的打印
void ListPrint(ListNode* phead)
{assert(phead);ListNode* cur = phead->next;printf("<=head=>");while (cur != phead){printf("%d<=>", cur->data);cur = cur->next;}printf("\n");
}
//链表的尾插
void ListPushBack(ListNode* phead, LTDateType x)
{assert(phead);ListNode* newnode = BuyListNode(x);ListNode* tail = phead->prev;tail->next = newnode;newnode->prev = tail;newnode->next = phead;phead->prev = newnode;
}
//链表的头插
void ListPushFront(ListNode* phead, LTDateType x)
{assert(phead);ListNode* newnode = BuyListNode(x);ListNode* first = phead->next;phead->next = newnode;newnode->prev = phead;newnode->next = first;first->prev = newnode;
}
//链表是否为空
bool ListEmpty(ListNode* phead)
{assert(phead);return phead->next == phead;
}
//链表的尾删
void ListPopBack(ListNode* phead)
{assert(phead);assert(!ListEmpty(phead));ListNode* tail = phead->prev;ListNode* tailPrev = tail->prev;phead->prev = tailPrev;tailPrev->next = phead;free(tail);tail = NULL;
}
//链表的头删
void ListPopFront(ListNode* phead)
{assert(phead);assert(!ListEmpty(phead));ListNode* first = phead->next;ListNode* firstNext = first->next;phead->next = firstNext;firstNext->prev = phead;free(first);first = NULL;
}
//链表的查找
ListNode* ListFind(ListNode* phead,LTDateType x)
{assert(phead);assert(!ListEmpty(phead));ListNode* cur = phead->next;while (cur != phead){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}
//链表在pos前面的插入
void ListInsert(ListNode* pos, LTDateType x)
{assert(pos);ListNode* newnode = BuyListNode(x);ListNode* prev = pos->prev;prev->next = newnode;newnode->prev = prev;newnode->next = pos;pos->prev = newnode;
}
//链表在pos位置处的删除
void ListErase(ListNode* pos)
{assert(pos);ListNode* prev = pos->prev;ListNode* next = pos->next;prev->next = next;next->prev = prev;free(pos);pos = NULL;
}
//链表的销毁
void ListDestroy(ListNode* phead)
{assert(phead);ListNode* cur = phead->next;while (cur != phead){ListNode* next = cur->next;free(cur);cur = next;}free(phead);phead = NULL;
}

Test.c

#define _CRT_SECURE_NO_WARNINGS 1#include"List.h"void TestList1()
{ListNode* plist = ListCreat();ListPushBack(plist, 1);ListPushBack(plist, 2);ListPushBack(plist, 3);ListPushBack(plist, 4);ListPushBack(plist, 5);ListPrint(plist);ListPushFront(plist, 6);ListPushFront(plist, 7);ListPushFront(plist, 8);ListPushFront(plist, 9);ListPushFront(plist, 10);ListPrint(plist);ListPopBack(plist);ListPopBack(plist);ListPopBack(plist);ListPopBack(plist);ListPrint(plist);ListPopFront(plist);ListPopFront(plist);ListPopFront(plist);ListPopFront(plist);ListPrint(plist);}
void TestList2()
{ListNode* plist = ListCreat();ListPushBack(plist, 1);ListPushBack(plist, 2);ListPushBack(plist, 3);ListPushBack(plist, 4);ListPushBack(plist, 5);ListPrint(plist);ListNode* pos = ListFind(plist, 3);(pos->data) *= 10;ListPrint(plist);ListInsert(pos, 20);ListPrint(plist);ListErase(pos);ListPrint(plist);}
int main()
{//TestList1();TestList2();return 0;
}

四、顺序表和链表的区别

不同点顺序表链表
存储空间上物理上一定连续逻辑上连续,但物理上不一定连续
随机访问支持,O(1)不支持,O(N)
任意位置插入或删除元素可能需要搬移元素,效率低,O(N)只需要改变指针指向
插入动态顺序表,空间不够时需要扩容没有容量的概念
应用场景元素高效存储+频繁访问任意位置插入和删除频繁
缓存利用率

如上表所示,是顺序表和链表的区别

在这里我们需要注意缓存利用率这个东西。

对于我们的顺序表/链表,想要实现他的数据访问、遍历、修改等

这些数据是由cpu来进行访问的。

也就是这些访问的指令都是cpu执行的。从而访问打印等操作。这时候cpu就需要去访问内存

而cpu其实是不能直接访问内存的,这是因为cpu太快了,内存太慢了

为了解决这个问题,就有了三级缓存,即cpu高速缓存这个东西。

也就是说cpu会先访问缓存,最高级的缓存再次缓存其实就是命中。

在计算机中有一个局部性原理,在访问某个位置的数据的时候,大概率要访问后面的数据。因此他就会将这个数据后面的数据也顺便加载进去。

这时候由于顺序表是连续的,后面的都直接被命中了。所以他的缓存利用率高

而链表他访问某个数据的时候也会加载一长段,但是后面的不一定会用。所以就会浪费,也会导致缓存污染。所以缓存利用率低。也就是他不仅没有起到正向作用,反而起到了反向作用。


本节内容到此位置

如果对你有帮助的话,不要忘记点赞加收藏哦!!!

http://www.yayakq.cn/news/101643/

相关文章:

  • 肯达建设网站版面设计素材
  • 德阳网站建设 选哪家好邢台做网站的
  • 网站网页直播怎么做家教网站开发
  • 把网站做成静态页面深圳大型网站建设公司
  • 品牌网站设计制作找哪家wordpress4.5.2主题
  • 简述新建站点的步骤黄页88网官网登录
  • 做自媒体的素材网站wordpress 多域名绑定
  • 做网站天津移动互联网开发平台有哪些
  • 民营医院网站建设牡丹江 网站建设
  • dede手机网站模板哦400电话西安网站制作 彩铃制作
  • 西安网站群搭建秦皇岛微信推广平台
  • 做网页的编程语言沈阳沈河seo网站排名优化
  • 站群子网站开发wordpress错误
  • 网站建设和seo网站建设所需的软件
  • 烟台企业建站系统模板晋江网站设计泉州名扬专业
  • 依宝诺手表官方网站关于网站建设项目的投诉函
  • 中国公司网站建设方案2023年10月爆发新冠
  • 海南电商网站建设wordpress公众号模板下载
  • 网站域名更换相应内容好的设计师互动网站
  • 品牌网站设计网站有口碑的徐州网站开发
  • 阿里云网站访问不了怎么办做徽标的网站
  • 织梦网站环境室内设计网课
  • 做网站如何配置自己的电脑太原百度快速排名提升
  • 网站安全代维做化学式的网站
  • 一个网站从开发到上线的过程软文范例大全100
  • 东莞市官网网站建设新产品代理
  • 做物流网站费用多少网站开发从入门到精通
  • 商务网站建设详细步骤衡阳网站设计ss0734
  • 网站添加二级域名上海app开发推荐中伟科
  • 网站图标怎么做的什么网站做ppt模板