c语言实现单链表面试题

来源:互联网 发布:淘宝新产品推广方案 编辑:程序博客网 时间:2024/06/04 19:58

首先实现一个无头单链表

#include <stdio.h>#include <assert.h>#include <stdlib.h>typedef int DataType;typedef struct ListNode{    DataType data;    struct ListNode* next;} ListNode;//打印void PrintList(ListNode* pList){    ListNode* Node = pList;    while (Node)    {        printf(" %d->", Node->data);        Node = Node->next;    }    printf("NULL");    printf("\n");}//尾插void PushBack(ListNode** ppList, DataType x){    if (*ppList == NULL)    {        ListNode* Node = (ListNode*)malloc(sizeof(ListNode));        Node->data = x;        Node->next = NULL;        *ppList = Node;    }    //只有一个节点    else if ((*ppList)->next == NULL)    {        (*ppList)->next = (ListNode*)malloc(sizeof(ListNode));        (*ppList)->next->data = x;        (*ppList)->next->next = NULL;    }    //多个节点    else    {        ListNode* Node = *ppList;        while (Node->next)        {            Node = Node->next;        }        Node->next = (ListNode*)malloc(sizeof(ListNode));        Node->next->data = x;        Node->next->next = NULL;    }}//尾删void PopBack(ListNode** ppList){    if (*ppList == NULL)        return;    //只有一个节点,直接释放    else if ((*ppList)->next == NULL)    {        free(*ppList);        *ppList = NULL;    }    else    {        ListNode* Node = *ppList;        while (Node->next)        {            Node = Node->next;        }        free(Node);        Node = NULL;    }}//头插void PushFront(ListNode** ppList, DataType x){    if (*ppList == NULL)    {        ListNode* Node = (ListNode*)malloc(sizeof(ListNode));        Node->data = x;        *ppList = Node;        (*ppList)->next = NULL;    }    else     {        ListNode* Node = (ListNode*)malloc(sizeof(ListNode));        Node->data = x;        Node->next = *ppList;        *ppList = Node;    }}//头删void PopFront(ListNode** ppList){    if (*ppList == NULL)        return;    else if ((*ppList)->next == NULL)    {        free(*ppList);        *ppList = NULL;    }    else    {        //保存指针的指向        ListNode* tmp = (*ppList)->next;        free(*ppList);        *ppList = tmp;    }}//查找ListNode* Find(ListNode* pList, DataType x){    assert(pList);    while (pList)    {        if (pList->data == x)        {            return pList;        }        pList = pList->next;    }    return NULL;}//在pos的前面插入一个节点void Insert(ListNode** ppList, ListNode* pos, DataType  x){    assert(*ppList);    assert(pos);    if ((*ppList)->next == NULL || pos == *ppList)    {        PushFront(ppList, x);    }    else    {        ListNode* tmp = NULL;        ListNode* head = *ppList;        while (head->next != pos)        {            head = head->next;        }        tmp =(ListNode*) malloc(sizeof(ListNode));        tmp->data = x;        head->next = tmp;        tmp->next = pos;    }}//删除一个节点void Erase(ListNode** ppList, ListNode* pos){    assert(*ppList && pos);    if ((*ppList)->next == NULL || pos == *ppList)    {        PopFront(ppList);    }    else    {        ListNode* tmp = *ppList;        while (tmp->next != pos)        {            tmp = tmp->next;        }        tmp->next = pos->next;        free(pos);        pos = NULL;    }}

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

空间上的比较(Space)

  1. 空间的开辟:
    顺序表的实现一般是实现连续开辟一段空间(或根据需求动态连续开辟空间),然后在进行数据的增删查改(静态顺序表),所以顺序表一般是固定空间大小的;

单链表则是一次只开辟一个结点的空间,用来存储当前要保存的数据及指向下一个结点或NULL的指针,所以单链表的空间大小是动态变化的。

  1. 空间的使用:
    当我们不知道要存储多少数据时,用顺序表来开辟的空间如果太大,就会造成一定程度上的浪费。

用单链表实现时,因为是每需要存储一个数据时,才开辟一个空间,虽然有非数据项的指针占空间,但相比顺序表来说,浪费不是那么明显。(因为链表每次都是开辟的位置都是随机的,那么可能会把这块空间搞得七零八碎,出现很多小的一般使用不到的碎片空间)

  1. 对CPU高速缓存的影响:
    顺序表的空间一般是连续开辟的,而且一次会开辟存储多个元素的空间,所以在使用顺序表时,可以一次把多个数据写入高速缓存,再写入主存,顺序表的CPU高速缓存效率更高,且CPU流水线也不会总是被打断;

单链表是每需要存储一个数据才开辟一次空间,所以每个数据存储时都要单独的写入高速缓存区,再写入主存,这样就造成了,单链表CPU高速缓存效率低,且CPU流水线会经常被打断。

时间上的比较(Time)

  1. 查找随机元素的时间复杂度:
    顺序表访问随机元素的时间复杂度是O(1),而单链表访问随机元素的时间复杂度是O(n)。

  2. 随机位置插入、删除元素的时间复杂度:
    顺序表在插入随机位置插入、删除元素的平均时间复杂度是O(n),
    单链表在插入随机位置插入、删除元素的时间复杂度是O(1)。

应用场景:
根据时间复杂度比较:
在查询操作使用的比较频繁时,使用顺序表会好一些;
在插入、删除操作使用的比较频繁时,使用单链表会好一些。

2.从尾到头打印单链表

void PrintHeadtoTail(ListNode* pList){    if (pList == NULL)    {        printf("NULL\n");        return;    }    while (pList)    {        printf(" %d->", pList->data);        pList = pList->next;    }    printf("NULL\n");}//递归方式void PrintHeadtoTail(ListNode* pList){    if (pList == NULL)    {        printf("NULL");        return;    }    printf("%d->", pList->data);    PrintHeadtoTail(pList->next);}

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

无头单链表无法找到pos的前一个节点,只能把pos后一个节点的内容赋值给pos然后删掉pos后一个节点

void  EraseNoTail(ListNode* pos){    if ((pos == NULL) && (pos->next == NULL))    {        return;    }    else    {        ListNode *next = pos->next;        pos->next = next->next;        pos->data = next->data;        free(next);        next = NULL;    }}

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

先申请一个节点插入到pos后面,然后再与pos的内容相交换

void FrontInsert(ListNode* pos,DataType x){    assert(pos);    ListNode *tmp = (ListNode*)malloc(sizeof(ListNode));    tmp->next = pos->next;    pos->next = tmp;    tmp->data = pos->data;    pos->data = x;}

5.单链表实现约瑟夫环

ListNode*  JosephRing(ListNode *pList, DataType k){    assert(pList);    int count = 0;    ListNode* tmp = NULL;    while (pList != pList->next)    {        count = k;        while (--count)        {            pList = pList->next;        }        pList->data = pList->next->data;        tmp = pList->next;        pList->next = pList->next->next;        free(tmp);        tmp = NULL;    }    return pList;}

6.逆置/反转单链表

ListNode* Reverse(ListNode* plist)//逆置反转单链表{    ListNode* newhead = NULL;    ListNode* pos = list;    ListNode* cur = pos;    while (cur)    {        pos = cur;        cur = cur->next;        pos->next = newhead;        newhead = pos;    }    return newhead;}

7.单链表排序(冒泡排序&快速排序)

ListNode *Bubblesort(ListNode *plist)//单链表排序(冒泡排序&快速排序){    if ((plist == NULL) || (plist->next == NULL))    {    }    else    {        ListNode *cur = NULL;        ListNode *next = NULL;        ListNode *tail = NULL;        while (tail != plist->next)        {            cur = plist;            next = plist->next;            while (next != tail)            {                if (cur->data > next->data)                {                    DataType tmp = next->data;                    next->data = cur->data;                    cur->data = tmp;                }                cur = next;                next = next->next;            }            tail = cur;        }        cur = next = tail = NULL;    }    return plist;}

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

ListNode *Merge(ListNode *plist1,ListNode *plist2){    ListNode *tail = NULL;    ListNode *tmp = NULL;    ListNode *plist = NULL;    if (plist1 == NULL)    {        return plist2;    }    if (plist2 == NULL)    {        return plist1;    }    while (plist1 && plist2)    {        if (plist1->data < plist2->data)        {            tmp = plist1;            plist1 = plist1->next;        }        else        {            tmp = plist2;            plist2 = plist2->next;        }        if (plist == NULL)        {            plist = tmp;            tail =ist;        }        else        {            tail->next = tmp;            tail = tmp;        }    }    if (plist1 == NULL)    {        tail->next = plist2;    }    else    {        tail->next = plist1;    }    return plist;    }

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

ListNode *FindMidNode(ListNode *plist){    assert(plist);    ListNode *fast = plist;    ListNode *slow = plist;    while (fast && fast->next)    {        fast = fast->next->next;        slow = slow->next;    }    return slow;}

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

ListNode *FindTailKNode(ListNode *plist,int k){    ListNode *fast = plist;    ListNode *slow = plist;    assert(plist);    while (--k)    {        fast = fast->next;        assert(fast);    }    while (fast->next != NULL)    {        fast = fast->next;        slow = slow->next;    }    return slow;}
原创粉丝点击