C语言单链表面试题1

来源:互联网 发布:silverlight调用js 编辑:程序博客网 时间:2024/05/17 07:56
题目总览

从尾到头打印单链表
删除一个无头单链表的非尾节点
在无头单链表的一个节点前插入一个节点
单链表实现约瑟夫环
逆置/反转单链表
单链表排序(冒泡排序&快速排序)
合并两个有序链表,合并后依然有序
查找单链表的中间节点,要求只能遍历一次链表
查找单链表的倒数第n个节点,要求只能遍历一次链表
思路及代码

从尾到头打印单链表

递归法,若当前节点不为空,在打印当前节点前打印上一个节点。

void ConversePrint(ListNode* pList)
{
    if (pList != NULL)
    {
        ConversePrint(pList->next);
        printf("%d ", pList->data);
    }
}

删除一个无头单链表的非尾节点

将当前节点的内容改为下个节点的内容,释放下个节点,让当前节点指向下下个节点。

void popNode(ListNode* pList)
{
    if (pList != NULL)
    {
        ListNode* tmp = pList->next;
        pList->data = pList->next->data;
        pList->next = pList->next->next;
        free(tmp);
    }
}
在无头单链表的一个节点前插入一个节点

新建一个节点保存当前节点的内容,并指向下一个节点。 
将当前节点改为插入的内容,指向新节点。

void InsertBefore(ListNode* pList, DataType x)
{
    assert(pList);
    ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
    newNode->data = pList->data;
    newNode->next = pList->next;
    pList->next = newNode;
    pList->data = x;
}

单链表实现约瑟夫环

接收一个环形链表的头指针地址,将头指针改为剩下的最后一个节点。
处理空链表。
用一个节点指针指向头节点,当前节点的下一节点不是当前节点(节点数大于一),循环,当前节点为第一个节点,走 n-1 步,指向第n个节点,删除当前节点。
将初始头指针赋值最后节点。
void Joseph(ListNode** ppList, int n)
{
    assert(ppList);
    if (*ppList == NULL)return;
    ListNode* pList = *ppList;
    while (pList->next != pList)
    {
        int i;
        for (i = 1; i < n; i++)
            pList = pList->next;
        popNode(pList);
    }
    *ppList = pList;
}

逆置/反转单链表

头插法,建立新链表,用 cur 指向旧链表头结点。
循环,用 tmp 保存要拿下来的原链表头结点 cur, cur 指向下一节点,将 tmp 放到新链表的头部(tmp的下一节点指向新链表头结点,再将新链表的头结点改为 tmp),直到 cur 为 NULL。
将原链表头指针改为新链表。
void ReverseList(ListNode** ppList)
{
    assert(ppList);

    ListNode* newList = NULL;
    ListNode* cur = *ppList;
    while (cur)
    {
        ListNode* tmp = cur;
        cur = cur->next;
        tmp->next = newList;
        newList = tmp;
    }
    *ppList = newList;
}

单链表排序(冒泡排序)

接收一个普通单链表,处理空链表和只有一个节点的情况。
用 tail 指示一次冒泡的终点,第一次要全排,赋值为NULL。
循环冒泡比较,每次比较(交换)后,cur 和 next 都指向它们的下一个节点,直到 next == tail。交换中,修改交换标记 exchange。
外层循环,用 cur 表示比较的第一个节点,用 next 表示比较的下一个节点,exchange 记录是否交换,一趟比较没有交换,说明已排好,直接返回。每趟比较之后,此次最后一个值已经为最大,tail 前移一个节点。
void ListBubbleSort(ListNode* pList)
{
    if (pList == NULL || pList->next == NULL)return;

    ListNode* tail = NULL;
    while (tail != pList)
    {
        ListNode* cur = pList;
        ListNode* next = pList->next;
        int exchange = 0;
        while (next != tail)
        {
            if (cur->data > next->data)
            {
                DataType num = cur->data;
                cur->data = next->data;
                next->data = num;
                exchange = 1;
            }
            cur = next;
            next = next->next;
        }
        if (exchange == 0)
            return;
        tail = cur;
    }
}

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

方法一 
- 新链表段接法 
- 接收两个链表指针,处理有空链表的情况。 
- 创建新链表头指针,指向两链表头节内值点较小的的节点。 
- 循环,节点小的指针后移,直到节点下一节点大于另一指针或节点为空,tmp 保存下一节点,当前节点指向另一指针,当前指针赋值为 tmp,如果当前指针为 NULL,说明合并完成,结束循环。 
- 返回新链表头指针。 
- 可以总是让 p1 指向小的,p2 指向大的,循环挪 p1,接完后再把小的给 p1,大的给 p2。可以不用比较。(原方法 else 是复制 if 后,1 和 2 换,其实更简单,看起来多) 
思路图 
这里写图片描述

ListNode* MergeList(ListNode* List1, ListNode* List2)
{
    if (List1 == NULL)return List2;
    if (List2 == NULL)return List1;

    ListNode* newList = List1->data <= List2->data ? List1 : List2;
    while (true)
    {
        if(List1->data <= List2->data)
        {
            while (List1->next != NULL && List1->next->data <= List2->data)
            {
                List1 = List1->next;
            }
            ListNode* tmp = List1->next;
            List1->next = List2;
            List1 = tmp;
            if (List1 == NULL)
                break;
        }
        else
        {
            while (List2->next != NULL && List2->next->data <= List1->data)
            {
                List2 = List2->next;
            }
            ListNode* tmp = List2->next;
            List2->next = List1;
            List2 = tmp;
            if (List2 == NULL)
                break;
        }
    }
    return newList;
}

方法二 
- 新链单节点表尾插法 
- 新建 newList = NULL, 循环,每次比较两指针内容,保存小的节点,小节点指针后移,小节点尾接到 newList。 
- 如果有一个指针为 NULL,另一个指针尾接到 newList,返回 newList。 
方法三 
- 介于上两个方法之间,以头结点小的为 newList,另一个链表每次拿下一个节点接到 newList 的合适位置,直到其中一个指针为空。

查找单链表的中间节点,要求只能遍历一次链表

快慢指针法,初始快指针、慢指针都是头指针。
循环,如果快指针能走两步(快指针的下一个节点和下下一个节点不为空),快指针走两步,慢指针走一步。
返回慢指针。
ListNode* FindMidNode(ListNode* pList)//mid = total>>1
{
    ListNode* fast = pList;
    while (fast->next != NULL && fast->next->next != NULL)
    {
        fast = fast->next->next;
        pList = pList->next;
    }
    return pList;
}

查找单链表的倒数第n个节点,要求只能遍历一次链表

当节点数少于n时,NULL。
快慢指针法,创建返回指针 ret ,快指针先走 n-1 步(此时 ret 为快指针的倒数第 n 个节点)或到 NULL 为止。
循环,快指针的下一个节点不为空,快指针和 ret 都向后走一步。
返回返回指针。
ListNode* FindFromLast(ListNode* pList, int n)
{
    ListNode* ret = pList;
    while (--n)
    {
        if(ret == NULL)return NULL;
        pList = pList->next;
    }
    while (pList->next != NULL)
    {
        ret = ret->next;
        pList = pList->next;
    }
    return ret;
}
原创粉丝点击