【编织时空二:探究顺序表与链表的数据之旅】(下)

简介: 【编织时空二:探究顺序表与链表的数据之旅】

【编织时空二:探究顺序表与链表的数据之旅】(上):https://developer.aliyun.com/article/1424871


有问题,我们发现将 tail 置为空后,但是3结点位置的 next 并没有置为空,那么就会出现野指针的问题。解决这个问题的关键就是将3结点位置的 next置为空.


方法一:创建新结点,让这个结点的位置的 next 等于 tail

// 单链表的尾删
void SListPopBack(SLTNode** pplist)
{
    assert(pplist); 
    //1.链表为空
  assert(*pplist != NULL);
  //2.链表只剩下一个元素
  if ((*pplist)->next == NULL)
  {
    free(*pplist);
    *pplist = NULL;
  }
  //3.链表有多个元素
  else
  {
    SLTNode* tailPrev = NULL;
    SLTNode* tail = *pplist;
    while (tail->next!= NULL)
    {
      tailPrev = tail;
      tail = tail->next;
    }
    free(tail);
    tailPrev->next = NULL;
  }
}


方法二:不创建新结点,使用 tail->next->next找到3结点位置

// 单链表的尾删
void SListPopBack(SLTNode** pplist)
{
    assert(pplist); 
    //1.链表为空
  assert(*pplist != NULL);
  //2.链表只剩下一个元素
  if ((*pplist)->next == NULL)
  {
    free(*pplist);
    *pplist = NULL;
  }
  //3.链表有多个元素
  else
  {
    SLTNode* tail = *pplist;
    while (tail->next->next != NULL)
    {
      tail = tail->next;
    }
    free(tail->next);
    tail->next = NULL;
  }
}


(5)、单链表头删:void SListPopFront(SLTNode** pplist)


单链表如何进行头删呢?头删需要找到头结点的 next ,将其  next 用一个新结点 newhead 保存起来,然后释放原来的头结点,再将 newhead 赋给 pplist 即可。

// 单链表头删
void SListPopFront(SLTNode** pplist)
{
    assert(pplist);
  //空
  assert(*pplist);
  //非空
  SLTNode* newhead = (*pplist)->next;
  free(*pplist);
  *pplist = newhead;
}


(6)、单链表查找:SLTNode* SListFind(SLTNode* plist, SLTDataType x)


要查找某个数字的在链表中的位置,需要遍历链表,让 tail指向空的位置,这样链表中的每个数据都能被访问到,就能查找的数字的在链表中的位置。

// 单链表查找
SLTNode* SListFind(SLTNode* plist, SLTDataType x)
{
  SLTNode* cur = plist;
  while (cur != NULL)
  {
    if (cur->data == x)
      return cur;
    cur = cur->next;
  }
  //没找到数据
  return NULL;
}



(7)、单链表在pos位置之前插入x:void SListInsert(SLTNode** plist, SLTNode* pos, SLTDataType x)


我们这里设置plist为二级指针,因为pos位置可能会头插,需要改变头结点指向的结点。

// 单链表在pos位置之前插入x
void SListInsert(SLTNode** plist, SLTNode* pos, SLTDataType x)
{
    assert(plist);
  assert(pos);
  SLTNode* newnode = BuySListNode(x);
  //头插
  if (pos == *plist)
  {
    newnode->next = *plist;
    *plist = newnode; 
  }
  //中间插入,不可能尾插
  else
  {
    SLTNode* posPrev = *plist;
    while (posPrev->next != pos)
    {
      posPrev = posPrev->next;
    }
    newnode->next = posPrev->next;
    posPrev->next = newnode;
    /*posPrev->next = newnode;
    newnode->next = pos;*/
  }
}


单链表在pos位置之前插入x,有两种情况,一种是头插,一种是在中间插入。头插可以复用之前的函数,但是注意传入的参数,中间插入的话首先要找到pos位置前一个结点posPrev,将posPrev位置的next指向要插入的结点,再将要插入的结点的next给到pos,即可完成连接。


(8)、单链表在pos位置之后插入x:void SListInsertAfter(SLTNode* pos, SLTDataType x)


在pos位置之后插入x不会出现头插的现象,所以这里我们不会改变plist,所以这个参数也就不用掺入了

// 单链表在pos位置之后插入x
void SListInsertAfter(SLTNode* pos, SLTDataType x)
{
  assert(pos);
  SLTNode* newnode = BuySListNode(x);
  newnode->next = pos->next;
  pos->next = newnode;
}


(9)、删除pos位置的值:void SListErase(SLTNode* pos, SLTDataType x);


单链表删除pos位置的值,有两种情况,一种是头删,一种是在中间删除。头删可以复用之前的函数,但是注意传入的参数,中间删除的话首先要找到pos位置前一个结点posPrev,将posPrev位置的next指向pos位置的next,即可完成删除。

// 删除pos位置的值
void SListErase(SLTNode** plist, SLTNode* pos)
{
  assert(plist);
    assert(pos);
  if (*plist == pos)
  {
    SLTNode* Next = pos->next;
    free(pos);
    *plist = Next;
  }
  else
  {
    SLTNode* posPrev = *plist;
    while (posPrev->next != pos)
    {
      posPrev = posPrev->next;
    }
    posPrev->next = pos->next;
    free(pos);
  }
}


(10)、单链表删除pos位置之后的值:void SListEraseAfter(SLTNode* pos)


这个函数有个缺点:不能删头,同时pos为尾结点,那么此时删除pos位置之后的值就无意义

// 单链表删除pos位置之后的值
void SListEraseAfter(SLTNode* pos)
{
  assert(pos);
  //删除pos位置之后的值就无意义
  if (pos->next == NULL)
    exit(-1);
  SLTNode* posNext = pos->next;
  pos->next = posNext->next;
  free(posNext);
}


这里我们为什么不写成pos->next = pos->next->next呢?为什么还要传教一个posNext结点呢?


因为如果我们写成pos->next = pos->next->next,那么删除的那个结点我们就丢失了,无法找到,就会早成内存泄露的问题。


(11)、单链表打印:void SListPrint(SLTNode* plist);


第一步:输出第一个节点的数据域,输出完毕后,让指针保存后一个节点的地址

第二步:输出移动地址对应的节点的数据域,输出完毕后,指针继续后移

第三步:以此类推,直到节点的指针域为NULL

// 单链表打印
void SListPrint(SLTNode* plist)
{
  SLTNode* cur = plist;
  while (cur != NULL)
  {
    printf("%d->", cur->data);
    cur = cur->next;
  }
  printf("NULL\n");
}


(12)、单链表销毁:void SListDestroy(SLTNode** pplist);


单链表的销毁比较简单,只保存cur下一个位置next,然后将当前的cur结点释放,再将cur移动到下一个结点的位置next,依次遍历直至cur为空即可完成销毁。

void SListDestroy(SLTNode** pplist)
{
  assert(pplist);
  SLTNode* cur = *pplist;
  SLTNode* next = NULL;
  while (cur)
  {
    next = cur->next;
    free(cur);
    cur = next;
  }
  *pplist = NULL;
}

相关文章
|
7月前
|
存储 监控 算法
公司监控上网软件架构:基于 C++ 链表算法的数据关联机制探讨
在数字化办公时代,公司监控上网软件成为企业管理网络资源和保障信息安全的关键工具。本文深入剖析C++中的链表数据结构及其在该软件中的应用。链表通过节点存储网络访问记录,具备高效插入、删除操作及节省内存的优势,助力企业实时追踪员工上网行为,提升运营效率并降低安全风险。示例代码展示了如何用C++实现链表记录上网行为,并模拟发送至服务器。链表为公司监控上网软件提供了灵活高效的数据管理方式,但实际开发还需考虑安全性、隐私保护等多方面因素。
105 0
公司监控上网软件架构:基于 C++ 链表算法的数据关联机制探讨
|
6月前
课时141:链表(数据删除)
1.数据删除的定义 2.在 ILink 接口里面追加新的删除方法 3.后续节点判断 4.完善 LinkImpl 子类中的 remove() 方法
|
6月前
|
存储
课时140:链表(判断数据是否存在)
在一个集合中往往会保存大量的数据,有时候会需要判断数据是否会存在。我们将使用对象比较的方式( Equals 方法)来实现这个功能。
|
6月前
|
索引
课时139:链表(修改指定索引数据)
现在已经可以通过索引来获取链表中的指定数据,既然可以获取数据,那么也就可以实现修改指定索引位置的数据这种常见功能。 本节将介绍如何实现这个功能。
|
9月前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
10月前
|
存储
顺序表和链表(2)
【10月更文挑战第23天】
171 2
顺序表和链表(2)
|
10月前
|
存储 算法 数据管理
顺序表和链表(1)
【10月更文挑战第22天】
118 2
|
11月前
|
存储 安全 Java
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
77 3
|
存储 SQL 算法
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表
|
存储 SQL 算法
LeetCode 题目 86:分隔链表
LeetCode 题目 86:分隔链表