顺序表和链表(二)

简介: 顺序表和链表

3.2 链表的分类


实际中链表的分类的有很多,这里只介绍两类:单向不带头,双向带头


单向不带头:结构简单,一般不会单独用来存储数据。更多的是作为其他数据结构的子结构,例如哈希桶


a981150d910bfd3e08240813f2ca3404_a09b28c400d745298cea137ac932120a.png


双向带头:结构最复杂,一般用来单独存储数据。


e08642c224ec8b2eb11920983d9aef59_804385750e9549f998cff994ef3c7474.png


3.3 链表的实现


单向不带头

定义类型和结构体


typedef int LSdatatype;
typedef struct Slist
{
  LSdatatype data;
  struct Slist* next;
}SL;


0034eaf865d1fb7d8c4ddff8252dafdf_adb495e3d0144f1fbc52eaeaef9c7a87.png


8df17d8d61b5ceac9f536a7d30d1afe7_19035411af2b4014a2f4237c206f0fae.png


打印单链表


//打印单链表
void SLprint(SL* phead);
void SLprint(SL* phead)
{
  SL* tmp = phead;
  while (tmp != NULL)
  {
  printf("%d->", tmp->data);
  tmp = tmp->next;
  }
  printf("NULL\n");
}


销毁单链表


//销毁单链表
void SLdestory(SL* phead);
void SLdestory(SL** pphead)
{
  assert(pphead);
  SL* cur = *pphead;
  while (cur != NULL)
  {
  SL* next = cur->next;
  free(cur);
  cur = next;
  }
  *pphead = NULL;
}


bebae07153ebd3e42520e71299ed593f_5cecc93cde7a4c4ea8c1f36de2d6c43a.png


由于每个节点中都保存下一个节点的地址,不能直接释放*pphead,需要创建临时变量进行替换。


插入数据,便需要创建一个新的节点,由于新节点的创建不止出现一次,为了方便,将其独立为函数


SL* CreateSLnode(LSdatatype x)
{
  SL* newnode = (SL*)malloc(sizeof(SL));
  if (newnode == NULL)
  {
  perror("malloc fail");
  return;
  }
  newnode->data = x;
  newnode->next = NULL;
  return newnode;
}


头插


//头插
void SLpushfront(SL** pphead, LSdatatype x);
void SLpushfront(SL** pphead, LSdatatype x)
{
  SL* newnode = CreateSLnode(x);
  newnode->next = *pphead;
  *pphead = newnode;
}


插入数据 1,2,3,4 监视如下


f802a3f20aef842cd0ec6799530401b2_5ed3a530143b4db4aaba966cadcf82e6.png

6dc6d3b29c87813943593485c3022e1e_7979ec9f83b54dcca2d42e6397b95f64.png



4742a46cbfc20d60982a80367338afd1_7cfd7dad193d43fea4c956e7679ee068.png


注意


改变数据,需要通过指针;改变指针,需要通过指针的指针。

由于上面是需要改变的指针,所有需要通过二级指针进行修改


在之后的学习中可以通过两种方式代替二级指针


  1. 返回新的链表头
  2. 设计为带哨兵位的链表


尾插


//尾插
void SLpushback(SL** pphead, LSdatatype x);
void SLpushback(SL** pphead, LSdatatype x)
{
  assert(pphead);
  SL* newnode = CreateSLnode(x);
  //1.plist 为空 改变结构体指针 
  if (*pphead == NULL)
  {
  *pphead = newnode;
  }
  //2.plist 不为空  改变结构体内容
  else
  {   
     //找尾
  SL* tail = *pphead;
  while (tail->next != NULL)
  {
    tail = tail->next;
  }
  tail->next = newnode;
  }
}


尾插数据 5 ,监视如下


0967afda715dd5c20bc394abbb04dcbd_aa3296d88dd04173a24fb8c8d1b12ce4.png


4ce97776dacc836e76678fef47721b29_fecd08a7dcfc46c5ac92534a567defc8.png


链表为空,插入第一个节点,改变SL*,通过结构体指针的指针pphead


链表不为空,插入节点,改变SL,通过结构体指针SL*tail


头删


//头删
void SLpopfront(SL** pphead);
void SLpopfront(SL** pphead)
{
  assert(pphead);
  SL* del = *pphead;
  //检查,避免数据删除完之后,继续删除数据,导致内存崩溃
  //1 温柔的检查
  while (*pphead == NULL)
  {
  return;
  }
  //暴力检查
  /*assert(*pphead != NULL);*/
  *pphead = (*pphead)->next;
  free(del);
  del = NULL;
}


头删数据 4,监视如下


d3034dc9f8cc08573935d1229289f484_6b30aba5d3e44027b10306f91b58e70d.png


949e10a6a91b69c25df7e77693489096_621e6c50ff3d41d9963211491f9b3c67.png


如果直接将第一个节点删去,就不能找到第二个节点,所以创建临时变量del保存第一个节点,之后再将其删去


尾删


//尾删
void SLpopback(SL** pphead);
void SLpopback(SL** pphead)
{
  assert(pphead);
  //1 温柔的检查
  while (*pphead == NULL)
  {
  return;
  }
  //暴力检查
  /*assert(*pphead != NULL);*/
  //1  一个节点
  if ((*pphead)->next == NULL)
  {
  free(*pphead);
  *pphead = NULL;
  }
  //2  多个节点
  else
  {
  方法1
  //SL* tail = *pphead;
  //while (tail->next->next != NULL)
  //{
  //  tail = tail->next;
  //}
  //free(tail->next);
  //tail->next = NULL;
  //方法2
  //找尾
  SL* tail = *pphead;
  SL* pre = NULL;
  while (tail->next != NULL)
  {
    pre = tail;
    tail = tail->next;
  }
  free(tail);
  tail = NULL;
  pre->next = NULL;
  }
}


尾删数据5,监视如下


538d1494ff5f5c2d5da800fb35897fa1_fcb4a5b9b50b47abb5c3a1873d1c6f36.png


9f22832f40e924aad514fe24923910f3_414b242dca9049b0920166f04fb664f4.png


查找节点


//查找节点
SL* SLfind(SL* phead, LSdatatype x);
SL* SLfind(SL* phead, LSdatatype x)
{
  assert(phead);
  SL* cur = phead;
  while (cur != NULL)
  {
  if (cur->data == x)
  {
    return cur;
  }
  cur = cur->next;
  }
  return NULL;
}


在pos之前插入新节点


//在pos之前插入新节点
void SLinsert(SL** pphead, SL* pos, LSdatatype x);
void SListInsert(SL** pphead, SL* pos, LSdatatype x)
{
  assert(pphead);
  assert(pos);
    //pos在第一个节点
  if (pos == *pphead)
  {
  SListPushFront(pphead, x);
  }
  else
  {
  SL* prev = *pphead;
  while (prev->next != pos)
  {
    prev = prev->next;
    // 暴力检查,pos不在链表中,或者pos的值是错误的
    assert(prev);
  }
  SL* newnode = CreateSLnode(x);
  prev->next = newnode;
  newnode->next = pos;
  }
}


在数据 3,插入数据4,监视如下


3458683373756f5e2d022f3e70c37d45_bc057b0f223e4549877efe017f77cdf0.png


image.png


在pos后面插入新节点


//在pos后面插入新节点
void SLinsertafter(SL* pos, LSdatatype x);
void SLinsertafter(SL* pos, LSdatatype x)
{
  assert(pos);
  SL* newnode = CreateSLnode(x);
  newnode->next = pos->next;
  pos->next = newnode;
}


在数据1 后面插入数据 0,监视如下


69b74f54047de1cd4bccde8539f23f74_009bcd361f73408ca0448d57cf0d78a9.png


0970f7b2573c14b5e56b147dce779d4d_f8facdaf9b884165bc235ce32174eaeb.png


删除pos位置


//删除pos位置
void SLerase(SL* pphead, SL* pos);
void SLerase(SL** pphead, SL* pos)
{
  assert(pphead);
  assert(pos);
  //pos是第一个节点
  if (*pphead == pos)
  {
  SLpopfront(pphead);
  }
  else
  {
  SL* tmp = *pphead;
  while (tmp->next != pos)
  {
    tmp = tmp->next;
  }
  tmp->next = pos->next;
  free(pos);
  //不需要将pos置空,改变pos不会改变链表
  //pos=NULL
  }
}


将数据2所在位置进行删除,监视如下


59ed4b19851de140d035142f66173d11_69f422707ff54ccf8ecf86fe77b5e8bf.png

01511f8d077bde27b2e4e71e2a8ff4d1_f8290e0c230b482abb8f41402e478ed7.png


删除pos后面的位置


//删除pos后面的位置
void SLeraseafter(SL* pos);
void SLeraseafter(SL* pos)
{
  assert(pos);
  if (pos->next == NULL)
  {
  return;
  }
  else
  {
  SL* next = pos->next;
  pos->next = next->next;
  free(next);
  }
}


将数据1所在位置后面的位置进行删除,监视如下


a6c8aa46f87f7030eb1b9280e2ce8dcf_982dca357382455bb4ab26822bda1f0a.png

b23002abd824e8a78980bc05aa5a921c_f5fccc1bfd3344b99e6acaa0b85b18a1.png


目录
相关文章
|
28天前
|
存储
顺序表和链表(2)
【10月更文挑战第23天】
顺序表和链表(2)
|
29天前
|
存储 算法 数据管理
顺序表和链表(1)
【10月更文挑战第22天】
|
2月前
|
存储 安全 Java
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
25 3
|
6月前
|
存储 缓存 算法
数据结构和算法学习记录——总结顺序表和链表(双向带头循环链表)的优缺点、CPU高速缓存命中率
数据结构和算法学习记录——总结顺序表和链表(双向带头循环链表)的优缺点、CPU高速缓存命中率
52 0
|
7月前
|
存储 缓存
[数据结构]——顺序表和链表
[数据结构]——顺序表和链表(下):https://developer.aliyun.com/article/1515507
|
4月前
|
存储 算法
【初阶数据结构篇】顺序表和链表算法题
此题可以先找到中间节点,然后把后半部分逆置,最近前后两部分一一比对,如果节点的值全部相同,则即为回文。
32 0
|
4月前
|
存储 缓存
【数据结构】——顺序表与链表
【数据结构】——顺序表与链表
|
6月前
|
存储 索引
顺序表和链表
通过以上示例,我们可以看到顺序表和链表在实际应用中如何操作。顺序表适合于需要频繁读取数据的场景,而链表则更适用于频繁修改数据的情况。在选择使用哪种数据结构时,应考虑到实际应用的需求和上下文环境。
34 2
|
6月前
|
存储
2.顺序表_链表(附练习)
2.顺序表_链表(附练习)
|
6月前
|
存储 算法
数据结构和算法学习记录——线性表之单链表(上)-初始单链表及其尾插函数(顺序表缺陷、单链表优点、链表打印)
数据结构和算法学习记录——线性表之单链表(上)-初始单链表及其尾插函数(顺序表缺陷、单链表优点、链表打印)
39 0