2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Hei~! Tämä on kamppaileva pieni lammas Olen ylpeä siitä, että voit lukea artikkelini. Tervetuloa!
💥💥个人主页:Taisteleva lammas
💥💥所属专栏:C-kieli
🚀本系列文章为个人学习笔记,在这里撰写成文一为巩固知识,二为展示我的学习过程及理解。文笔、排版拙劣,望见谅。
Yksittäin linkitetty lista voi käyttää tietoja vain yhteen suuntaan tietyn solmun osoitteen kautta, kun taas kaksoislinkitetty lista voi käyttää tietoja molempiin suuntiin solmun osoitteen kautta. Lisäyksen, poistamisen, tarkistamisen ja muokkaamisen tehokkuus on suurempi mutta koodin toteutus on yksinkertaisempaa kuin yksitellen linkitetty luettelo.
Linkitetyt luettelot on jaettu kahdeksaan tyyppiin: otsikko, ei-otsikko, yksisuuntainen, kaksisuuntainen, syklinen ja ei-syklinen. Opimme vain yleiset.Yksisuuntainen ei-syklinen linkitetty luettelo ilman johtajaajaKaksisuuntainen pyöreä linkitetty luettelo, tuo onYksittäinen listajaKaksoislinkitetty lista。
Kaksoislinkitetty lista:
Itse asiassa edellisessä artikkelissa kutsuimme yksitellen linkitetyn luettelon ensimmäistä solmua pääsolmuksi, mikä on epätarkka. Syy miksi sitä kutsutaan niin, on helpompi ymmärtää, koska yksitellen linkitetyssä luettelossa ei ole päätä, ja vain kaksoislinkitetyssä luettelossa on pääsolmuSentinellin asema。
- Kaksoislinkitetyn luettelon pääsolmuun ei ole tallennettu kelvollisia tietoja, mutta virheellisiä tietoja.
- Sentinel-asemaa ei voi muuttaa, vain sen suuntaa voidaan muuttaa.
Kaksinkertaisesti linkitetyn luettelon solmujen on tallennettava tiedot sekä edellisen ja seuraavan solmun osoitteet. Siksi kaksoislinkitetyn luettelon solmut ovat:
typedef int dlist_data_type;
//双向链表节点
typedef struct dlist
{
dlist_data_type data;
struct dlist* next
struct dlist* prev;
}dlist;
Toisin kuin yksitellen linkitetyssä luettelossa, kaksoislinkitetyssä luettelossa on oltava vähintään yksi solmu, joka on pääsolmu, jota kutsutaan myös vartijapaikaksi.
Ennen kuin lisäät, poistat, tarkistat tai muokkaat,Kaksoislinkitetyn listan on alustava vartijabitti, ja sentinelbitti tallentaa virheellistä dataa.
Sovelletulla solmulla on aluksi kaksi itseensä osoittavaa osoitinta.。
//申请节点
dlist* dlist_buy(dlist_data_type x)
{
dlist* newdlist = (dlist*)malloc(sizeof(dlist));
if (newdlist == NULL)
{
perror("malloc fail!");
return 1;
}
newdlist->data = x;
newdlist->next = newdlist;
newdlist->prev = newdlist;
return newdlist;
}
On kaksi tapaa alustaa vartijabitti:
Palautettu parametreina:
void dlist_init(dlist** pphead)
{
assert(pphead);
*pphead = dlist_buy(-1);
}
Tämä menetelmä edellyttää toissijaisten osoittimien käyttöä.
Palautettu palautusarvona:
dlist* dlist_init()
{
dlist* phead = dlist_buy(-1);
return phead;
}
Tämä menetelmä on yksinkertaisempi ja helpompi ymmärtää.
Tietojen tulostaminen ja haku eivät eroa paljoakaan yksittäislinkin listan vastaavista, joten en mene yksityiskohtiin.
Ainoa asia, joka vaatii huomiota, on ehto silmukan lopettamiselle. Silmukka päättyy, kun osoitin osoittaa vartiobittiä, eikä kun osoitin osoittaa vartiobittiä.NULL
。
//打印数据
void dlist_print(dlist* phead)
{
assert(phead);
dlist* pcur = phead->next;
while (pcur != phead)
{
printf("%d->", pcur->data);
pcur = pcur->next;
}
printf("NULLn");
}
//查找
dlist* dlist_find(dlist* phead, dlist_data_type x)
{
assert(phead);
dlist* pcur = phead->next;
while (pcur != phead)
{
if (pcur->data == x)
{
return pcur;
}
pcur = pcur->next;
}
return NULL;
}
Yksittäin linkitetystä listasta poiketen kaksoislinkitetyn listan lisäys-, poisto-, kysely- ja muokkaustoiminnot eivät muuta sentinel-bittiä, joten vainkutsu arvon mukaanSe siitä.
Kaksinkertaisesti linkitetyn luettelon lisäystoiminto edellyttää kolmen solmun muokkausta Muokkausprosessin aikanaMuokkaa ensin uutta solmua, sitten seuraavaa solmua ja lopuksi muokkaa edellistä solmua.
//插入操作不改变哨兵位,因此传一级指针即可
//尾插
void dlist_push_back(dlist* phead, dlist_data_type x)
{
assert(phead);
dlist* newdlist = dlist_buy(x);
//新尾节点
newdlist->prev = phead->prev;
newdlist->next = phead;
//旧尾节点
phead->prev->next = newdlist;
//头节点(哨兵位)
phead->prev = newdlist;
}
//头插
void dlist_push_front(dlist* phead, dlist_data_type x)
{
assert(phead);
dlist* newdlist = dlist_buy(x);
//新节点
newdlist->prev = phead;
newdlist->next = phead->next;
//旧节点
phead->next->prev = newdlist;
//哨兵位
phead->next = newdlist;
}
Poistotoimenpiteen edellytyksenä on, että solmuja ei voi olla (Sentinel-asemaa ei lasketa), tallenna silti solmun osoite ennen poistamista, liity uudelleen kaksoislinkitettyyn luetteloon ja vapauta sitten solmu.
//尾删
void dlist_pop_back(dlist* phead)
{
assert(phead);
assert(phead->next != phead);//双向链表不能为空
dlist* del = phead->prev;
//新尾节点
del->prev->next = phead;
//哨兵位
phead->prev = del->prev;
free(del);
del = NULL;
}
//头删
void dlist_pop_front(dlist* phead)
{
assert(phead);
assert(phead->next != phead);
dlist* del = phead->next;
del->next->prev = phead;
phead->next = del->next;
free(del);
del = NULL;
}
//在指定位置之后插入数据
void dlist_insert_back(dlist* pos, dlist_data_type x)
{
assert(pos);
dlist* newdlist = dlist_buy(x);
newdlist->prev = pos;
newdlist->next = pos->next;
pos->next->prev = newdlist;
pos->next = newdlist;
}
//删除指定位置的节点
void dlist_erase(dlist* pos)
{
assert(pos);
dlist* del = pos;
pos->prev->next = pos->next;
pos->next->prev = pos->prev;
free(pos);
pos = NULL;
}
Kun tiedot on poistettu määritetystä sijainnista,Koska tätä funktiota kutsutaan arvon perusteella, määritämme vain muodollisen parametrin osoittimen määritetyssä paikassa olevan solmun vapauttamisen jälkeenNULL
, ja varsinainen parametriosoitin osoittaa edelleen tähän paikkaan, joten funktiokutsun päätyttyä varsinainen parametriosoitin on asetettavaNULL
。
Kaksinkertaisesti linkitetyn listan tuhoamisen loppuehto on irtautua silmukasta, kun läpikulkuosoitin osoittaa pääsolmuun.Lopuksi vartija-asento on vapautettava erikseen, kun kaksoislinkitetyn listan tuhoamisfunktiokutsu on valmis, on myös asetettava vartijabitille osoittava osoitin.NULL
。
//销毁
void dlist_destroy(dlist* phead)
{
assert(phead);
dlist* pcur = phead->next;
while (pcur != phead)
{
dlist* next = pcur->next;
free(pcur);
pcur = next;
}
free(pcur);
pcur = NULL;
}
dlist.h:
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int dlist_data_type;
//双向链表节点
typedef struct dlist
{
dlist_data_type data;
struct dlist* next;
struct dlist* prev;
}dlist;
//初始化
//插入数据之前,双向链表必须初始化到只有一个头节点(哨兵位)
//void dlist_init(dlist** pphead);//以参数的形式返回
dlist* dlist_init();//以返回值的形式返回
//打印数据
void dlist_print(dlist* phead);
//查找
dlist* dlist_find(dlist* phead, dlist_data_type x);
//尾插
void dlist_push_back(dlist* phead, dlist_data_type x);
//头插
void dlist_push_front(dlist* phead, dlist_data_type x);
//尾删
void dlist_pop_back(dlist* phead);
//头删
void dlist_pop_front(dlist* phead);
//在指定位置之后插入数据
void dlist_insert_back(dlist* pos, dlist_data_type x);
//删除指定位置的节点
void dlist_erase(dlist* pos);
//销毁
void dlist_destroy(dlist* phead);
dlist.c:
#define _CRT_SECURE_NO_WARNINGS
#include "dlist.h"
//申请节点
dlist* dlist_buy(dlist_data_type x)
{
dlist* newdlist = (dlist*)malloc(sizeof(dlist));
if (newdlist == NULL)
{
perror("malloc fail!");
return 1;
}
newdlist->data = x;
newdlist->next = newdlist;
newdlist->prev = newdlist;
return newdlist;
}
//初始化
//void dlist_init(dlist** pphead)
//{
// assert(pphead);
// *pphead = dlist_buy(-1);
//}
dlist* dlist_init()
{
dlist* phead = dlist_buy(-1);
return phead;
}
//打印数据
void dlist_print(dlist* phead)
{
assert(phead);
dlist* pcur = phead->next;
while (pcur != phead)
{
printf("%d->", pcur->data);
pcur = pcur->next;
}
printf("NULLn");
}
//查找
dlist* dlist_find(dlist* phead, dlist_data_type x)
{
assert(phead);
dlist* pcur = phead->next;
while (pcur != phead)
{
if (pcur->data == x)
{
return pcur;
}
pcur = pcur->next;
}
return NULL;
}
//插入操作不改变哨兵位,因此传一级指针即可
//尾插
void dlist_push_back(dlist* phead, dlist_data_type x)
{
assert(phead);
dlist* newdlist = dlist_buy(x);
//新尾节点
newdlist->prev = phead->prev;
newdlist->next = phead;
//旧尾节点
phead->prev->next = newdlist;
//头节点(哨兵位)
phead->prev = newdlist;
}
//头插
void dlist_push_front(dlist* phead, dlist_data_type x)
{
assert(phead);
dlist* newdlist = dlist_buy(x);
//新节点
newdlist->prev = phead;
newdlist->next = phead->next;
//旧节点
phead->next->prev = newdlist;
//哨兵位
phead->next = newdlist;
}
//尾删
void dlist_pop_back(dlist* phead)
{
assert(phead);
assert(phead->next != phead);//双向链表不能为空
dlist* del = phead->prev;
//新尾节点
del->prev->next = phead;
//哨兵位
phead->prev = del->prev;
free(del);
del = NULL;
}
//头删
void dlist_pop_front(dlist* phead)
{
assert(phead);
assert(phead->next != phead);
dlist* del = phead->next;
del->next->prev = phead;
phead->next = del->next;
free(del);
del = NULL;
}
//在指定位置之后插入数据
void dlist_insert_back(dlist* pos, dlist_data_type x)
{
assert(pos);
dlist* newdlist = dlist_buy(x);
newdlist->prev = pos;
newdlist->next = pos->next;
pos->next->prev = newdlist;
pos->next = newdlist;
}
//删除指定位置的节点
void dlist_erase(dlist* pos)
{
assert(pos);
dlist* del = pos;
pos->prev->next = pos->next;
pos->next->prev = pos->prev;
free(pos);
pos = NULL;
}
//销毁
void dlist_destroy(dlist* phead)
{
assert(phead);
dlist* pcur = phead->next;
while (pcur != phead)
{
dlist* next = pcur->next;
free(pcur);
pcur = next;
}
free(pcur);
pcur = NULL;
}
test.c:
#define _CRT_SECURE_NO_WARNINGS
#include "dlist.h"
void test()
{
//dlist* plist = NULL;
//dlist_init(&plist);
dlist* plist = dlist_init();
//尾插
dlist_push_back(plist, 1);
dlist_push_back(plist, 2);
dlist_push_back(plist, 3);
dlist_print(plist);
//头插
//dlist_push_front(plist, 1);
//dlist_push_front(plist, 2);
//dlist_push_front(plist, 3);
//dlist_print(plist);
//尾删
//dlist_pop_back(plist);
//dlist_pop_back(plist);
//dlist_pop_back(plist);
//dlist_print(plist);
//头删
//dlist_pop_front(plist);
//dlist_print(plist);
//dlist_pop_front(plist);
//dlist_print(plist);
//dlist_pop_front(plist);
//dlist_print(plist);
//dlist* find = dlist_find(plist, 2);
//dlist_insert_back(find, 4);
//dlist_print(plist);
//dlist* find = dlist_find(plist, 2);
//dlist_erase(find);
//find = NULL;
//dlist_print(plist);
dlist_destroy(plist);
plist = NULL;//手动置空
}
int main()
{
test();
return 0;
}
Sekvenssitaulukko | Linkitetty luettelo (kaksinkertainen linkitetty luettelo) | |
---|---|---|
Säilytystilalla | loogisesti ja fyysisesti jatkuvaa | Loogisesti jatkuva, ei välttämättä fyysisesti jatkuva |
satunnainen pääsy | Monimutkaisuus O(1) | Monimutkaisuus O(N) |
Lisää tai poista tietoja missä tahansa | Tiedot on siirrettävä, monimutkaisuus O(N) | Vaihda vain osoitin kohtaan |
lisää | Dynaamista järjestystaulukkoa voidaan laajentaa, kun tilaa ei ole riittävästi. | Ei käsitettä kapasiteetista |
Sovellusskenaariot | Tehokas tiedon tallennus + toistuva pääsy | Lisää ja poista tietoja usein missä tahansa |
välimuistin käyttö | korkea | Matala |
Sekvenssiluetteloilla ja linkitetyillä listoilla on toisiaan täydentäviä etuja, ja ne voivat käyttää vastaavia etujaan erilaisissa sovellusskenaarioissa.
- Jos sovellusskenaario vaatii toistuvia haku- ja poistotoimintoja ja voi hyväksyä enemmän muistinkulutusta, kaksoislinkitetty luettelo voi olla sopivampi.
- Jos muisti on rajallinen tai haku- ja poistotoimintojen tehokkuus ei ole korkea, yksittäin linkitetty luettelo voi olla sopivampi.