zoukankan      html  css  js  c++  java
  • 面试收集关于链表的一些面试题

    链表相关的面试题是经常出现的,今天总结一下~

    1.如何判断一个链表是否有环?如果有,找到环的入口?

    设置快慢指针,快指针步长为2,慢指针步长为1,如果有环,最终快慢指针会相遇,代码如下:

    bool hasCircle(Node* head, Node* &encounter)
    
    {
    
             Node *fast = head, *slow = head;
    
             while(fast && fast->next)
    
            {
    
                   fast = fast->next->next;
    
                  slow = slow->next;
    
                  if(fast == low)
    
                   {
    
                       encounter = fast;
    
                       return true;
    
                   }
    
              }
    
             // fast == NULL || fast->next == NULL
    
             encounter = NULL;
    
             return false;
    
    }

    至于如何确定环入口,请看下图

    image

    设入口点距链表头部head有x步,由于必然相遇,设相遇点距入口点有y步,环长度为r(表示环中有r+1个节点),那么有判断相遇的算法可知

    (x+y)*2=nr+(x+y)   => nr=x+y  =>(n-1)r+r-y=x 

    由上面的分析可知,设置两个指针,一个初始为head,另一个初始为encounter,同步前进,一旦相遇就是入口,于是有了下面的代码:

    Node* findEntry(Node* head, Node* encounter)
    
    { 
    
              Node *p1 = head, *p2 = encounter;
    
             while(p1 != p2)
    
             {
    
                    p1 = p1->next;
    
                   p2 = p2->next;
    
              }
    
             return p1;
    
    }

    来源:http://hi.baidu.com/iwitggwg/item/7ad684119a27fefc9c778a5c

    2. 假设有两个单链表,给出头指针 head1 和 head 2,判断两个链表是否有交点?

    先判断是否有环,不过题目一般会给定无环条件。如果没有给出,可以判断,判断算法如下:

    如果无环,遍历两个链表,得到两个链表的长度m和n,先遍历较长链表|m-n|次,接着同步遍历两个链表,一旦相同,则找到相同节点,否则返回null。

    3. 只给定单链表中某个结点p(并非最后一个结点,即p->next!=NULL)指针,删除该结点。

    这个题目有个trick,正常的思路是利用p前面一个节点才能删掉p,但是现在只有p的地址,得不到p前面的地址,那么只能删除p后面的节点,删除之前把p->next的内容复制到p中,注意这个算法删除不了最后一个元素。

    4.只给定单链表中某个结点p,在p前面插入一个结点?

    思路同3,先在p后面插入一个节点,然后将p的数据复制到p->next中,在将插入的结点的数据复制到p中

    5.给定单链表的头结点head,删除链表中倒数第k个结点

    同样是快慢指针问题,设置快指针p_fast,慢指针p_slow,初始均指向head,快指针先走k步,走完k步之后快慢指针同步走,每次走一步,直至快指针走到队尾。这里没有考虑有环的状况,有环这个题目就无法解了, 如果严谨的话要先判断是否有环。下面是一个不太严谨的算法。

    Node *(Node *head,int k)
    
    {
    
    	assert(head!=NULL);
    
    	Node *p_fast=head;
    
    	Node *p_slow=head;
    
    	int count=k;
    
    	while(count>0&&p_fast!=NULL)
    
    	{
    
    		p_fast=p_fast->next;
    
    		count--;
    
    	}
    
    	if(count>0)return NULL;
    
    	while(p_fast!=NULL)
    
    	{
    
    		p_fast=p_fast->next;
    
    		p_slow=p_slow->next;
    
    	}
    
    	return p_slow;
    
    }

    6.找出链表的中间元素(无环)

    同样是快慢指针的问题,快指针慢指针初始都指向head,快指针每次行进两步,慢指针一步,一旦快指针指向链表尾部,慢指针指向的就是中间元素。实际写代码要考虑一些奇偶情况。

    7.链表的就地逆置

    头插法,但是要注意一些特殊情况,代码如下:

        Node* reverseList(Node* head)
    
      {
    
         Node *p1, *p2 , *p3;
    
      //链表为空,或是单结点链表直接返回头结点
    
         if (head == NULL || head->next == NULL)
    
        {
    
          return head;
    
        }
    
        p1 = head;
    
        p2 = head->next;
    
        while (p2 != NULL)
    
        {
    
          p3 = p2->next;
    
          p2->next = p1;
    
          p1 = p2;
    
          p2 = p3;
    
        }
    
        head->next = NULL;
    
        head = p1;
    
        return head;
    
      }

    8.复杂链表的复制

    这个是程序员面试精选的第49题,思路很巧妙,题目意思见链接,链表复杂就复杂在于除了有一个m_pNext指针指向下一个结点外,还有一个m_pSibling指向链表中的任一结点或者NULL。其结点的C++定义如下:

    struct ComplexNode
    
    {
    
        int m_nValue;
    
        ComplexNode* m_pNext;
    
        ComplexNode* m_pSibling;
    
    };
    程序员面试题精选100题(49)-复杂链表的复制 - 何海涛 - 微软、Google等面试题
     
    解法一旦知晓就没什么神秘的了,答案参看链接。

    9.已知两个链表head1 和head2 各自有序,请把它们合并成一个链表依然有序,保留所有结点,即便大小相同,递归实现,假设都是升序排列。

    Node * merge(Node * head1,Node *head2)
    
    {
    
    	Node *ret=NULL;
    
    	if(head1==NULL)return head2;
    
    	if(head2==NULL)return head1;
    
    
    
    	if(head1->val<head2->val)
    
    	{
    
    		ret=head1;
    
    		ret->next=merge(head1->next,head2);
    
    	}
    
    	else
    
    	{
    
    		ret=head2;
    
    		ret->next=merge(head1,head2->next);
    
    	}
    
    	return ret;
    
    }

    10.从一个未排序的链表中删除重复的元素,给出一种算法,如果要求不适用额外的空间呢?

    如果能用hash表的话,只需要遍历一次链表,如果发现元素不在hash表中,就插入hash表,反之则删除。

    如果不用额外的空间的话,有个O(N2)的算法:

    LinkList RemoveDupNode(LinkList L)//删除重复结点的算法
    {
        LinkList p,q,r;
        p=L->next;
        while(p)    // p用于遍历链表
        {
             q=p;
             while(q->next) // q遍历p后面的结点,并与p数值比较
             {
                 if(q->next->data==p->data)
                 {
                     r=q->next; // r保存需要删掉的结点
                     q->next=r->next;   // 需要删掉的结点的前后结点相接
                     free(r);
                 }
                 else
                     q=q->next;
             }
             p=p->next;
        }
        return L;
    }
  • 相关阅读:
    RabbitMq+Haproxy负载均衡
    RabbitMq常用命令
    几种常见的消息队列
    RabbitMq集群搭建
    a=a+b与a+=b的区别
    Redis集群搭建
    变量作用域
    8.3吝啬SAT问题
    Surrounded Regions
    Binary Tree Maximum Path Sum
  • 原文地址:https://www.cnblogs.com/obama/p/3065928.html
Copyright © 2011-2022 走看看