链表面试题

基于上一次写的链表,现在,我们来讨论下面这些问题。

1.链表的冒泡排序 
2.删除无头非尾节点 
3.反转链表 
4.在当前节点前插入一个数据x 
5.查找链表的中间节点。 
6.删除单链表的倒数第K个节点(K>1&&K<总长度)

对于上面这6个问题,我们进行分析与解答。

链表的代码我都已经写过博客:数据结构—单链表的实现

另外,我也在我的github上有链表的代码,github链接

如还有什么问题,可以发邮件到 [email protected]

1.链表的冒泡排序

关于链表的排序问题是我们需要关心的,链表的排序,排序时比较方便的就是交换链表所保存的内容,所以接下来我们来提供一种思路来解决这个问题。

在这里我们的思路是先有两个指针,一个头指针,一个尾指针,第一次从头进行冒泡到尾,第二次从头冒泡到最后一个节点的前一个节点,就这样一次进行下去,一直到冒泡到第一个节点和第二个节点。 
这里写图片描述

讲到这,我想大家应该明白了怎么实现了吧。 
下面附上代码实现过程。

void BubbleSort(pLinkList pList)
{

    pLinkNode cur = NULL;
    pLinkNode tail = NULL;
    assert(pList);
    cur = pList->pHead;
    if ((pList->pHead == NULL)||(pList->pHead->next==NULL))
    {
        return;
    }
    while (cur != tail)     //当尾指针不等于头指针时进行冒泡
    {
        while (cur->next != tail)   //控制cur指针最后到的位置是倒数第二个节点
        {

            if (cur->data > cur->next->data)
            {
                datatype tmp = cur->data;
                cur->data = cur->next->data;
                cur->next->data = tmp;
            }
            cur = cur->next;
        }
        tail = cur;
        cur = pList->pHead;
    }
}

2.删除无头非尾节点

删除无头非尾的节点,我想乍一看大家可能会觉得没有什么思路,按照惯性思维,咱们删除一个节点的时候,应该对这个节点的前一个节点记住,然后让前一个系欸但指向删除节点的下一个节点,但是在这里我们说了已经是无头的节点。所以我们需要转换我们的思路,进行一种新的操作。

其实思路也挺简单的,就是我们要把删除的节点后的节点的数据放到要删除的节点上,删除了删除的节点后的节点,然后让删除节点指向删除删除的节点后的节点后的节点。 
是不是感觉头都大了? 
接下来,我用图解为大家分析下。 
这里写图片描述
代码实现如下:

void EraseNotTail(pLinkNode pos)
{
    pLinkNode del = NULL;
    assert(pos->next != NULL);      //断言,排除尾节点的情况

    pos->data = pos->next->data;    //让当前节点后面节点的数据赋给当前节点。
    del = pos->next;                //删除记录当前节点后的节点
    pos->next = pos->next->next;    //当前节点的nex指向删除节点的下一个节点。
    free(del);
    del = NULL;
}

3.反转链表

反转链表这个算法的实现也需要有一个清晰的思路,闲话就不多说了,咱们进入正题。对于反转链表,其实就是你把链表重新排下序。 
这里写图片描述

void Reverse(pLinkList plist)
{
    pLinkNode tmp = NULL;       //记录中间节点
    pLinkNode cur = NULL;       //记录链表的第一个节点
    pLinkNode newnode = NULL;   //记录翻转后的第一个节点
    assert(plist);
    cur = plist->pHead;
    while (cur != NULL)
    {
        tmp = cur;                  //  
        cur = cur->next;
        tmp->next = newnode;
        newnode = tmp;
    }
    plist->pHead = newnode;
}

4.在当前节点前插入一个数据x

在当前节点之前插入一个数据,按照常规思路坑定也是不行的,这时候我们就要像删除无头节点一样有一个思维:

我们可以把插入节点的数据放到新节点上,把新节点的数据放到插入节点的数据上。 
这样我们就可以实现在当前节点前插入一个节点了。

这里写图片描述

void InsertFrontNode(pLinkNode pos, datatype x)
{

    pLinkNode newnode = NULL;
    assert(pos);
    newnode = (pLinkNode)malloc(sizeof(LinkNode));
    if (NULL == newnode)
    {
        printf("out of memory");
        exit(EXIT_FAILURE);
    }
    newnode->data = pos->data;
    pos->data = x;
    newnode->next = pos->next;
    pos->next = newnode;

}

5.查找链表的中间节点。

查找链表的中间节点,所采用的思路就是我们平时所说的快慢指针。

给一个快指针,让快指针每次移动两步,给一个慢指针,让慢指针每次移动一步,最后结果就是快指针移动到最后一个节点,慢指针最后移动到了中间的节点上。 
这里写图片描述

pLinkNode FindMidNode(pLinkList pList)
{
    pLinkNode fast = NULL;
    pLinkNode slow = NULL;
    assert(pList);
    slow = fast = pList->pHead;
    while (fast != NULL)
    {
        if (fast->next != NULL)
        {
            fast = fast->next->next;
        }
        else
        {
            break;;
        }
        slow = slow->next;
    }
    return slow;
}

6.删除单链表的倒数第K个节点(K>1&&K<总长度)

在这里,对于删除单链表的倒数第K个节点,我们采用的思路是首先给个front指针和back指针,让front指针移动K次,然后让front指针和back指针同时移动,一直到front指针指向为NULL,此时back指针指向的就是倒数第K个节点,然后我们就可以删除back节点,注意,这里所采用的删除的方法要和上面类似,采用交换删除的方式。

当然,在这里面请你考虑一些异常的情况! 
这里写图片描述

void DelKNode(pLinkList pList, int k)
{
    pLinkNode front = NULL;
    pLinkNode back = NULL;
    pLinkNode del = NULL;

    assert(pList);
    if (pList->pHead == NULL)
    {
        printf("链表为空!!\n");
        return;
    }
    if (k == 1)
    {
        printf("K应当大于1\n");
        return;
    }
    front = back = pList->pHead;
    while (front != NULL)
    {
        k--;
        if (k < 0)
        {
            back = back->next;
        }
        front = front->next;
    }
    if (k <= 0)
    {
        back->data = back->next->data;
        del = back->next;
        back->next = back->next->next;
        free(del);
    }

}

猜你喜欢

转载自blog.csdn.net/ds1130071727/article/details/80401554