单链表常见面试题(一)

一:从尾到头打印单链表

思路:从尾到头,我们可以很容易想到使用函数递归的思想,要打印第一个节点,必须先打印第二个,要打印第二个必须先打印第三个……,所以这里方法最简单的就是用函数递归,代码实现如下:

void PrintSListFromTail2Head(PNode pHead)//将单链表头节点传过来
{
    if (pHead->_pNext != NULL)
    {
        PrintSListFromTail2Head(pHead->_pNext);
    }

    printf("%d ", pHead->data);
    return;
}

还有就是可以创建一个结构体数组,将单链表保存到数组中,然后从尾到头打印数组

二:删除无头单链表的非尾节点(不能遍历链表)

思路:因为是非尾节点,且不能遍历链表,找不到pos前面的那个节点,所以不能按照常规思路,找到pos前面的节点,让其_pNext指向pos后一个节点,只能删除pos后面的节点,并把后面的内容赋给pos,代码实现如下:

void DeleteListNotTailNode(PNode pos)
{
    PNode pNext = pos->_pNext;
    pos->data = pNext->data;//先将数据覆盖pos
    pos->_pNext = pNext->_pNext;//再链接后面的节点
    free(pNext);//释放节点
    pNext = NULL;

}

三:在无头单链表一个节点前插入一个节点(不能遍历链表)

思路:与上题一样因为没有头节点,不能遍历链表,所以不可能找到pos前面的节点只能找到后面的节点,然后在那之间插入新节点,最后将新节点的内容与pos交换(因为人家让插到pos位置),代码实现如下:

void InesrtPosFront(PNode pos, DataType data)
{
    PNode Next = pos->_pNext;//先保存pos的下一个节点
    pos->_pNext = BuyNewNode(pos->data);//将新节点链接到pos后面,传pos的data为了避免以后再交换
    pos->_pNext->_pNext = Next;//将pos之前的下个节点连接到新节点后面
    pos->data = data;//将传过来的data覆盖原data
}

四:单链表实现约瑟夫环(Josephcircle)

思路:约瑟夫环:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人只剩一个,代码实现如下:

void JosephCircle(PNode* pHead, const int M)
{
    assert(pHead);

    while ((*pHead)->_pNext != *pHead)//等于说明只剩一个节点
    {
        PNode pCur = *pHead;
        PNode pPre = *pHead;
        int count = M;

        while (--count)//注意,这里是前置--
        {
            pPre = pCur;
            pCur = pCur->_pNext;
        }
        pPre->_pNext = pCur->_pNext;//链接链表
        *pHead = pCur->_pNext;//改变头指针位置
        free(pCur);
    }
}

五:逆置/反转单链表

方法一:三指针,思路:将原本_pNext域指向下一个节点,改成指向上一个节点,就完成了逆置,代码事项如下:

void ReverseSList(PNode* pHead)
{
    assert(pHead);
    PNode pPre = NULL;
    PNode pCur = *pHead;
    PNode pNext = NULL;

    if (NULL == *pHead)
        return;

    while (pCur)
    {
        pNext = pCur->_pNext;
        pCur->_pNext = pPre;//将Next指针域指向上一个节点
        pPre = pCur;
        pCur = pNext;
    }

    *pHead = pPre;//pPre成为新的头节点

}

方法二头插法思路:创建一个头指针赋空,就然后将单链表从前到后每一个节点头插到创建的头指针中,代码实现如下:

PNode ReverseSListOP(PNode pHead)
{
    PNode pCur = pHead;
    PNode pNewHead = NULL;

    while (pHead)
    {
        pCur = pHead;//进来就让pCur指向头部
        pHead = pHead->_pNext;//头向后走一步
        pCur->_pNext = pNewHead;//pCur插到新头部前
        pNewHead = pCur;//新头为pCur
    }

    return pNewHead;
}

六:合并两个有序链表,合并后依然有序

思路:创建一个新头节点赋空,创建两个指针分别指向两个链表的头节点,从前到后开始比较,较大的尾插到新头节点,代码实现如下:

PNode MergeSList(PNode pHead1, PNode pHead2)
{
    PNode pCur1 = pHead1;
    PNode pCur2 = pHead2;
    PNode pNewHead = NULL;
    PNode pTail = NULL;//用来记录合并后链表的最后一个节点

    if (NULL == pHead1)
        return pHead2;
    if (NULL == pHead2)
        return pHead1;
    //放pNewHead
    if (pCur1->data < pCur2->data)
    {
        pNewHead = pCur1;
        pCur1 = pCur1->_pNext;
    }

    else if (pCur1->data >= pCur2->data)
    {
        pNewHead = pCur2;
        pCur2 = pCur2->_pNext;
    }
    //两链表中较小的节点尾插到pNewHead
    pTail = pNewHead;
    while (pCur1 && pCur2)//只要有一个链表为空就出来
    {
        if (pCur1->data < pCur2->data)
        {
            pTail->_pNext = pCur1;
            pCur1 = pCur1->_pNext;  
        }
        else if (pCur1->data >= pCur2->data)
        {
            pTail->_pNext = pCur2;
            pCur2 = pCur2->_pNext;
        }
        pTail = pTail->_pNext;

    }

    if (pCur1)//说明pCur2为空
        pTail->_pNext = pCur1;

    else
        pTail->_pNext = pCur2;

    return pNewHead;
}

猜你喜欢

转载自blog.csdn.net/virgofarm/article/details/80007357