C语言中的单链表面试题----基础

来源:互联网 发布:mac地址 ip地址区别 编辑:程序博客网 时间:2024/06/06 19:51

题目总览

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

思路及代码

从尾到头打印单链表

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

void ConversePrint(ListNode* pList){    if (pList != NULL)    {        ConversePrint(pList->next);        printf("%d ", pList->data);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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

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

void popNode(ListNode* pList){    if (pList != NULL)    {        ListNode* tmp = pList->next;        pList->data = pList->next->data;        pList->next = pList->next->next;        free(tmp);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在无头单链表的一个节点前插入一个节点

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

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;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

单链表实现约瑟夫环

  • 接收一个环形链表的头指针地址,将头指针改为剩下的最后一个节点。
  • 处理空链表。
  • 用一个节点指针指向头节点,当前节点的下一节点不是当前节点(节点数大于一),循环,当前节点为第一个节点,走 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;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

逆置/反转单链表

  • 头插法,建立新链表,用 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;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

单链表排序(冒泡排序)

  • 接收一个普通单链表,处理空链表和只有一个节点的情况。
  • 用 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;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

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

方法一 
新链表段接法 
- 接收两个链表指针,处理有空链表的情况。 
- 创建新链表头指针,指向两链表头节内值点较小的的节点。 
- 循环,节点小的指针后移,直到节点下一节点大于另一指针或节点为空,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;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

方法二 
新链单节点表尾插法 
- 新建 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;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

查找单链表的倒数第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;}
原创粉丝点击