嵌入式学习笔记DAY20(链表,gdb调试)

一、链表

1. 练习

  • 单链表——尾插
int InsertTailLinkList(LinkList *ll, DATATYPE *data)
{
    // 创建新节点并分配内存
    LinkNode* newnode1 = malloc(sizeof(LinkNode));
    
    // 内存分配失败检查
    if (newnode1 == NULL)
    {
        fprintf(stderr, "InsertTailLinkList malloc failed\n");
        return 1;  // 返回错误码1表示内存分配失败
    }

    // 复制数据到新节点
    memcpy(&newnode1->data, data, sizeof(DATATYPE));
    newnode1->next = NULL;  // 新节点的next指针置为NULL,作为尾节点

    // 处理链表为空的特殊情况
    if(IsEmptyLinkList(ll))
    {
        ll->head = newnode1;  // 空链表时,新节点直接成为头节点
    }
    else
    {
        // 链表非空时,寻找当前尾节点
        LinkNode* tail = ll->head;
        
        // 循环遍历直到找到尾节点(next为NULL的节点)
        while(tail->next) 
        {
            tail = tail->next;
        }
        
        // 将新节点插入到尾节点之后
        tail->next = newnode1;
    }
   
    ll->clen++;  // 更新链表长度
    return 0;    // 返回0表示插入成功
}
  • 链表——按位置插入 
int InsertAtPosition(LinkList *ll, int pos, DATATYPE *data) {

  if (ll == NULL || pos < 0 || pos > ll->clen) {
    return 1; // pos的位置应该在合理范围之内
  }

  if (pos == 0) {
    return InsertHeadLinkList(ll, data); //如果pos为0,那么直接掉迎头差
  }

  //创建新节点,为新节点分配内存,使用memcpy将data复制到新节点的data成员中
  LinkNode *newnode = malloc(sizeof(LinkNode));
  if (newnode == NULL) {
    fprintf(stderr, "InsertAtPosition malloc failed\n");
    return 1;
  }
  memcpy(&newnode->data, data, sizeof(DATATYPE));

  //遍历到第pos-1个节点(即想要插入位置的前面一个节点)
  LinkNode *prev = ll->head; //
  for (int i = 0; i < pos - 1; i++) {
    prev = prev->next;
  }

  newnode->next = prev->next; //新节点的 next 指向 prev 的下一个节点
  prev->next = newnode;       // prev 的 next 指向新节点

  ll->clen++;
  return 0;
}
  • 修改链表
int ModifyLinkList(LinkList *ll, char *name, DATATYPE *data) 
{
  DATATYPE *tmp = FindLinkList(ll, name);
  if (NULL == tmp) 
  {
    return 1;
  }
  memcpy(tmp, data, sizeof(DATATYPE));
  return 0;
}
  •  链表的销毁
int DestroyLinkList(LinkList *ll) 
{
  // 检查链表是否为空或已被销毁
  if (ll == NULL || ll->head == NULL) {
    return 0;  // 空链表无需处理,直接返回成功
  }

  
  LinkNode *tmp = ll->head;  // 初始化临时指针,指向链表头节点
  LinkNode *next;            // 用于保存下一个节点的指针

  // 遍历链表,逐个释放节点
  while (tmp != NULL) 
  {
    
    next = tmp->next;  // 保存当前节点的下一个节点指针
    
    free(tmp);         // 释放当前节点的内存
    
   
    tmp = next;        // 移动临时指针到下一个节点
  }

  // 链表销毁后,重置链表头指针为NULL,节点计数为0
  ll->head = NULL;
  ll->clen = 0;

  return 0;  // 返回成功状态码
}

 

2.  gdb调试 

一般调试步骤与命令:

1、gcc -g  *.c 加上调试选项(eg:gcc -g main.c linklist.c)

2、gdb a.out(调试可执行文件,eg:gdb ./a.out)

3、r  运行(出现页面然后进行输入)

4、b fun.c:36 设置断点,运行到这个位置,程序自动暂停

  •  b :100          默认停在main.c的100行;
  •  b fun.c : 36     停在fun.c的36行
  •  b 函数名          eg: b InserPosLinkList)

5、n 执行下一步  步过(如果是函数,直接调用结束)

      s  步入自定义函数(系统函数不入)

6、使用p命令,查看变量或指针等数据

  •    p  变量: 显示变量值   eg:p len
  •    p  指针: 看地址          eg:p *data

7、q命令 退出(y)

3. 练习

  • 查找链表中间节点
DATATYPE *FindMiddleLinklist(LinkList *ll) {
   
    LinkNode *slow = ll->head;   //初始化慢指针slow,使其指向链表的头节点
    
    LinkNode *fast = ll->head;   //初始化快指针fast,使其也指向链表的头节点

    // 检查链表是否为空,如果为空,直接返回NULL
    if (NULL == ll) {
        return NULL;
    }

    // 使用快慢指针法寻找中间节点
    // 当快指针fast不为空,且fast的下一个节点和下下个节点都不为空时,循环继续
    while (fast != NULL && fast->next->next != NULL) {
        
        fast = fast->next->next;  // 快指针每次移动两步
        
        slow = slow->next;        // 慢指针每次移动一步
    }

    // 循环结束后,此时慢指针slow指向的节点即为中间节点(或中间偏右节点,取决于链表长度奇偶性)
    // 返回中间节点的数据指针
    return &slow->data;
}

如何返回偏前的中间节点?

修改循环条件,让快指针提前一步终止

 
LinkNode* FindMiddleLinklist(LinkList* ll) {
    if (ll == NULL || ll->head == NULL) return NULL;
    
    LinkNode* slow = ll->head;
    LinkNode* fast = ll->head;
    
    // 修改循环条件:fast->next->next != NULL
    while (fast != NULL && fast->next != NULL && fast->next->next != NULL) {
        slow = slow->next;
        fast = fast->next->next;
    }
    
    return slow;
}

  •  找倒数第k个节点
LinkNode* FindKthFromEnd(LinkList* ll, int k) 
{
    // 处理空链表以及无效的k值
    if (ll == NULL || ll->head == NULL || k <= 0) return NULL;
    
    // 检查k是否超过链表长度
    if (k > ll->clen)
    {
        fprintf(stderr, "Error: k (%d) exceeds list length (%d)\n", k, ll->clen);
        return NULL;
    }
    
    LinkNode* fast = ll->head;
    LinkNode* slow = ll->head;
    
    // 1.快指针先走k步
    for (int i = 0; i < k; i++) 
    {
        fast = fast->next;
    }
    
    // 2.快慢指针同步移动
    while (fast != NULL) 
   {
        slow = slow->next;
        fast = fast->next;
    }
    
    return slow;  // 返回倒数第k个节点
}

算法核心思想

假设链表总长度为 n,倒数第 k 个节点的正向位置是 n-k+1(从 1 开始计数)。例如:

  • 链表 10 → 20 → 30 → 40 → 50(n=5)

  • 倒数第 2 个节点是 40,其正向位置是 5-2+1=4

双指针法的巧妙之处在于:

  1. 让快指针(fast)先走 k 步,此时快指针距离链表尾部还有 n-k 步。

  2. 然后让慢指针(slow)和快指针同步移动。

  3. 当快指针到达尾部(NULL)时,慢指针恰好走了 n-k 步,此时慢指针的位置就是 n-k+1(倒数第 k 个节点)。

  •  链表的逆序
int RevertLinkList(LinkList *ll) {
  LinkNode *prev = NULL;
  LinkNode *tmp = ll->head;
  LinkNode *next = tmp->next;
  int len = GetSizeLinkList(ll);
  if (len < 2) {
    return 1;
  }
  while (1) {
    tmp->next = prev;
    prev = tmp;
    tmp = next;
    if (NULL == tmp)
      break;
    next = next->next;
  }
  ll->head = prev;
  return 0;
}
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值