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

有哪些是外国人做的网站吗艺点意创官网

有哪些是外国人做的网站吗,艺点意创官网,个人怎么做自媒体,佛山旺道seo【数据结构】线性表--链表(二) 一.前情回顾二.知识补充1.单向链表,双向链表2.带头结点,不带头结点3.循环链表,不循环链表 三.带头双向循环链表的实现1.申请新结点:2.链表初始化:3.尾插函数&…

【数据结构】线性表--链表(二)

  • 一.前情回顾
  • 二.知识补充
    • 1.单向链表,双向链表
    • 2.带头结点,不带头结点
    • 3.循环链表,不循环链表
  • 三.带头双向循环链表的实现
    • 1.申请新结点:
    • 2.链表初始化:
    • 3.尾插函数:
    • 4.头插函数:
    • 5.头删函数:
    • 6.尾删函数:
    • 7.查找函数:
    • 8.在pos结点前插入函数:
    • 9.删除pos结点的值函数:
    • 10.判空函数:
    • 11.销毁函数:
  • 四.总结
    • 1.头文件(声明链表的结构,操作等,起到目录作用):
    • 2.源文件(具体实现各种操作):
    • 3.测试文件(对各个函数功能进行测试):

一.前情回顾

上篇文章主要讲述了单链表的特点及各种增删查改等各种操作,我们会发现单链表在某些操作中需要寻找前驱结点,然而单链表中并没有直接指向前驱结点的指针,这样使得有些操作会有点麻烦,因此可以增加一个指向前驱结点的指针域,成为双链表。

二.知识补充

同时我们可以在链表的第一个结点前增加一个哨兵位的头结点,为了操作的统一和方便而设立。它的数据域并不保存实际的有效数据,指针域指向链表的第一个结点。
除此之外,链表还可分为循环链表和不循环链表。循环链表的最后一个结点又指向头结点,不循环链表的尾结点指向空。
因此链表总共可分为三类:

1.单向链表,双向链表

在这里插入图片描述

2.带头结点,不带头结点

在这里插入图片描述

3.循环链表,不循环链表

在这里插入图片描述
所以以上三类可以任意结合出来八种链表,上篇文章讲述的单链表具体是不带头单向不循环链表。
本篇文章则要讲述带头双向循环链表。

三.带头双向循环链表的实现

tips:涉及增删查改的各种操作时,可以自己画图看看如何修改各个指针的指向,便于理解。

1.申请新结点:

申请新结点即先用malloc函数申请一块结点大小的空间,然后将数值赋给数值域,指针都置为空,然后将新结点返回。
malloc函数不清楚的可以去看我之前的C语言动态内存管理那篇文章: https://blog.csdn.net/2401_85032912/article/details/142744593?spm=1001.2014.3001.5501)

//申请新结点
ListNode* BuyListNode(LTDataType x)
{ListNode* NewNode = (ListNode*)malloc(sizeof(ListNode));NewNode->data = x;NewNode->prev = NULL;NewNode->next = NULL;return NewNode;
}

2.链表初始化:

链表初始化需要先申请一个头结点,然后将头结点的prev指针和next指针均指向自己(因为是双向链表),数值域可以给一个不具有实际意义的值。
在这里插入图片描述

初始化方式有两种,第一种是通过传二级指针修改头结点;第二种是直接申请新结点,修改完之后作为返回值返回。

//初始化函数
ListNode* ListInit()
{//初始链表为空时,需将头结点的prev指针和next指针都指向自己ListNode* phead = BuyListNode(-1);phead->next = phead->prev = phead;return phead;
}
//或:
/*
void ListInit(ListNode** pphead)
{//初始链表为空时,需将头结点的prev指针和next指针都指向自己*pphead = BuyListNode(-1);(*pphead)->next = (*pphead)->prev = *pphead;
}
*/

3.尾插函数:

因为可以通过头结点找到第一个有效结点,所以不需要传二级指针(后续操作同理)。
双向链表尾插时不需要从头遍历到尾结点,因为头结点的prev指针即指向最后一个结点。
如图:
在这里插入图片描述
因此在尾插时,只需要通过头结点找到最后一个结点,将新结点的prev指针指向最后一个结点;next指针指向头结点;让最后一个结点的next指针指向新的结点;将头结点的prev指针指向新的结点。

//尾插函数
void ListPushBack(ListNode* phead, LTDataType x)
{assert(phead); ListNode* newnode = BuyListNode(x);ListNode* tail = phead->prev;//找到最后一个结点newnode->prev = tail;//新结点的prev指向最后一个结点newnode->next = phead;//新结点的next指向头结点tail->next = newnode;//最后一个结点的next指向新结点phead->prev = newnode;//头结点的prev指向新结点//此时新结点成为当前链表的最后一个结点
}

此时新结点就变成最后一个结点。

4.头插函数:

头插时先找到第一个结点,将新结点的next指针指向第一个结点,prev指针指向头结点,再第一个结点的next指针指向修改为新结点,最后修改头结点的next指针指向的结点。

//头插函数
void ListPushFront(ListNode* phead, LTDataType x)
{assert(phead);ListNode* newnode = BuyListNode(x);ListNode* first = phead->next;//找到第一个结点newnode->next = first;newnode->prev = phead;first->prev = newnode;phead->next = newnode;
}

因为将第一个结点找出来了,所以不需要考虑修改指针的先后顺序,否则需要考虑顺序问题。

5.头删函数:

头删时注意链表为空时不能删除。不为空时,找出第一个结点和第二个结点,将头结点的next指针指向第二个结点,将第二个结点的prev指针指向头结点,然后释放第一个结点指向的空间,指针置为空。

//头删函数
void ListPopFront(ListNode* phead)
{assert(phead);//链表为空时不能删除(或者使用断言)if (phead->next == phead)return;else{ListNode* first = phead->next;//找到第一个结点ListNode* second = first->next;//找到第二个结点phead->next = second;second->prev = phead;free(first);first = NULL;}
}

6.尾删函数:

链表为空时同样不能删除。不为空时找出最后一个结点和倒数第二个结点,将头结点的prev指针指向倒数第二个结点,倒数第二个结点的next指针指向头结点,释放最后一个结点,并置为空。

//尾删函数
void ListPopBack(ListNode* phead)
{assert(phead);//链表为空时不能删除(或者使用断言)if (phead->next == phead)return;else{ListNode* tail = phead->prev;//找到最后一个结点ListNode* prev = tail->prev;//找到倒数第二个结点phead->prev = prev;prev->next = phead;free(tail);tail = NULL;}
}

7.查找函数:

链表为空时直接返回NULL,链表不为空时,从第一个结点开始遍历到最后一个结点,若结点的值与查找的值相等,返回该结点,否则返回NULL。

ListNode* ListFind(ListNode* phead, LTDataType x)
{assert(phead);//链表为空时,返回NULLif (phead->next == phead)return NULL;else{ListNode* cur = phead->next;while (cur != phead){if (cur->data == x)return cur;elsecur = cur->next;}return NULL;}
}

8.在pos结点前插入函数:

首先申请一个新结点,然后找到pos结点的前一个结点,将前一个结点的next指针指向新结点,pos结点的prev指针也指向新结点,新结点的prev指针指向前一个结点,next指针指向pos结点。

//在pos位置前插入结点
void ListInsert(ListNode* pos, LTDataType x)
{assert(pos);ListNode* newnode = BuyListNode(x);ListNode* prev = pos->prev;//找到前一个结点prev->next = newnode;pos->prev = newnode;newnode->prev = prev;newnode->next = pos;
}

实现完该函数之后,头插、尾插函数可以直接复用该函数。
头插即在头结点的next位置插入,可以直接写成ListInsert(phead->next, x);
尾插即在头结点前插入,可以直接写成ListInsert(phead, x);

9.删除pos结点的值函数:

删除pos结点时,如果该链表仅剩最后一个头结点,不能删除。
其余情况先找到pos结点的前一个结点和后一个结点,将前一个结点的next指针指向后一个结点,后一个结点的prev指针指向前一个结点,然后释放pos结点,并置为空。

//删除pos位置的结点
void ListErase(ListNode* pos)
{assert(pos);//如果该链表仅剩最后一个头结点,不能删除if (pos->next == pos)return;ListNode* prev = pos->prev;ListNode* next = pos->next;prev->next = next;next->prev = prev;free(pos);pos = NULL;
}

实现完该函数后,头删和尾删函数即可复用该函数。
头删即把头结点的next指针指向的结点删除可以直接写成 ListErase(phead->next);
尾删即把头结点的prev指针指向的结点删除,可以直接写成ListErase(phead->prev);

10.判空函数:

判空函数很简单,如果头结点的next指针指向的结点还是自己即为空,返回true,否则不为空,返回false。

//判空函数
bool ListEmpty(ListNode* phead)
{if (phead->next == phead)return true;return false;
}

11.销毁函数:

销毁函数比较简单,找到第一个结点之后,进入循环,找到下一个结点,然后释放当前结点,直到结点==头结点退出循环,最后释放头结点,并置为空。

//销毁函数
void ListDestory(ListNode* phead)
{assert(phead);ListNode* cur = phead->next;while (cur != phead){ListNode* tmp = cur;cur = cur->next;free(tmp);tmp = NULL;}free(phead);phead = NULL;
}

四.总结

带头双向循环链表可能结构稍微复杂,但是优点是在任意位置插入删除结点的时间复杂度都是O(1), 缺点是以结点为存储单位,不支持随机访问。
以下是全部源码实现:

1.头文件(声明链表的结构,操作等,起到目录作用):

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int LTDataType;
typedef struct ListNode
{LTDataType data;//存放的数据元素struct ListNode* prev;//前驱指针struct ListNode* next;//后继指针
}ListNode;
//C++中双向链表叫List,所以起名也叫List//申请新结点
ListNode* BuyListNode(LTDataType x);//打印函数(方便调试观看)
void PrintList(ListNode* phead);//初始化函数
ListNode* ListInit();
//或者:void ListInit(ListNode** pphead);//销毁函数(通过头结点可以链表第一个有效节点进行修改,因此不需要传二级指针)
void ListDestory(ListNode* phead);//尾插函数
void ListPushBack(ListNode* phead, LTDataType x);//头插函数
void ListPushFront(ListNode* phead, LTDataType x);//头删函数
void ListPopFront(ListNode* phead);//尾删函数
void ListPopBack(ListNode* phead);//查找函数
ListNode* ListFind(ListNode* phead, LTDataType x);//在pos位置前插入结点
void ListInsert(ListNode* pos, LTDataType x);//删除pos位置的结点
void ListErase(ListNode* pos);//判空函数
bool ListEmpty(ListNode* phead);

2.源文件(具体实现各种操作):

#include"List.h"//申请新结点
ListNode* BuyListNode(LTDataType x)
{ListNode* NewNode = (ListNode*)malloc(sizeof(ListNode));NewNode->data = x;NewNode->prev = NULL;NewNode->next = NULL;return NewNode;
}//打印函数(方便调试观看)
void PrintList(ListNode* phead)
{ListNode* cur = phead->next;while (cur != phead){printf("%d ", cur->data);cur = cur->next;}printf("NULL\n");
}//初始化函数
ListNode* ListInit()
{//初始链表为空时,需将头结点的prev指针和next指针都指向自己ListNode* phead = BuyListNode(-1);phead->next = phead->prev = phead;return phead;
}
//或:
/*void ListInit(ListNode** pphead)
{//初始链表为空时,需将头结点的prev指针和next指针都指向自己*pphead = BuyListNode(-1);(*pphead)->next = (*pphead)->prev = *pphead;
}*///销毁函数
void ListDestory(ListNode* phead)
{assert(phead);ListNode* cur = phead->next;while (cur != phead){ListNode* tmp = cur;cur = cur->next;free(tmp);tmp = NULL;}free(phead);phead = NULL;
}//尾插函数
void ListPushBack(ListNode* phead, LTDataType x)
{assert(phead); //ListNode* newnode = BuyListNode(x);//ListNode* tail = phead->prev;//找到最后一个结点//newnode->prev = tail;//新结点的prev指向最后一个结点//newnode->next = phead;//新结点的next指向头结点//tail->next = newnode;//最后一个结点的next指向新结点//phead->prev = newnode;//头结点的prev指向新结点//或者ListInsert(phead, x);
}//头插函数
void ListPushFront(ListNode* phead, LTDataType x)
{assert(phead);//ListNode* newnode = BuyListNode(x);//ListNode* first = phead->next;//找到第一个结点//newnode->next = first;//newnode->prev = phead;//first->prev = newnode;//phead->next = newnode;//或者ListInsert(phead->next, x);
}//头删函数
void ListPopFront(ListNode* phead)
{assert(phead);链表为空时不能删除(或者使用断言)//if (phead->next == phead)//	return;//else//{//	ListNode* first = phead->next;//找到第一个结点//	ListNode* second = first->next;//找到第二个结点//	phead->next = second;//	second->prev = phead;//	free(first);//	first = NULL;//}//或者ListErase(phead->next);
}//尾删函数
void ListPopBack(ListNode* phead)
{assert(phead);链表为空时不能删除(或者使用断言)//if (phead->next == phead)//	return;//else//{//	ListNode* tail = phead->prev;//找到最后一个结点//	ListNode* prev = tail->prev;//找到倒数第二个结点//	phead->prev = prev;//	prev->next = phead;//	free(tail);//	tail = NULL;//}//或者ListErase(phead->prev);
}//查找函数
ListNode* ListFind(ListNode* phead, LTDataType x)
{assert(phead);//链表为空时,返回NULLif (phead->next == phead)return NULL;else{ListNode* cur = phead->next;while (cur != phead){if (cur->data == x)return cur;elsecur = cur->next;}return NULL;}
}//在pos位置前插入结点
void ListInsert(ListNode* pos, LTDataType x)
{assert(pos);ListNode* newnode = BuyListNode(x);ListNode* prev = pos->prev;//找到前一个结点prev->next = newnode;pos->prev = newnode;newnode->prev = prev;newnode->next = pos;
}//删除pos位置的结点
void ListErase(ListNode* pos)
{assert(pos);//如果该链表仅剩最后一个头结点,不能删除if (pos->next == pos)return;ListNode* prev = pos->prev;ListNode* next = pos->next;prev->next = next;next->prev = prev;free(pos);pos = NULL;
}//判空函数
bool ListEmpty(ListNode* phead)
{if (phead->next == phead)return true;return false;
}

3.测试文件(对各个函数功能进行测试):

#include"List.h"//测试初始化函数
void test01()
{ListNode* phead = ListInit();PrintList(phead);ListDestory(phead);
}//测试尾插函数
void test02()
{ListNode* phead = ListInit();ListPushBack(phead, 1);ListPushBack(phead, 2);ListPushBack(phead, 3);ListPushBack(phead, 4);PrintList(phead);ListDestory(phead);
}//测试头插函数
void test03()
{ListNode* phead = ListInit();ListPushFront(phead, 1);ListPushFront(phead, 2);ListPushFront(phead, 3);ListPushFront(phead, 4);PrintList(phead);ListDestory(phead);
}//测试头删函数
void test04()
{ListNode* phead = ListInit();ListPushBack(phead, 1);ListPushBack(phead, 2);ListPushBack(phead, 3);ListPushBack(phead, 4);PrintList(phead);ListPopFront(phead);PrintList(phead);ListPopFront(phead);PrintList(phead);ListPopFront(phead);PrintList(phead);ListPopFront(phead);PrintList(phead);ListPopFront(phead);PrintList(phead);ListDestory(phead);
}//测试尾删函数
void test05()
{ListNode* phead = ListInit();ListPushBack(phead, 1);ListPushBack(phead, 2);ListPushBack(phead, 3);ListPushBack(phead, 4);PrintList(phead);ListPopBack(phead);PrintList(phead);ListPopBack(phead);PrintList(phead);ListPopBack(phead);PrintList(phead);ListPopBack(phead);PrintList(phead);ListPopBack(phead);PrintList(phead);ListDestory(phead);
}//测试查找函数
void test06()
{ListNode* phead = ListInit();ListPushBack(phead, 1);ListPushBack(phead, 2);ListPushBack(phead, 3);ListPushBack(phead, 4);PrintList(phead);//查找是否有值为1的结点if (ListFind(phead,1) != NULL){printf("存在值为1的结点\n");}else{printf("不存在值为1的结点\n");}//查找是否有值为57的结点if (ListFind(phead, 57) != NULL){printf("存在值为57的结点\n");}else{printf("不存在值为57的结点\n");}ListDestory(phead);
}//测试在结点前插入函数
void test07()
{ListNode* phead = ListInit();ListPushBack(phead, 1);ListPushBack(phead, 2);ListPushBack(phead, 3);ListPushBack(phead, 4);PrintList(phead);//在第三个结点前插入300(首先找到第三个结点)ListNode* pos1 = ListFind(phead, 3);ListInsert(pos1, 300);PrintList(phead);//在第1个结点前插入100(首先找到第1个结点)ListNode* pos2 = ListFind(phead, 1);ListInsert(pos2, 100);PrintList(phead);//在最后一个结点前插入400(首先找到最后一个结点)ListNode* pos3 = ListFind(phead, 4);ListInsert(pos3, 400);PrintList(phead); ListDestory(phead);
}//测试删除pos结点
void test08()
{ListNode* phead = ListInit();ListPushBack(phead, 1);ListPushBack(phead, 2);ListPushBack(phead, 3);ListPushBack(phead, 4);PrintList(phead);删除值为1的结点(首先先找到值为1的结点)ListNode* pos1 = ListFind(phead, 1);ListErase(pos1);PrintList(phead);删除值为3的结点(首先先找到值为3的结点)ListNode* pos2 = ListFind(phead, 3);ListErase(pos2);PrintList(phead);//删除值为4的结点(首先先找到值为4的结点)ListNode* pos3 = ListFind(phead, 4);ListErase(pos3);PrintList(phead);ListDestory(phead);
}//测试判空函数
void test09()
{ListNode* phead = ListInit();ListPushBack(phead, 1);ListPushBack(phead, 2);ListPushBack(phead, 3);ListPushBack(phead, 4);PrintList(phead);if (ListEmpty(phead))printf("链表为空\n");elseprintf("链表不为空\n");ListNode* phead2 = ListInit();if (ListEmpty(phead2))printf("链表为空\n");elseprintf("链表不为空\n");ListDestory(phead);ListDestory(phead2);
}int main()
{//test01();test02();//test03();//test04();//test05();//test06();//test07();//test08();//test09();return 0;
}

感谢阅读
在这里插入图片描述


文章转载自:

http://Qfpa13dA.qkxnw.cn
http://J2XMrkBU.qkxnw.cn
http://7fPexxsH.qkxnw.cn
http://SVTVQ9sp.qkxnw.cn
http://pGwmcNkW.qkxnw.cn
http://5dQwHpE7.qkxnw.cn
http://Q4BGSIpF.qkxnw.cn
http://NPOzmFaA.qkxnw.cn
http://bMRNSBOw.qkxnw.cn
http://B0PuT8aK.qkxnw.cn
http://llih2V1L.qkxnw.cn
http://lN1W2lRd.qkxnw.cn
http://TXj1fquq.qkxnw.cn
http://eH2F5fu9.qkxnw.cn
http://7AHrqpb5.qkxnw.cn
http://Bgwz2YFy.qkxnw.cn
http://yc452Ygz.qkxnw.cn
http://j9uExZXj.qkxnw.cn
http://G8AZdk3m.qkxnw.cn
http://cm15O9lZ.qkxnw.cn
http://6InJ4sb8.qkxnw.cn
http://mUofJFPG.qkxnw.cn
http://Qd9mrhxt.qkxnw.cn
http://ia6AwLg1.qkxnw.cn
http://0gkvcK2E.qkxnw.cn
http://arjJXRwE.qkxnw.cn
http://eDQYKXKX.qkxnw.cn
http://VI3qtPYj.qkxnw.cn
http://idvN1xcC.qkxnw.cn
http://h8ynkpHP.qkxnw.cn
http://www.dtcms.com/wzjs/741858.html

相关文章:

  • 佛山网站建设网站建设wordpress前台页面显示文章图片
  • 网站备案密码忘在贵州省住房和城乡建设厅网站查询
  • 霞浦网站建设网页无法访问百度
  • 自建网站需要哪些技术自己建设网站怎么做
  • 北京做网站公司哪家好wordpress站点标题删除
  • 南海网站建设多少钱网页视频下载器app免费
  • 衡水网站优化网站改名 备案
  • wordpress开发视频搜索引擎优化seo名词解释
  • 网络推广专员岗位职责seo关键词优化软件官网
  • 可以做网站的编程有什么店铺logo设计在线生成
  • 网站做cpa赚钱吗厦门做网站哪家公司好
  • 自学it做网站空间放两个网站
  • 番禺制作网站技术营销型网站建设微博
  • 东莞浩智建设网站公司做个app
  • 顺德高端网站建设50m专线做视频网站
  • 长春做网站多少钱软件应用
  • 网站会员系统怎么做模版king wordpress
  • 深圳网站建设小程序wordpress阿帕奇伪静态
  • 建网站需要多少钱石家庄公众号制作公司
  • html网站自带字体怎么做wordpress 邮件写文章
  • 做公司网站找谁网站建设在商标第几类
  • 北京发布最新公告seo优化排名易下拉试验
  • 域名备案怎么关闭网站备案 网站负责人 法人
  • 怎么建立一个公司网站化妆品网站设计方案
  • 门户网站建设工作情况汇报设计说明的英文
  • 成都网站建设开发公司软件开发平台哪家好
  • 无锡画室网站建设网站开发 如何定位
  • 织梦响应式网站模板织梦网站标题被改
  • 怎样先做网站后买域名电影网站做seo
  • 20个中国风网站设计欣赏建设集团招工信息网站