01-02-5

news2024/11/22 9:17:53

1、单链表中按位置查找

a.原理

通过传递的位置,返回该位置对应的地址,放到主函数定义的指针变量中。

我们认为位置从:有数据的节点开始计数

即如下结构:

查找位置,就是返回该位置对应的空间地址。

b.代码说明

Ⅰ.子函数名的说明

查找指定位置的元素的子函数,返回该位置对应的地址,即指针类型,所以返回值是结构体指针类型。

不需要修改链表中的数据,所以形参不需要引用。

用一个整型变量形参接收位置参数。

Ⅱ.定义结构体指针变量,使其指向第一个数据节点

代码如下

LNode* p = L->next;//此时p指向第一个元素节点。L指向的是头节点,使用->头节点内部的next域,得到第一个元素节点的地址

Ⅲ.子函数内部的判断

如果查找的位置是0,则默认返回的是头节点,也就不需要操作了,直接将传来的头指针返回即可。头指针内部放的就是头节点对应的地址。

若是位置小于0,则表示查找的位置不合法,即返回NULL。

代码如下

if (0 == i)//判断变量和常量是否相等,通常将常量写在左侧
{
    return L;
}
if (i < 0)
{
    return NULL;
}

Ⅳ.利用循环,找到对应的位置

利用while循环,使指针不断后移动,知道指向指定的位置即可。

循环的条件是:指针不为空,且当前位置小于指定的位置。

循环内执行的操作是:不断的将指针p重新赋值为当前指针执行的空间中的next值,再进行位置的++操作。

当循环结束后,此时的p刚好指向指定的位置空间。

代码如下

while (p && j < i)
{
    p = p->next;
    j++;
}

Ⅴ.循环结束后,返回指向指定位置的指针

代码如下

return p;

Ⅵ.整体代码如下

LinkList GetElem(LinkList L, int i)//我们认为位置从有数据的元素节点开始计数。
{
    int j = 1;
    LNode* p = L->next;//此时p指向第一个元素节点。L指向的是头节点,使用->头节点内部的next域,得到第一个元素节点的地址
    if (0 == i)//判断变量和常量是否相等,通常将常量写在左侧
    {
        return L;
    }
    if (i < 0)
    {
        return NULL;
    }
    while (p && j < i)
    {
        p = p->next;
        j++;
    }
    return p;
}

2、单链表中按值查找

a.原理

利用循环,便利链表,在子函数中定义结构体指针变量,使其指向第一个数据节点,获取该节点中的数据,与指定的数据进行相比:

若相等则返回该节点对应的地址;

若是不等,则将指针赋值为当前节点的next值,使其指向下一个节点。利用循环,再次比较。

b.子函数代码说明

Ⅰ.子函数名的说明

最终返回的是该值所在的节点的地址,所以需要返回的是一个结构体指针类型:LinkList

因为不需要修改链表中的数据,所以不需要引用。

查找的值的类型必须与传递的值的类型一致,所以需要定义一个相同类型的形参进行接收。

代码如下

LinkList LocateElem(LinkList L, ElemType e)

Ⅱ.在子函数内部定义一个结构体指针类型变量

使该指针变量指向第一个数据元素。

代码如下

LinkList p = L->next;

Ⅳ.利用while循环进行遍历链表

while循环的条件就是指针不为空。

在循环内判断该指针对应的data域与指定的值是否相同,相同则返回该指针,不同则将指针赋值为当前指针的next域。再次循环。

代码如下

while (p)
{
    if (p->data == e)
    {
        return p;
    }
    p = p->next;
}

3、单链表插入元素

α.在中间插入元素

a.原理

申请新的节点空间,将插入的数据放到该空间的数据域中。

然后再找到插入的位置的地址,将申请的节点的next域赋值为找到的地址。(即实现原位置数据后移操作)

最后将存放找到的地址的变量重新赋值为刚申请的节点的地址。(即实现节点插入操作)

利用查找位置的函数,传入的是该位置对应的下标,所以需要将位置-1,得到下标后再传入。

查找位置的函数,返回的就是插入位置的前一个节点的地址,将返回的地址放到p指针中,通过p指针访问next域,才能得到插入位置的地址。

向第i个位置插入数据,利用GetElem函数找到第i-1的位置的地址,而不是找到第i个位置的原因:

若是返回第i个位置地址,此时直接访问data域,则会覆盖原来的数据。

b.代码说明

Ⅰ.子函数名的说明

调用该函数是否插入成功,所以返回一个bool类型,即返回值为bool类型。

不需要改变链表中的值,所以不需要引用链表。

用一个整型变量,用于存放插入的指定位置。

定义一个与插入类型相同的变量,用于接收插入的数据。

代码如下

bool ListFrontInsert(LinkList L, int i, ElemType e)

插入到第i个位置,就必须拿到第i个位置的地址,第i个位置的地址放在第i-1节点的next域中。所以需要借助按位置查找函数,找到第i-1位置的节点对应的地址。

所以需要定义一个结构体节点指针,用于接收按位置查找的返回值,即插入位置的前一个位置的地址。

代码如下:

LinkList p = GetElem(L, i - 1);

Ⅱ.找到插入位置i前一个位置的地址

找到插入位置的前一个节点的地址,利用GetElem函数即可。所以当向第i个位置插入数据时,就需要先找到第i-1位置对应的地址。GetElem函数返回的是指向指定位置的指针,所以我们要找前一个位置的指针时,传入的数据需要-1

代码如下

LinkList p = GetElem(L, i - 1);

此时返回的指针p,内部的地址就是插入位置的前一个节点的地址。

Ⅲ.判断插入位置的前一个节点指针

若是为空,则返回false,不为空后才可以继续插入。

代码如下

if (NULL == p)
{
    return false;
}

Ⅳ.申请节点空间

代码如下

LinkList s = (LNode*)malloc(sizeof(LNode));//为新插入的节点申请空间

Ⅴ.给新的节点数据域赋值

代码如下

s->data = e;

Ⅵ.将插入的新节点的next域赋值为找到的地址next域:实现后移操作

代码如下

s->next = p->next;

Ⅶ.将存放找到的地址的变量重新赋值为申请节点的地址

代码如下

p->next = s;

Ⅷ.操作成功后返回true

代码如下

return true;

Ⅸ.整体代码如下

找到第i个位置,并返回指向该位置的指针

LinkList GetElem(LinkList L, int i)//我们认为位置从有数据的元素节点开始计数。
{
    int j = 1;
    LNode* p = L->next;//此时p指向第一个元素节点。L指向的是头节点,使用->头节点内部的next域,得到第一个元素节点的地址
    if (0 == i)//判断变量和常量是否相等,通常将常量写在左侧
    {
        return L;
    }
    if (i < 0)
    {
        return NULL;
    }
    while (p && j < i)
    {
        p = p->next;
        j++;
    }
    return p;
}

向第i个位置插入数据

bool ListFrontInsert(LinkList L, int i, ElemType e)
{
    LinkList p = GetElem(L, i - 1);//这个p内部的是指向第i-1位置的地址,插入的是第i个位置
    if (NULL == p)
    {
        return false;
    }
    LinkList s = (LNode*)malloc(sizeof(LNode));//为新插入的节点申请空间
    s->data = e;
    s->next = p->next;//实现后移操作
    p->next = s;//实现插入操作
    return true;
}

4、单链表删除节点

a.原理

利用GetElem()函数,找到删除位置的前驱节点,返回该前驱节点的地址,保存该节点中next域的地址。这个地址指向的就是要删除的节点的地址。备份该节点的地址,将该节点中的next域的地址赋值到前驱节点的next域中,然后释放保存的节点的地址(即要删除的节点的地址,也就是前面备份的地址),再将原来用于备份该节点的地址的指针置空即可。

删除第2个位置的节点,

第一步:先找到指向第一个位置的指针,利用GetElem函数查找即可。

备份第二个节点的地址,

第二步:再将第一个位置的next域赋值为第二个节点的next域值,

第三步:最后将释放第二个节点即可,利用第一步备份的地址访问到第二个节点。

若是不备份,则再第二步执行完后,会导致第二个节点的地址丢失,无法访问和释放第二个节点。

结构如下

若是不找到前驱节点的地址,则无法访问到前驱节点的next域,即无法完成第二步操作。所以必须借助GetElem()函数,找到前驱节点的地址。

b.代码说明

Ⅰ.头文件的说明

返回一个bool类型,用于说明删除成功与否。传入进行删除的链表名,一个记录删除位置的变量。

代码如下

bool ListDelete(LinkList L, int i)

Ⅱ.找到删除位置的前驱

代码如下

LinkList p = GetElem(L, i - 1);//找到删除位置的前驱节点

说明

此时的p指针,内部存放的就是指向删除节点的前面节点的地址。该指针指向的空间内部的next域,就是要删除的位置的节点对应的地址。

Ⅲ.判断前驱节点的指针是否存在

代码如下

if (NULL == p)//判断前驱节点是否存在
{
    return false;
}

说明

只要在后面需要使用指针,访问对应的域,都需要判断该指针是否为空。

Ⅳ.判断删除的位置是否存在

代码如下

LinkList q = p->next;//此时q指向的就是删除位置的节点对应的地址
if (NULL == q)
{
    return false;//要删除的位置不存在
}

说明

p指针指向的是前驱节点的地址,p->next指向的就是要删除的节点的地址。将其放在指针变量q中。若q指针为空,则表示p指向的就是最后一个节点的地址,无法进行删除操作。

Ⅴ.备份要删除的节点的地址

代码如下

LinkList q = p->next;//此时q指向的就是删除位置的节点对应的地址

Ⅵ.进行断链操作

代码如下

p->next = q->next;//断链

Ⅶ.释放要删除的节点空间

代码如下

free(q);//释放q指向的地址,即对应节点的空间 q = NULL;//将q这个指针置空

free()函数只是释放该指针指向的空间,没有改变该指针的指向,所以还需要将该指针置空,才算彻底结束。

Ⅷ.返回成功true

代码如下

return true;

Ⅸ.整体代码如下

bool ListDelete(LinkList L, int i)
{
    LinkList p = GetElem(L, i - 1);//找到删除位置的前驱节点
    if (NULL == p->next)//预防删除的位置是否存在
    {
        return false;
    }
    LinkList q = p->next;//此时q指向的就是删除位置的节点对应的地址
    p->next = q->next;//断链
    free(q);//释放q指向的地址,即对应节点的空间
    q = NULL;//将q这个指针置空
    return true;
}

5、双向链表

a.双向链表的说明

结构如下图

文字说明

相对于单链表,双链表比单链表多一个指针,用于指向该节点的前驱节点,即内部放的是前驱节点的地址。

单链表中的指针放的是该节点的后继节点的地址。

b.结构体定义如下

typedef struct DNode
{
    ElemType data;//数据域
    struct DNode* prior;//前驱指针
    struct DNode* next;//后继指针
}DNode,*DLinkList;//进行重命名结构体和结构体指针

c.插入节点的说明

结构如下

文字说明

申请新的空间,并将该空间的地址放到结构体指针S中。

将新申请的节点,插入到第i个位置,先找到第i-1位置的节点,并将该节点的地址放到结构体指针P中。

①将新申请的节点的next赋值为前驱节点的next中的值,代码如下:S->next=P->next

②将后继节点的prior域重新赋值为新申请的节点地址,代码如下:P->next->prior=S

此时①②,实现了后继节点可以指向申请的空间,申请的空间也可指向后继节点:即新申请的空间的next域放的是后继节点的地址,后继节点的piror域放的是申请的空间的地址。

③将前驱节点的地址放到新申请的空间的prior域中,代码如下:S->prior=P

④将新申请的空间的地址放到前驱节点的next域中,代码如下:P->next=S

此时③④,实现了前驱节点的next域可以指向新申请的空间,新申请的空间的prior域可以指向前驱节点。即前驱节点空间中的next域放的是新申请的空间地址,新申请的空间的prior域放的是前驱节点的地址。

d.头插法新建双向链表

头插法的逻辑说明

新的节点的prior指向头节点;

新的节点的next指向头节点后面的一个节点;

头节点的next指向新的节点;

头节点后面的节点的prior指向新的节点。

即第一个申请的数据节点,再链表形成后会放到最后一个。(或者说每一个申请的数据节点,都直接放在头节点后面)这样的插入称为头插法。

Ⅰ.子函数名的说明

代码如下

DLinkList Dlist_head_insert(DLinkList& DL)

说明

返回一个结构体指针,函数参数引用主函数中的头指针DL

Ⅱ.新建结构体指针变量,用于接收申请的新空间地址

代码如下

DNode* s; int x;

说明

整型变量x用于存放到节点数据域中的数据。

Ⅲ.申请新的节点空间

代码如下

DL = (DLinkList)malloc(sizeof(DNode));

说明

malloc()函数用于申请空间,sizeof(DNode):用于计算申请的空间大小,单位是字节。内部是结构体名,表示申请和该结构体定义时一样的空间。将申请的空间强制类型转换为结构体指针类型,并将其保存在结构体指针变量DL中。

DL是头指针,此处申请的空间节点是头节点。

Ⅳ.将新申请的空间置空

代码如下

DL->prior = NULL; DL->next = NULL;

说明

将指向前驱节点和指向后继节点的指针域置空。

Ⅴ.读取数据,利用循环进行申请空间并建立链表

代码如下

scanf("%d", &x);//从标准输入读取数据,放到变量中暂时存储
//3 4 5 6 7 9999
while (x != 9999)
{
    s = (DLinkList)malloc(sizeof(DNode));//此时申请的节点是数据节点
    s->data = x;
    s->next = DL->next;//①
    if (DL->next != NULL)
    {
        DL->next->prior = s;//④
    }
    s->prior = DL;//使插入的节点指向前一个节点②
    DL->next = s;//使原来的节点指向新插入的节点③
    scanf("%d", &x);//继续读取数据
}

新申请第一个数据节点时结构如下:

第一个数据节点有三步:

①新的节点的next域放的是前一个节点的next中的内容。

②新的节点的prior域放的是头节点对应的地址,指向头节点。

③前一个节点的next域放的是新的节点的地址,指向新的节点对应的空间。

申请第二个及以后的数据节点时的结构如下:

总结:

①新申请的节点的next内是头节点后面的节点地址,指向头节点后面的节点

④头节点后面的一个节点的prior指向新申请的节点

②新申请的节点的prior内是头节点,

③头节点的next指向新申请的节点。

Ⅵ.返回头指针即可

代码如下

return DL;

Ⅶ.整体代码如下

DLinkList Dlist_head_insert(DLinkList& DL)
{
    DNode* s; int x;
    DL = (DLinkList)malloc(sizeof(DNode));//带头节点的链表,此处的节点是头节点,DL指向头节点
    DL->prior = NULL;
    DL->next = NULL;
    scanf("%d", &x);//从标准输入读取数据,放到变量中暂时存储
    //3 4 5 6 7 9999
    while (x != 9999)
    {
        s = (DLinkList)malloc(sizeof(DNode));//此时申请的节点是数据节点
        s->data = x;
        s->next = DL->next;//若是DL->next内部是一个地址,则表示将该地址放到s->next中,即s->next访问的是存放在DL->next内部的地址对应的空间
        if (DL->next != NULL)
        {
            DL->next->prior = s;
        }
        s->prior = DL;//使插入的节点指向前一个节点
        DL->next = s;//使原来的节点指向新插入的节点
        scanf("%d", &x);//继续读取数据
    }
    return DL;
}

e.双链表打印

代码同单链表一致

Ⅰ.子函数名的说明

代码如下

void PrintDList(DLinkList DL)

说明

不需要返回,不需要引用,直接用形参接收即可

Ⅱ.改变头指针指向,使其指向第一个数据节点

代码如下

DL = DL->next;//改变头指针指向,使其指向第一个数据节点 说明

此时的DL指针指向第一个数据节点

Ⅲ.利用循环,先输出,在改变指针

代码如下

while (DL != NULL)
    {
        printf("%3d", DL->data);
        DL = DL->next;
    }

说明

因为在开始已经使DL指向用一个数据节点的地址,可以直接访问,所以循环处直接判断该指针是否为空即可

不为空时,先输出,直接访问进行输出即可,再改变DL,使其指向下一个节点的地址,访问当前节点的next域即可。

Ⅳ.整体代码如下

void PrintDList(DLinkList DL)
{
    DL = DL->next;//改变头指针指向,使其指向第一个数据节点
    while (DL != NULL)
    {
        printf("%3d", DL->data);
        DL = DL->next;
    }
    printf("\n");
}

f.尾插法

结构说明

尾插法:每次新申请的节点,放在最后。

双链表的尾插法步骤说明:

先申请一个头节点,定义尾指针变量,初始化时令尾指针指向头节点。然后借助循环,在循环中申请新的数据节点,存放数据域,然后将尾指针的next域赋值为新的节点,新的节点的prior赋值为尾指针指向的地址,最后将尾指针赋值为新节点即可。离开循环后,将尾指针指向的节点的next域置空即可。

总结:最后形成的链表:头节点的prior为NULL,尾节点的next为NULL。

Ⅰ.子函数名的说明

代码如下

DLinkList Dlist_tail_insert(DLinkList& DL)

说明

返回一个结构体指针类型,引用主函数的头指针变量。

Ⅱ.定义变量,用于读取输入的数据域的值

代码如下

int x

Ⅲ.申请头节点的空间,放到头指针中

代码如下

DL=(DLinkList)malloc(sizeof(DNode))

说明

利用malloc()申请新的空间,空间大小利用sizeof()计算一个结构体大小即可。

malloc()申请的空间无类型,需要强制类型转换为结构体指针类型后再赋值到头指针变量中。

Ⅳ.定义两个指针变量

代码如下

DNode*s,*r=DL;

说明

*s指针,用于接收新申请的节点空间;*r指针,用于记录尾指针。

注意:最初的尾指针,和头指针保持一致,指向第一个头节点。

Ⅴ.将头节点的prior置空

代码如下

DL->prior=NULL;

Ⅵ.读取数据

代码如下

scanf("%d",&x);

说明

后面将x变量中的数据放到数据域中即可。

Ⅶ.利用循环将数据放到数据域中,并不断申请新的节点

代码如下

while (x != 9999)
{
    s = (DLinkList)malloc(sizeof(DNode));//申请的第一个数据节点
    s->data = x;
    r->next = s;
    s->prior = r;
    r = s;//r指向新的表尾节点
    scanf("%d", &x);//继续读取新的数据
}

说明

在循环内,先申请一个节点空间,放到指针变量s中。然后给该节点的数据域进行赋值为x变量。

r指针开始指向的是头节点,给头节点的next赋值为新申请的节点地址。

将新申请的节点的prior域赋值为前一个节点的地址,也就是r指针保存的。

然后再使r指针指向最后一个节点,也就是新申请的节点s。

Ⅷ.循环结束后,将尾节点的next域赋值为空

代码如下

r->next = NULL;//尾节点的next指针赋值为NULL

说明

r指针在每次循环结束时,都指向最后一个节点,所以在循环外,借助r指针,直接访问next域即可。

Ⅸ.最后返回头指针即可

代码如下

return DL;

Ⅹ.整体代码如下

DLinkList Dlist_tail_insert(DLinkList& DL)
{
    int x;//用于临时存放数据域的数据
    DL = (DLinkList)malloc(sizeof(DNode));
    DNode* s, * r;//r代表尾指针
    DL->prior = NULL;
    //3 4 5 6 7 9999
    scanf("%d", &x);
    while (x != 9999)
    {
        s = (DLinkList)malloc(sizeof(DNode));//申请的第一个数据节点
        s->data = x;
        r->next = s;
        s->prior = r;
        r = s;//r指向新的表尾节点
        scanf("%d", &x);//继续读取新的数据
    }
    r->next = NULL;//尾节点的next指针赋值为NULL
    return DL;//返回头指针
}

g.双链表的查找节点

等同于单链表的查找节点

h.插入到指定位置

Ⅰ.子函数名的说明

代码如下

bool DListFrontInsert(DLinkList DL, int i, ElemType e)

说明

返回bool类型,用于说明查找是否成功;

使用形参接收头指针DL即可;

定义整型变量,用于记录插入的位置;

定义相同类型的变量,用于临时保存插入的数据。

Ⅱ.找到插入位置的前驱节点

代码如下

DLinkList p = GetElem(DL, i - 1);//找到该位置的前驱节点,返回的是前驱节点的地址指针

说明

找前驱节点是为了修改前驱节点的next域,保证插入后仍是一条完整的链表。

此时的p指针,指向的是插入位置的前驱节点。

Ⅲ.判断插入位置的前驱节点是否存在

代码如下

if (NULL == p)//注意:插入的位置可以不存在,但是插入的前驱节点一定存在:即插在最后一个节点的后面
{
    return false;//若是插入位置的前驱节点不存在,才报错
}

说明

若是插入位置的前驱节点不存在,则不能插入。插入的位置可以不存在,此时表示插入在最后一个节点的后面。

Ⅳ.申请节点空间

代码如下

DLinkList s = (DLinkList)malloc(sizeof(DNode));

说明

此时的s指针,指向新申请的节点。

Ⅴ.插入节点

代码如下

s->data = e;//存放数据
s->next = p->next;//实现新申请的节点指向后面的节点
p->next->prior = s;//实现原位置的节点指向新申请的节点
s->prior = p;//实现新申请的节点指向前驱节点
p->next = s;//实现前驱节点指向新申请的节点

说明

即需要完成:新申请的节点的next域存放原位置的空间地址;

改变原位置的节点的前面指向,使其指向新的节点;

此时完成新申请的节点与后面的节点相互指向。

在完成新申请的节点prior域存放前驱节点的地址;

前驱节点的next域存放新申请的节点的地址;

此时完成新申请的节点与前面的节点相互指向。

Ⅵ.返回true

代码如下

return true

Ⅶ.整体代码如下

bool DListFrontInsert(DLinkList DL, int i, ElemType e)
{
    DLinkList p = GetElem(DL, i - 1);//找到该位置的前驱节点,返回的是前驱节点的地址指针
    if (NULL == p)//注意:插入的位置可以不存在,但是插入的前驱节点一定存在:即插在最后一个节点的后面
    {
        return false;//若是插入位置的前驱节点不存在,才报错
    }
    DLinkList s = (DLinkList)malloc(sizeof(DNode));
    s->data = e;//存放数据
    s->next = p->next;//实现新申请的节点指向后面的节点
    p->next->prior = s;//实现原位置的节点指向新申请的节点
    s->prior = p;//实现新申请的节点指向前驱节点
    p->next = s;//实现前驱节点指向新申请的节点
    return true;
}

Ⅷ.插入节点的说明

参考本文件中:c.插入节点的说明

i.删除节点

Ⅰ.子函数名的说明

代码如下

bool DListDelete(DLinkList DL, int i)

说明

返回bool类型,用于说明删除是否成功;

使用普通形参,用于接收头文件;

定义整型变量,用于记录删除的位置。

Ⅱ.找到删除位置的前驱节点

代码如下

DLinkList p = GetElem(DL, i - 1);//找到删除位置的前驱节点

说明

利用GetElem()函数找到删除位置的前驱节点,此时的p指针指向的是前驱节点

Ⅲ.判断前驱节点是否存在

代码如下

if (NULL == p)
{
    return false;
}

说明

p指针此时指向的是前驱节点的空间

Ⅳ.判断删除位置的节点是否存在

代码如下

DLinkList q = p->next;//此时q指向删除位置的空间
if (NULL == q)//判断删除的位置是否存在
{
    return false;
}

说明

定义结构体类型指针q,用于指向删除位置的节点。因为p指针指向的是前驱节点,前驱节点的next域放的就是删除位置的地址,直接访问即可。所以此时的q指针,就是指向删除位置的节点。利用if语句,判断该节点是否存在,若是不存在直接返回false即可。

q指针,也是用于备份删除位置的地址,否则后面修改后,无法访问到删除的空间地址,进而无法释放该空间。

Ⅴ.进行断链操作

代码如下

p->next = q->next;//断链
if (q->next != NULL)
{
    q->next->prior = p;
}

说明

q指针指向的是删除位置的节点,p指针指向的是前驱节点。p->next=q->next:此句用于实现让前驱节点直接访问到后面的节点,即跳过删除的节点。

若是后继节点存在,即不为空,则将其的prior指向前驱节点即可,即if语句中就是用于实现此处的代码。

Ⅵ.释放指向该空间的指针

代码如下

free(q);

说明

也就是释放前面备份的空间地址

Ⅶ.将备份指针置空

代码如下

q=NULL;

Ⅷ.返回true

代码如下

return true;

Ⅸ.整体代码如下

bool DListDelete(DLinkList DL, int i)
{
    DLinkList p = GetElem(DL, i - 1);//找到删除位置的前驱节点
    if (NULL == p)
    {
        return false;
    }
    DLinkList q = p->next;//此时q指向删除位置的空间
    if (NULL == q)//判断删除的位置是否存在
    {
        return false;
    }
    p->next = q->next;//断链
    if (q->next != NULL)
    {
        q->next->prior = p;
    }
    free(q);//释放对应节点的空间
    q = NULL;
    return true;
}

6.循环单链表和循环双链表的概念

a.循环单链表

循环单链表就是将最后一个节点的next赋值为头指针。

即尾指针直接访问next域,赋值为头指针即可。

b.循环双链表

循环双链表就是将最后一个节点的next域赋值为头指针;

将头结点的prior域赋值为尾指针。

7.静态链表

即一个结构体数组,结构体包含数据域和一个整型变量,这个整型变量用于记录后面一个节点对应的下标,通过下标访问下一个节点。

8.总结

链表只有在头插法、尾插法改变头指针的值的时候,才需要借助引用,其他删除、插入都不需要引用。

创建头指针,可以初始化为NULL

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深度剖析深度神经网络(DNN):原理、实现与应用

目录 引言 一、DNN基本原理 二、DNN核心算法原理 三、DNN具体操作步骤 四、代码演示 引言 在人工智能和机器学习的浪潮中&#xff0c;深度神经网络&#xff08;Deep Neural Network&#xff0c;简称DNN&#xff09;已经成为了一种非常重要的工具。DNN模仿人脑神经网络的结…

2023年数维杯国际大学生数学建模挑战赛A题复合直升机的建模与优化控制问题解题全过程论文及程序

2023年数维杯国际大学生数学建模挑战赛 A题 复合直升机的建模与优化控制问题 原题再现&#xff1a; 直升机具有垂直起降等飞行能力&#xff0c;广泛应用于侦察、运输等领域。传统直升机的配置导致旋翼叶片在高速飞行过程中受到冲击波的影响&#xff0c;难以稳定飞行。为了在保…

【轮转数组】力扣python

1.python切片 这里nums[:]代表列表 class Solution:def rotate(self, nums: List[int], k: int) -> None:nlen(nums)nums[:]nums[-k%n:]nums[:-k%n] 2.边pop边push 0代表插入的位置 class Solution:def rotate(self, nums: List[int], k: int) -> None:nlen(nums)fo…

[Linux] 入门指令详解

目录 ls指令 pwd指令 whoami指令 cd指令 clear指令 touch指令 mkdir指令 rmdir指令 rm指令 man指令 cp指令 mv指令 cat指令 tac指令 more指令 less指令 head指令 tail指令 如何读取文件中间某一段内容&#xff1f; date指令 cal指令 find指令 which指令…

Java Web开篇

Java Web开篇 大纲 整个内容梳理 具体案例 整个内容梳理 这是前端和后端组成的系统的框架结构

自动攻丝机进出料激光检测 进料出料失败报警循环手动及关闭报警退出无限循环

/**************进料检测********************/ /***缺料无限次循环 手动退出 超时报警*******/ void check_Pon() // { zstatus0; //报警计数器归零 Signauto1; …

使用Pyramid、Mako和PyJade生成 HTML

Pyramid 是一个流行的 Python Web 框架&#xff0c;而 Mako 和 PyJade 是用于模板引擎的工具&#xff0c;它们可以与 Pyramid 配合使用来生成 HTML 内容。但是在实际使用中还是有些差别的&#xff0c;尤其会遇到各种各样的问题&#xff0c;下面我将利用我所学的知识一一为大家解…

Python | Leetcode Python题解之第87题扰乱字符串

题目&#xff1a; 题解&#xff1a; class Solution:def isScramble(self, s1: str, s2: str) -> bool:cachedef dfs(i1: int, i2: int, length: int) -> bool:"""第一个字符串从 i1 开始&#xff0c;第二个字符串从 i2 开始&#xff0c;子串的长度为 le…

MyBatis——MyBatis 参数处理

一、单个简单类型参数 简单类型包括&#xff1a; byte short int long float double char Byte Short Integer Long Float Double Character String java.util.Date java.sql.Date parameterType 属性&#xff1a;告诉 MyBatis 参数的类型 MyBatis 自带类型自动推断机制…

部署Discuz论坛项目

DIscuz 是由 PHP 语言开发的一款开源社交论坛项目。运行在典型的LNMP/LAMP 环境中。 安装MySQL数据库5.7 主机名IP地址操作系统硬件配置discuz-db192.168.226.128CentOS 7-mini-20092 Core/4G Memory 修改主机名用来自己识别 hostnamectl set-hostname discuz-db #重连远程…

虚拟资源在线交易服务平台源码 线上虚拟商品交易平台搭建

在信息爆炸的时代&#xff0c;虚拟资源、素材、源码系统等等以其独特的魅力&#xff0c;逐渐成为人们日常生活和工作中不可或缺的一部分。如何高效地获取、管理和交易这些虚拟资源&#xff0c;分享一款虚拟资源在线交易服务平台源码&#xff0c;轻松搭建线上虚拟商品交易平台&a…

【全开源】智慧小区物业管理小程序基于FastAdmin+UniApp

基于FastAdminUniApp开发的智慧小区物业管理小程序&#xff0c;包含小区物业缴费、房产管理、在线报修、业主活动报名、在线商城等功能。为物业量身打造的智慧小区运营管理系统&#xff0c;贴合物业工作场景&#xff0c;轻松提高物业费用收缴率&#xff0c;更有功能模块个性化组…

HTML静态网页成品作业(HTML+CSS+JS)——华为商城网页(1个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;使用Javacsript代码实现首页图片切换轮播效果&#xff0c;共有1个页面…

【网络安全产品互联互通 告警信息资产信息】相关思维导图

近日&#xff0c;在某客户安全建设项目中&#xff0c;涉及安全告警事件的梳理上报。在整理及学习中发现最近&#xff08;以19年等保2.0为参考分隔“最近”&#xff09;发布的可参考标准&#xff0c;因此做了思维导图的整理。 PS&#xff1a;标准中存在引用的情况&#xff0c;过…

OpenHarmony NAPI 使用Cmake 编译C++ .so 并ets调用

一、前言 这两年多随着国产化兴起&#xff0c;国内越来越多的项目都要求支持国产化系统&#xff0c;并且随着OpenHarmony 开源&#xff0c;更多的人都想分一杯羹。因此&#xff0c;我们公司也要求&#xff0c;所有产品的都需要支持开源鸿蒙OpenHarmony。 对于我们这边的设备&am…

Mybatis进阶详细用法

目录 条件构造器 案例 自定义SQL 案例 Service接口 案例 综合案例 条件构造器 案例 Testvoid testQueryMapper() {// 创建 QueryWrapper 实例QueryWrapper<User> queryWrapper new QueryWrapper<>();queryWrapper.select("id," "username,&…

uniapp小程序:大盒子包裹小盒子但是都有点击事件该如何区分?

在开发过程中我们会遇到这种情况&#xff0c;一个大盒子中包裹这一个小盒子&#xff0c;两个盒子都有点击事件&#xff0c;例如&#xff1a; 这个时候如果点击评价有可能会点击到它所在的大盒子&#xff0c;如果使用css中的z-index设置层级的话如果页面的盒子多的话会混乱&…

基于微信小程序+JAVA Springboot 实现的【停车场小程序】app+后台管理系统 (内附设计LW + PPT+ 源码+ 演示视频 下载)

项目名称 项目名称&#xff1a; 停车场微信小程序的设计与实现 在当前信息技术飞速发展的背景下&#xff0c;停车场微信小程序的开发成为了一个创新的解决方案&#xff0c;旨在提高停车场管理的效率和用户的停车体验。本项目通过深入分析现有停车场管理系统的不足&#xff0c…

使用Apache Spark从MySQL到Kafka再到HDFS的数据转移

使用Apache Spark从MySQL到Kafka再到HDFS的数据转移 在本文中&#xff0c;将介绍如何构建一个实时数据pipeline&#xff0c;从MySQL数据库读取数据&#xff0c;通过Kafka传输数据&#xff0c;最终将数据存储到HDFS中。我们将使用Apache Spark的结构化流处理和流处理功能&#…

[Linux][网络][高级IO][一][五种IO模型][同步通信][异步通信][非阻塞IO]详细讲解

目录 0.预备知识 && 思考问题1.五种IO模型0.形象理解五种模型1.阻塞IO2.非阻塞IO3.信号驱动IO4.多路转接/多路复用5.异步IO 2.高级IO重要概念1.同步通信 vs 异步通信2.阻塞 vs 非阻塞 3.非阻塞IO1.fcntl()2.实现SetNonBlock 0.预备知识 && 思考问题 网络通信本…