zoukankan      html  css  js  c++  java
  • 单链表热点面试题

    单链表热点面试题

    单链表结构  (注:有些题需要用到单链表基本实现中的函数,本文不再重新实现)
    typedef int DataType;

    typedef struct ListNode
    {
    DataType _data; //数据
    struct ListNode * _next; //指向下一个节点的指针
    }ListNode;

    1.比较顺序表和单链表的优缺点
    1)顺序表支持随机访问,单链表不支持。
    2)顺序表插入/删除数据效率很低,时间复杂度为O(N)(除尾插、尾删)
    单链表插入/删除数据效率高,时间复杂度为O(1)。
    3)顺序表的CPU高速缓存效率更高,单链表的CPU高速缓存效率更低。

    2.从尾到头打印单链表
    void PrintTailToHead(ListNode * pHead)
    {
    if (pHead)
    {
    PrintTailToHead(pHead->_next);
    printf("%d <- ", pHead->_data);
    }
    }

     

    3.删除一个无头单链表的非尾节点
    void DelNonTailNode(ListNode * pos)
    {
    assert(pos && pos->_next);
    pos->_data = pos->_next->_data;
    ListNode * next = pos->_next->_next;

    free(pos->_next);
    pos->_next = next;
    }
    解析:由于没有头指针,无法遍历单链表删除节点,所以可以换一种思想。将pos的下一个节点的数据赋给pos节点,令pos指向下一个的下一个,这样就可以转换为删除pos的下一个节点。




    4.在无头单链表的一个非头结点前插入一个节点
    void InsertFrontNode(ListNode * pos, DataType x)
    {
    assert(pos);
    ListNode * tmp = BuyNode(x);
    tmp->_next = pos->_next;
    pos->_next = tmp;
    DataType tmpData = tmp->_data;
    tmp->_data = pos->_data;

    	pos->_data = tmpData;
    }

    解析:与第三题类似,可以在pos节点后插入一个新节点,将pos与新节点的数据值交换一下,就实现了在pos前插入节点了。






    5.单链表实现约瑟夫环
    void JosephCycle(ListNode * &pHead, int m)
    {
    ListNode* cur = pHead;
    if (cur == NULL)
    {
    return ;
    }
    while (1)
    {
    if (cur == cur->_next) //只剩下一个节点
    {
    return ;
    }
    int x = m;
    while (--x)
    {
    cur = cur->_next;
    //替换法进行删除
    cur->_data = cur->_next->_data;
    ListNode* del = cur->_next;
    cur->_next = del->_next;
    free(del);
    }
    }
    }


    6.逆置/反转单链表
    void Reverse(ListNode * &pHead)
    {
    ListNode * cur = pHead;
    ListNode * newHead = NULL;
    while (cur)
    {
    ListNode * tmp = cur;
    cur = cur->_next;
    tmp->_next = newHead;
    newHead = tmp;
    }
    pHead = newHead;


    }

    解析:cur指针用来遍历单链表,同时用tmp指针指向cur节点,将cur向后指,此时可类似于摘下tmp节点,令tmp的下一个节点是newHead,以此类推,最后,newHead即为逆置后的单链表。



    7.单链表排序(冒泡)
    void SortList(ListNode *pHead)
    {
    if (pHead == NULL || pHead->_next == NULL) //为空或有一个节点
    {
    return;
    }
    	ListNode * cur = pHead->_next;
    ListNode * prev = pHead;
    ListNode * tail = NULL;
    	while (tail != pHead)
    {
    prev = pHead;
    cur = pHead->_next;
    int exchange = 0;
    while (cur != tail)
    {
    if (prev->_data > cur->_data) //前面的数据大于后面的数据,交换值
    {
    DataType tmp = prev->_data;
    prev->_data = cur->_data;
    cur->_data = tmp;
    exchange = 1;
    }
    prev = cur;
    cur = cur->_next;
    }
    if (exchange == 0)
    {
    return;
    }
    tail = prev;
    }
    }

    8.合并两个有序链表,合并后依然有序
    ListNode * MergeList(ListNode * pHead1, ListNode * pHead2)
    {
    if (pHead1 == NULL)
    {
    return pHead2;
    }
    if (pHead2 == NULL)
    {
    return pHead1;
    }
    ListNode* newHead;
    ListNode* tail;
    ListNode* cur1 = pHead1;
    ListNode* cur2 = pHead2;
    	if (cur1->_data < cur2->_data)
    {
    newHead = cur1;
    cur1 = cur1->_next;
    }
    else
    {
    newHead = cur2;
    cur2 = cur2->_next;
    }
    	tail = newHead;
    while (cur1 && cur2)
    {
    if (cur1->_data < cur2->_data)
    {
    tail->_next = cur1;
    cur1 = cur1->_next;
    }
    else
    {
    tail->_next = cur2;
    cur1 = cur2->_next;
    }
    tail = tail->_next;
    }
    if (cur1)
    {
    tail->_next = cur1;
    }
    if (cur2)
    {
    tail->_next = cur2;
    }
         return newHead;

    }
    解析:由于两个单链表都已经有序,只需要先将两个链表的第一个数据比较大小,加入新链表,接下来将两个链表中的数据依次比较大小,依次放入新链表即可。

    9.查找单链表的中间节点,要求只遍历一次
    ListNode * FindMid(ListNode *pHead)
    {
    ListNode * slow = pHead, *fast = pHead;
    while (fast && fast->_next)
    {
    slow = slow->_next;
    fast = fast->_next->_next;
    }
    return slow;
    }



    解析:查找中间节点,可以利用快慢指针的思想,快指针走两步,慢指针走一步,这样,当快指针走到尾节点时,慢指针刚好在链表的中间。

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

    ListNode * FindKTailNode(ListNode * pHead, int k)
    {
    ListNode * slow = pHead, *fast = pHead;
    while (k--)
    {
    if (fast == NULL) //没有k这么长,返回空
    {
    return NULL;
    }
    fast = fast->_next; //fast先走k步
    }
    while (fast)
    {
    slow = slow->_next;
    fast = fast->_next;
    }
    return slow;
    }

    解析:依然利用快慢指针,先令fast走k步,在令slow与fast同时向后走,则当fast走到尾时,slow为倒数第二个节点。

    11.判断单链表是否带环,求长度、入口点

    解析:要判断单链表是否带环,利用快慢指针,快指针走两步,慢指针走一步,若最后会相遇,则带环,否则不带环。
    bool HasCycle(ListNode *pHead)
    {
    ListNode * slow = pHead, *fast = pHead;
    while (fast && fast->_next)
    {
    fast = fast->_next->_next;
    slow = slow->_next;
    if (slow == fast)
    {
    return true;
    }
    }
    return false;
    }
    ListNode * MeetNode(ListNode *pHead)    //找到快慢指针的相遇点
    {
    ListNode * slow = pHead, *fast = pHead;
    while (fast && fast->_next)
    {
    fast = fast->_next->_next;
    slow = slow->_next;
    if (slow == fast)
    return slow;
    }
    return NULL;
    }
    int GetCycleLength(ListNode * meetNode)    //求环的长度
    {
    ListNode * slow = meetNode->_next, *fast = meetNode->_next->_next;
    int count = 1;
    while (slow != fast)
    {
    slow = slow->_next;
    fast = fast->_next->_next;
    count++;
    }
    return count;
    }
    ListNode * GetEntryNode(ListNode *pHead, ListNode * meetNode)    //求入口点
    {
    while (pHead && meetNode)
    {
    if (pHead == meetNode)
    {
    return pHead;
    }
    pHead = pHead->_next;
    meetNode = meetNode->_next;
    }
    return NULL;
    }

    12.判断两个链表是否相交,求交点(假设链表不带环)
    解析:两个链表,如果他们相交的话,那么他们最后的一个节点一定是相同的,否则是不相交的。因此判断两个链表是否相交就很简单了,分别遍历到两个链表的尾部,然后判断他们是否相同,如果相同,则相交;否则不相交。

    bool CheckCross(ListNode * l1, ListNode * l2)
    {
    ListNode * tail1 = l1, *tail2 = l2;
    while (tail1->_next != NULL)
    {
    tail1 = tail1->_next;
    }
    while (tail2->_next != NULL)
    {
    tail2 = tail2->_next;
    }
    if (tail1 == tail2)
    {
    return true;
    }
    return false;
    }
    ListNode *CrossNode(ListNode * l1, ListNode * l2) //求交点
    {
    ListNode * cur1 = l1, *cur2 = l2;
    ListNode * tail1 = l1, *tail2 = l2;
    int len1 = 0, len2 = 0;
    while (tail1->_next != NULL) //分别求出单链表长度
    {
    len1++;
    		tail1 = tail1->_next;
    }
    while (tail2->_next != NULL)
    {
    len2++;
    tail2 = tail2->_next;
    }
    int x;
    if (len1 > len2) //若l1长,先走x步
    {
    x = len1 - len2;
    while (x--)
    {
    cur1 = cur1->_next;
    }
    }
    else //若l2长,先走x步
    {
    x = len2 - len1;
    while (x--)
    {
    cur2 = cur2->_next;
    }
    }
    while (cur1 != cur2)
    {
    cur1 = cur1->_next;
    cur2 = cur2->_next;
    }
    return cur1;
    }


    13.判断两个链表是否相交,求交点(假设链表带环)
    bool CheckCross1(ListNode * l1, ListNode * l2)
    {
    ListNode * meetNode1 = MeetNode(l1);
    ListNode * meetNode2 = MeetNode(l2);
    if (meetNode1 == NULL && meetNode2 == NULL) //不带环
    {
    ListNode * tail1 = l1, *tail2 = l2;
    while (tail1->_next != NULL)
    {
    tail1 = tail1->_next;
    }
    while (tail2->_next != NULL)
    {
    tail2 = tail2->_next;
    }
    if (tail1 == tail2)
    return true;
    else
    return false;
    }
    if (meetNode1 && meetNode2) //带环
    {
              if(meetNode1 == meetNode2)
              {
                return true;
              }

    ListNode * cur = meetNode1->_next;
    while (cur != meetNode1)
    {
    if (cur == meetNode2)
    return true;
    cur = cur->_next;
    }
    return false;
    }
         return false;

    }

    ListNode * CrossNode1(ListNode * l1, ListNode * l2) //求交点
    {
    ListNode * meetNode1 = MeetNode(l1);
    ListNode * meetNode2 = MeetNode(l2);
    ListNode * entryNode1 = GetEntryNode(l1, meetNode1);
    ListNode * entryNode2 = GetEntryNode(l2, meetNode2);
    if (entryNode1 == entryNode2) //入口点一样,说明在入环之前就相交
    {
    entryNode1 = NULL;
    ListNode * crossNode = CrossNode(l1, l2);
    return crossNode;
    }
    else //入口点不同,说明在环内相交(应有两个交点,现在假设l2交l1)
    {
    return entryNode2;
    }
    }
     
    14.求两个已排序链表中相同的数据
    void UnionSet(ListNode* l1, ListNode* l2)
    {
    while (l1 && l2)
    {
    if (l1->_data == l2->_data)
    {
    printf("%d ", l1->_data);
    l1 = l1->_next;
    l2 = l2->_next;
    }
    else if (l1->_data < l2->_data)
    {
    l1 = l1->_next;
    }
    else
    {
    l2 = l2->_next;
    }
    }
    }
    
    
  • 相关阅读:
    GridView Footer页脚统计实现多行
    Windows cmd 启动 tomcat 中文乱码问题
    git
    CentOS 的 dnf 命令
    不知道是否是wcf 的一个bug
    图像卷积与滤波的一些知识点
    Phaser开源2d引擎 javascript/html5游戏框架
    关于Ldoc
    自写vim插件ldoc.vim,提供智能的lua注释代码补全
    svn diff 使用 vimdiff 作为比较差异工具
  • 原文地址:https://www.cnblogs.com/guochuanrui/p/5412263.html
Copyright © 2011-2022 走看看