关于单链表的一些面试题-基础篇(C语言实现)

关于单链表的一些面试题-基础篇(C语言实现)

1.比较顺序表和链表的优缺点,说说它们分别在什么场景下使用?

@顺序表和链表的优缺点比较

  • 在需要进行频繁查找,但很少进行插入和删除操作的时候,宜采用存储结构
  • 在需要频繁插入、删除时,链表结构更佳
2.从尾到头打印单链表

因为单链表本身属性的性质,在这里采用递归的方式实现从尾到头打印单链表的方法

  • 具体思路如图所示:

    @递归部分思路

  • 代码实现

void PrintTailToHead(ListNode *pList)
{
    if(pList == NULL)
    {
        return;
    }
    PrintTailToHead(pList->next);
    printf("%d->",pList->data);
}
3.删除一个无头单链表的非尾节点

注意:无头单链表并不是指单链表没有头指针,而是指在题目实现中不给出该单链表的头指针

删除无头单链表的非尾结点:
1、需要注意判断是否是非尾结点
2、在移动时注意要给两个指针,方便找到赋值元素的上一个结点位置
3、记得释放被删指针及临时指针的内存,防止出现野指针或者内存泄露的问题

  • 具体思路如图所示:

    @无头单链表删除部分思路示意图

  • 代码实现

void EraseNoHeadList(ListNode* pList,ListNode** pos)
{
    ListNode* next = *pos;
    ListNode* pre = *pos;

    assert(*pos);

    if(((*pos) == pList) || ((*pos)->next == NULL))
    {
        return;
    }
    else
    {
        while(next->next)
        {
            pre = next;
            pre->data = (next->next)->data;
            next = next->next;
        }
        free(next);
        pre->next = NULL;
    }
}
4.在无头单链表的一个节点前插入一个节点

注意:无头单链表并不是指单链表没有头指针,而是指在题目实现中不给出该单链表的头指针

在无头单链表的一个节点前插入一个节点:
1、因为是无头单链表,所以无法找到当前结点的前一个位置
2、故采取在当前结点的下一个位置插入一个新结点
3、之后再交换两结点中的数据即可

  • 具体思路如图所示:

    @无头单链表插入新结点部分思路示意

  • 代码实现

void InsertNoHeadList(ListNode** ppList, ListNode* pos,DataType x)
{

    if(*ppList == NULL)
        *ppList= CreateNode(x);
    else
    {
        ListNode* cur = CreateNode(x);
        cur->next = pos->next;
        pos->next = cur;

        cur->data = pos->data;
        pos->data = x;
    }

}
5.单链表实现约瑟夫环

@约瑟夫环思路简析

  • 实现思路
    1、使单链表成环
    2、判断链表是否为空
    3、循环遍历代码,找到要删除的结点
    4、删除结点

  • 代码实现

ListNode* JosephRing(ListNode* pList, int m)//单链表实现约瑟夫环,m指每一轮需要报的数
{
    ListNode* cur = pList;
    ListNode* next;
    ListNode* tmp = pList;

    int count;

    while(tmp->next)//使单链表成环
    {
        tmp = tmp->next;
    }
    tmp->next = pList;

    if(pList == NULL)//判断链表是否为空
        return;

    while(cur != cur->next)
    {
        count = m;
        while(--count)//走(k-1)步
        {
            cur = cur->next;
        }
        next = cur->next;
        cur->data = next->data;
        cur->next = next->next;
        free(next);

    }
    cur->next = NULL;
    return cur;

}
6.逆置/反转单链表

@逆置思路示意图

  • 实现思路
    1、摘结点
    2、循环赋值给新链表

  • 代码实现

ListNode* Reverse(ListNode *pList)
{

    ListNode* cur = pList;
    ListNode* tmp = NULL;

    ListNode* newlist = NULL;

    while(cur)//摘结点
    {
        tmp = cur;
        cur = cur->next;
        tmp->next = newlist;
        newlist = tmp;
    }
    return newlist;
}
7.单链表排序(冒泡排序)

@冒泡排序部分示意图

实现思路
1、判断链表是否为空
2、用tail指针控制cur停止位置
3、用两层循环分别实现链表遍历&数据交换
4、用exchange优化程序,使程序可以在一定程度上减少遍历链表的次数(当链表部分有序时)

代码实现

void BubbleSort(ListNode* pList)//单链表排序(冒泡排序)
{
    ListNode* tail = pList;
    ListNode* next =pList;
    ListNode* cur = next->next;
    DataType tmp;
    int exchange = 0;

    while(tail->next)//尾指针-控制终止位置
    {
        tail = tail->next;
    }

    //1.链表只有一个元素
    if(pList == NULL || pList->next == NULL)
        return;
    //2.链表有多个元素
    else
    {
        while(tail != pList->next)
        {
            while(cur != tail)
            {
                exchange = 0;
                if(next->data > cur->data)
                {
                    tmp = next->data;
                    next->data = cur->data;
                    cur->data = tmp;
                }
                cur = cur->next;
                next = next->next;

                exchange =  1;
            }
            if(next->data > cur->data)
            {
                tmp = next->data;
                next->data = cur->data;
                cur->data = tmp;
            }

            tail = next;

            next = pList;
            cur = next->next;

            if(exchange = 0)
                break;
        }
    }
}
8.合并两个有序链表,合并后依然有序

@合并部分步骤示意图

实现思路
1、判断两链表是否为空
2、摘取新链表的表头结点
3、比较两链表的大小,合并有序链表

代码实现

ListNode* MergeList(ListNode* List1, ListNode* List2)//合并两个有序链表,合并后依然有序
{
    ListNode* List = NULL;
    ListNode* tmp = NULL;


    //判断链表是否为空
    if(List1 == NULL && List2 == NULL)//两表均为空
        return NULL;

    else if(List1 != NULL || List2 != NULL)//其中一表为空
    {
        if(List1 == NULL)
            return List2;
        return List1;
    }

    else
    {
        if(List1->data < List2->data)//摘取新链表的表头
        {
            List = List1;
            List1 = List1->next;
        }
        else
        {
            List = List2;
            List2 = List2->next;
        }

        tmp = List;

        while(List1 && List2)
        {
            if(List1->data < List2->data)
            {
                List->next = List1;
                List = List->next;
                List1 = List1->next;
            }
            else
            {
                List->next = List2;
                List = List->next;
                List2 = List2->next;
            }
        }

        if(List1 == NULL)
            List->next = List2;
        if(List2 == NULL)
            List->next = List1;
        return tmp;
    }
}
9.查找单链表的中间节点,要求只能遍历一次链表

@查找的部分示意图


实现思路
1、因为要求只能遍历一次链表,所以采用快慢指针的方法来进行查找
2、慢指针每次移动一个结点,快指针每次移动两个结点,即两个指针在每次移动时会拉开1的距离
3、到最后,快指针刚刚好是慢指针的两倍,当时,慢指针应正好指向链表的中间结点。

代码实现

ListNode* FindMiddle(ListNode* pList)//查找单链表的中间节点,要求只能遍历一次链表
{
    ListNode* fast = pList;
    ListNode* slow = pList;

    if(pList == NULL)//链表没有元素
        return NULL;
    else if(pList->next->next == NULL || pList->next == NULL)//链表只有一个or两个元素
        return pList;
    else//链表有多个元素
    {
        while(fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
        }
        return slow;
    }
}
10.查找单链表的倒数第k个节点,要求只能遍历一次链表

@步骤示意图

实现思路
1、先挪快指针k步
2、然后快指针和慢指针一起挪动
3、当fast->next == NULL时,slow即链表中的倒数第k个结点

代码实现

ListNode* FindK(ListNode* pList, int k)//查找单链表的倒数第k个节点,要求只能遍历一次链表
{
    ListNode* fast = pList;
    ListNode* slow = pList;


    if(pList == NULL)//链表没有元素
        return NULL;
    while(--k)
    {
        fast = fast->next;
        if(fast == NULL)
            return NULL;

    }
    while(fast->next != NULL)
    {
        fast = fast->next;
        slow = slow->next;

    }
    return slow;
}
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值