zoukankan      html  css  js  c++  java
  • 面试笔试总结——链表

    收集一些关于链表的常见面试笔试题。

    链表结构:

    typedef struct ListNode
    {
        int val;
        ListNode* next;        
    }ListNode;

    1. 链表反转

    思路:将需要反转的结点的下一个结点暂存起来,然后将需要反转的结点与它指向的下一个结点交换指针位置,交换完毕以后,再将指针指向暂存的结点即可,这样相当于是改变了链表指针的指向,同时要注意到,返回值是原先链表的链表尾,可以通过判断下一结点是否为空,如果为空则说明到达链表末尾,此时就可以将结果返回,链表已经被反转成功。

    ListNode* reverseList(ListNode* pHead)
    {
        if (pHead == NULL || pHead->next == NULL) //链表为空或者仅1个数直接返回
            return pHead;
        ListNode* p = pHead, *res= NULL; //定义一个节点,保存最终结果
        ListNode* temp;
        while (p != NULL)                  //循环到链尾
        {
            temp = p->next;      //暂存p下一个地址,防止变化指针指向后找不到后续的数
            p->next = res;                 //p->next指向前一个空间
            res     = p;                       //新链表的头移动到p,扩长一步链表
            p       = temp;                  //p指向原始链表p指向的下一个空间
        }
        return res;
    }

    2. 从尾到头输出单链表

    思路:

    1)从头到尾遍历链表,每经过一个节点的时候,把该节点放到栈中。当遍历完整个链表后,再从栈顶开始输出结点的值,此时输出的节点的顺序已经反转过来了。该方法需要维护一个额外的栈空间;

    2)既然想到了栈来实现这个函数,而递归本质上就是一个栈结构,于是可以用递归来实现。要实现反过来输出链表,每访问一个节点的时候,先递归输出到它后面的节点,再输出该节点自身,这样即链表的输出结果就反过来了。

    思路1实现:

    void PrintListReverse(ListNode *pHead)
    {
        stack<ListNode*> s_node;
        ListNode* tmp = pHead;   
        while(temp != NULL)
        { 
             s_node.push(temp);
             temp = temp->next;  
        }
    
        while(!s_node.empty())
        {
             printf("%d",s_node.top()->val);
             s_node.pop();  
        }
    }

    思路2:

    void PrintListReverse(ListNode *pHead)
    {
        if(pHead != NULL)
        { 
             PrintListReverse(pHead->next);   //先递归输出到它后面的节点
        }
        printf("%d", pHead->val);        //输出后一个节点自身
    }

    此题还有两个常见的变体:
    (1)从尾到头输出一个字符串;

    (2)定义一个函数求字符串的长度,要求该函数体内不能声明任何变量;

    对于这两个变体,都可以参考思路2的实现方式。

    3. 合并两个有序链表

    思路:逐个比较两个链表的元素,较小的元素进入到新的链表中,并且指向它的下一个。递归法实现。

    ListNode* Merge(ListNode* n1, ListNode* n2)
    {
        if (n1 == NULL) {
            return n2;
        } else if (n2 == NULL) {
            return n1;
        }
    
        ListNode* res = NULL;
        if (n1->val < n2->val)
        {
            res = n1;
            res->next = Merge(n1->next, n2);
        } else {
            res = n2;
            res->next = Merge(n1, n2->next);
        }
        return res;
    }

    4. 判断单链表是否有环

    此题有很多种方法,这里介绍一种比较巧妙的方法,使用STL中的map实现。

    思路:首先定义 map<ListNode*,int> m; 将一个ListNode*指针映射成数组的下标,并赋值为一个int类型的数值。然后从链表的头指针开始往后遍历,每次遇到一个指针p,就判断m[p]是否为0。如果为0,则将m[p]赋值为1,表示该节点是第一次访问;而如果m[p]的值为1,则说明这个节点已经被访问过一次了,于是就形成了环。

    map<ListNode*, int> m;
    bool IsLoop(ListNode *phead)
    {
        if(pHead == NULL)
            return  false;
        ListNode* p = pHead;
        while(p != NULL)
        {
            if(m[p] == 0) 
                m[p] = 1;
            else if(m[p] == 1) 
                return true;
            p = p->next;
        }
    }    

    延伸:找到有环链表的环入口

    思路:

    //断链法:破坏链表结构
    /*
    时间复杂度为O(n),两个指针,一个在前面(front),另一个(pre)紧邻着这个指针,在后面。
    两个指针同时向前移动,每移动一次,令前面的指针的next指向NULL。
    也就是说:访问过的节点都断开,最后到达的那个节点一定是尾节点的下一个,
    也就是循环的第一个。
    这时候已经是第二次访问循环的第一节点了,第一次访问的时候我们已经让它指向了NULL,
    所以到这结束。
    */

    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {
        if(!pHead->next)
            return NULL;
        ListNode* pre = pHead;
        ListNode* front = pHead->next;
        while(front){
            pre->next = NULL;
            pre=front;
            front = front->next;
        }
        return pre;
    }        

    5. 找到链表的倒数第k个节点

    思路:创建两个指针,先让第一个指针和第二个指针都指向头结点,然后再让第一个指针走(k-1)步,到达第k个节点,然后两个指针同时往后移动,当第一个结点到达末尾的时候,第二个结点所在位置就是倒数第k个节点了

    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
      ListNode *pHead = pListHead;
      ListNode *pTail = pListHead;
      if(pListHead==NULL || k==0)
        return NULL;
      for(int i=1;i<k;++i){
        if(pTail->next != NULL)
        {
          pTail=pTail->next;
        }
        else 
          return NULL;
      }
    
      while(pTail->next!=NULL)
      {
        pTail=pTail->next;
        pHead = pHead->next;
      }
      return pHead;
    }

    6. 求两个链表的公共节点

    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
      ListNode* p1 = pHead1;
      ListNode* p2 = pHead2;
      while(p1!=p2){
        p1 = (p1==NULL ? pHead2 : p1->next);
        p2 = (p2==NULL ? pHead1 : p2->next);
      }
      return p1;
    }

    7. 删除有序链表中的重复结点

    在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

    采用递归法:

    ListNode* deleteDuplication(ListNode* pHead)
    {
        if(pHead==NULL) return NULL;
        if(pHead!=NULL && pHead->next==NULL) return pHead;
        
        ListNode* cur;
        if(pHead->next->val == pHead->val){
            cur=pHead->next->next;
            while(cur!=NULL && cur->val==pHead->val)
                cur=current->next;
            return deleteDuplication(cur);
        }
        else{
            cur = pHead->next;
            pHead->next = deleteDuplication(cur);
            return pHead;
        }
    }

    先总结到这里,后面再补充~

  • 相关阅读:
    Win32 开发
    Corners in C#
    swfupload在IE8下显示正常,但是单击添加按钮无反应
    Windows Script Host(WSH)
    研磨设计模式 之 中介者模式(Mediator)
    Pure GPU Computing Platform : NVIDIA CUDA Tutorial
    BattleField 2142引擎图形程序员小访谈
    利用SAH实现kD树快速分割模型实践
    给大家看一下德国的家居装潢技术,在装修房子的朋友可以欣赏一下
    Python与Microsoft Office自动化操作
  • 原文地址:https://www.cnblogs.com/bencai/p/9499788.html
Copyright © 2011-2022 走看看