数据结构-单链表常见面试题

一、题目
1、从尾到头打印单链表 (两种方法)
2、删除一个无头单链表的非尾节点(不能遍历链表)
3、在无头单链表的一个节点前插入一个节点(不能遍历链表)
4、单链表实现约瑟夫环(JosephCircle)
5、逆置/反转单链表
6、单链表排序(冒泡排序&快速排序)
7、合并两个有序链表,合并后依然有序
8、查找单链表的中间节点,要求只能遍历一次链表
9、查找单链表的倒数第k个节点,要求只能遍历一次链表
10、删除链表的倒数第K个结点

//从尾到头打印单链表
void SLinkPrintTailToHead(SLinkNode* phead)
{
      if(phead==NULL)
      {
            return ;
      }

      SLinkNode* end=NULL;
      SLinkNode* cur=phead;
      printf(" actual:");
      while(  end!=phead)
      {
            SLinkNode* cur=phead;
          //找到尾节点
            while(  cur->next!=end)
            {
                  cur=cur->next;
            }
            printf("%c  ",cur->data);
            //更新尾节点
            end=cur;
      }
}
从尾到头打印单链表2(递归调用)
void SLinkPrintTailToHeadR(SLinkNode* phead)
{
      if(  phead==NULL)
      {
            return;
      }
      else
      {
            SLinkPrintTailToHeadR(phead->next);
            printf("%c ",phead->data);

      }
}

这里写图片描述

  • 删除一个无头非尾节点
    这里写图片描述

void SLinkDelNode(SLinkNode* pos)
{
      if(  pos!=NULL&&pos->next!=NULL)
      {
            return;
      }
      //保存下一个节点,便于销毁
      SLinkNode* next=pos->next;
      //pos-next的值赋值给pos
      pos->data=pos->next->data;
      //修改pos->next指向
      pos->next=pos->next->next;
      //销毁next
      free(next);
      next=NULL;
}
  • 在无头单链表的一个节点前插入一个节点
    这里写图片描述


void SLinkNoHeadInsert(SLinkNode* pos,DataType value)
{
      if(pos==NULL)
      {
            return ;
      }
      //创建一个新节点
      SLinkNode* newnode=SLinkCreat(value);

      //将pos->data和newnode的值互换
      newnode->data=pos->data;
      pos->data=value;
      //修改pos->next和newnode->next指针指向指向
      newnode->next=pos->next;
      pos->next=newnode;
}

这里写图片描述
- 单链表实现约瑟夫环
这里写图片描述



SLinkNode* SLinkJoSePhCircle(SLinkNode* phead)
{
    //没有环,无法构成约瑟夫环
     if(  phead==NULL)
     {
           return NULL;

     }
     int step=0;
     SLinkNode* cur=phead;
     SLinkNode* tmp=NULL;
     //构造一个带环的链表
     while(cur->next!=phead)
     {
           cur=cur->next;
     }
     cur->next=phead;
     //开始杀人
     while(cur->next!=cur)
     {
           cur=cur->next;
           ++step;
           if(step==2)
           {
                tmp=cur->next;
                cur->data=tmp->data;
                cur->next=cur->next->next;
                step=0;
                free(tmp);
                tmp=NULL;



           }
     }
     return cur;
}
  • 反转单链表
    这里写图片描述

SLinkNode* SLinkReverse(SLinkNode* phead)
{

      if(  phead==NULL||phead->next==NULL)
      {
            return phead;
      }
      //p1,p2完成逆置,p3完成标记
      SLinkNode* p1=phead;
      SLinkNode* p2=p1->next;
      SLinkNode* p3=p2->next;
      //逆置
      while(p2)
      {
            //将p2指向p1
            p2->next=p1;
            //更新p1
            p1=p2;
            //更新p2
            p2=p3;
            //更新p3
            if(  p3)
            {
                  p3=p3->next;
            }
      }
      //更改头指针指向(此时头指针还指向开始位置)
      phead->next=NULL;
      phead=p1;
      return phead;


}
  • 查找中间节点
    这里写图片描述
SListNode* SListFindMidNode(SListNode* list) //查找单链表的中间节点,要求只能遍历一次链表
{
    SListNode *fast;
    SListNode *slow;
    assert(list);
    fast=slow=list; 
    //如果链表没节点,一个或者两个节点,直接返回头节点
    if (NULL==list || NULL==list->_next || NULL==list->_next->_next)      
    {
        return list;
    }
    while (fast) //快指针走到尾,慢指针走到中间。
    {
        fast=fast->_next->_next;  //快指针一次走两步 
        slow=slow->_next;  //慢指针一次走一步
    }
    return slow; //返回中间节点的地址
}
  • 查找单链表的倒数第K个节点,要求只能遍历一次链表
    这里写图片描述

SLinkNode* SLinkFindKNode(SLinkNode* phead,size_t k)
{
      assert(  phead);



      //定义一个快指针,一个慢指针
      SLinkNode* slow=phead;
      SLinkNode* fast=phead;
      //让快指针先走k-1步,然后快慢指针一起走
      while(--k)
      {
            if(  fast==NULL)
            {
                  return phead;
            }
            fast=fast->next;
      }
      while(  fast->next!=NULL)
      {
          slow=slow->next;
          fast=fast->next;
      }
      //走到链表结尾,慢指针就是倒数第K个节点,返回慢指针
      return slow;
}
  • 单链表排序(冒泡排序)

这里写图片描述


void SeqListSort(SListNode *Seq)//单链表排序(冒泡排序)
{
    SListNode *tail,*tmp=Seq;
    DataType flag=0;
    assert(Seq);
    if (Seq->_next==NULL||Seq==NULL)  //防止是空链表或者只有一个节点
    {
        return;
    }
    while (tmp!=NULL)  //找尾节点
    {
        tmp=tmp->_next;
    }
    tail=tmp;  
    while (tail!=Seq)
    {
        tmp=Seq;  //重置头节点
        while (tmp->_next!=tail)
        {
            if (tmp->_data>tmp->_next->_data)    //循环冒泡
            {
                flag=1;
                tmp->_data^=tmp->_next->_data;
                tmp->_next->_data^=tmp->_data;
                tmp->_data^=tmp->_next->_data;
            }
            tmp=tmp->_next;  //循环后移
        }
        if (flag==0)  //优化冒泡次数
        {
            break;
        }
        tail=tmp; //尾前移
    }
}

这里写图片描述


  • 合并两个有序链表

这里写图片描述

SListNode* SListMerge(SListNode *list1,SListNode *list2)//合并两个有序链表,合并后依然有序 
{
    SListNode *list,*tmp;
    assert(list1&&list2);
    if (list1->data<list2->data)  //找到两个链表中小的一个头节点
    {
        list=list1;
        list1=list1->next;
    }
    else
    {
        list=list2;
        list2=list2->next;
    }
    tmp=list; //将小的一个链表头节点作为新链表的节点
    while (NULL!=list1 && NULL!=list2)  //循环比较,取两链表中小的节点链给list
    {
        if (list1->data<list2->data)
        {
            list->next=BuySListNode(list1->data);  //
            list1=list1->next;
            list=list->next;
        }
        else
        {
            list->next=BuySListNode(list2->data);
            list2=list2->next;
            list=list->next;
        }
    }
    if (NULL==list1)  //取出剩下一个量表后面所有连接给list
    {
        list->next=list2;
    }
    else
    {
        list->next=list1;
    }
    return tmp; //返回新链表的头节点地址
}

这里写图片描述

  • 求链表的长度

//求链表的长度
size_t SLinkSize(SLinkNode* phead)
{
      if(  phead==NULL)
      {
            return ;
      }
      SLinkNode* cur=phead;
      size_t count=0;
      while(cur!=NULL)
      {
            ++count;
            cur=cur->next;
      }
      return count;
}

这里写图片描述

这里写图片描述


//删除链表倒数第K个节点
void SLinkDelKNode(SLinkNode** pphead,size_t k)
{
      //非法输入
      if(  pphead==NULL)
      {
            return ;
      }
      //空链表,直接返回
      if(  *pphead==NULL)
      {
            return ;
      }
      size_t len=SLinkSize(*pphead);
      //K在链表长度之外,直接返回
      if(  k>len)
      {
            return ;
      }
      //K刚好等于链表的长度,直接调用头删
      else if(  k==len)
      {
            SLinkPopFront(pphead);
            return ;
      }
      //其他位置,遍历找到第K个节点之前的节点

      else
      {
            SLinkNode* cur=*pphead;
            int i=0;
            for(  ;i<len-k-1;i++)
            {
                  cur=cur->next;
            }
            //找到第K个节点之前的节点
            SLinkNode* to_delete=cur->next;
            //修改第K个节点之前的节点与第K个节点之后的节点关系指向
            cur->next=to_delete->next;
            //释放第K个节点,并置为NULL,避免成为野指针
            free(  to_delete);
            to_delete=NULL;

      }

}

这里写图片描述

//判断链表是否带环
这里写图片描述


//判断链表是否带环
size_t SLinkHasCircle(SLinkNode* phead)
{
      if(  phead==NULL)
      {
            return 0;
      }
      //定义一个快指针,一次走两步
      //定义一个慢指针,一次走一步
      SLinkNode* fast=phead;
      SLinkNode* slow=phead;
      //如果链表带环,则两个指针一定会相遇
      //相遇返回1,否则返回0
      while(  fast!=NULL&&fast->next!=NULL)
      {
            fast=fast->next->next;
            slow=slow->next;
            if(  fast==slow)
            {
                  return 1;
            }
      }
      return 0;

}

求环的长度

//求环的长度
size_t SLinkGetCircleLenth(SLinkNode* phead)
{
      if(  phead==NULL)
      {
            return 0;
      }
      //定义一个指针,指向前面快慢指针相遇点
      //让其沿环走一圈,即是环长度
      SLinkNode *cur=SLinkHasCircle(phead);
      SLinkNode* tmp=cur;
      size_t count=0;
      while(cur->next!=NULL)
      {

            count++;
            if(cur==tmp)
            {
                  break;
            }
      }
      return count;

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值