链表相关笔试题

  在笔试面试考数据结构时,因为时间有限,所出的题不会是红黑树、平衡二叉树等比较复杂的数据结构。链表结构简单,题目规模小但须要仔细考虑细节,所以称为笔试面试中的高频考点。所以,下面总结出链表相关题目,以供复习。面试

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

    2.从尾到头打印单链表(剑指offer第五题)数据结构

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

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

    5.单链表实现约瑟夫环code

    6.逆置/反转单链表排序

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

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

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

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

    11.判断单链表是否带环?若带环,求环的长度?求环的入口点?并计算每一个算法的时间复杂度&空间复杂度。

    12.判断两个链表是否相交,若相交,求交点(假设链表不带环)

    13.判断两个链表是否相交,若相交,求交点(假设链表带环)

    14.复杂链表的复制,一个链表的每一个节点,有一个指向next指针指向下一个节点,还有一个random指针指向这个链表中的一个随机节点或者NULL,如今要求实现复制这个链表

    15.求两个已排序链表中相同的数据。void UnionSet(ListNode* l1,ListNode* l2);

/////////////////////////////////////////////////////////////////////////////////////////////////////////(分割线)///////////////////////////////////////////////////

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

      首先咱们从顺序表和链表的结构上来进行分析:
        (1)对于顺序表,不管是动态的仍是静态的,他们都是连续的存储空间,在读取上时间效率比较高,可经过地址之间的运算来访问,可是在插入和删除时会出现比较麻烦的负载操做。

        (2)对于顺序表,由于是链式存储。所以在咱们须要的时候咱们才在堆上为他们开辟空间,链表对于插入删除比较简单,可是遍历的话须要屡次跳转。

      其次,咱们从顺序表和链表的空间申请方式来看:

        (1)对于顺序表,空间开辟是在顺序表已满的时候开辟,开辟次数较多的时候会出现较大的空间浪费

        (2)对于链表,空间是针对单个节点的,不存在多余的空间浪费。而且在碎片内存池的机制下,能够有效的利用空间。

      综上所述:顺序表通常用于查找遍历操做比较频繁的状况下使用,链表则针对于数据删除修改操做比较多的状况下使用。

    2.从尾到头打印单链表

      从尾到头打印单链表有两种解法,一种是利用栈把节点从头至尾push进去,利用栈先进后出的特色,从尾到头打印单链表节点,一种是利用递归,在输出现有节点以前输出下一个节点,循环直至最后一个节点,而后再将节点从尾到头依次打印。

     code1:利用栈

      void PrintTailToHead(ListNode* head)
      {
          stack<int> st;
          ListNode* p = head;
          while (p != NULL)
          {
              st.push(p->_data);
              p = p->_next;
          }
          while (!st.empty())
          {
              printf("%d->", st.top());
              st.pop();
          }
      }

     code2:利用递归

      void PrintTailToHead(ListNode* head)
     {
          if (head != NULL)
          {
              while (head->_next != NULL)
              {
                  PrintTailToHead(head->_next);
              }
          }
          printf("%d->", head->data);
     }

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

      因为链表无头,因此用常规方法删除节点是不可能的。因此咱们能够换种思路,将要删除的节点后面的节点的值赋给要删的节点,而后再把要删除节点的后面的节点删除,等于经过转换,为被删除节点创造了一个头结点。代码以下:

      void DeleteNotTailNode(ListNode* p)
     {
          ListNode* s = p->_next;
          assert(s);
          p->_data = s->_data;
          p->_next = s->_next;
          free(p);
     }

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

      这个题目跟上一个题目很像。在这个非头结点后面插入一个节点,把这个非头节点的值赋给新插入的节点,而后再把要插入的值赋给这个非头节点便可。

      void InsertNotHeadNode(ListNode* p, int data)
     {
          ListNode* s = (ListNode)malloc(sizeof(&ListNode));
          assert(s);
          s->_next =p->_next;
          p->_next = s;
          s->_data = p->_data;
          p->_data = data;
     }

    5.单链表实现约瑟夫环(剑指offer第45题)

       

    6.逆置/反转单链表(剑指offer第16题)

      

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

    8.合并两个有序链表合并后依然有序(剑指offer第17题)

      这个题比较简单,分别用指针指向两个链表,比较两个链表指针所指向节点的值,而后将节点取下来从新组成一个链表便可,代码以下:

      ListNode Merge(ListNode* head1, ListNode* head2)
     {
          if (head1 == NULL)
              return head2;
          if (head2 == NULL)
              return head1;
          ListNode* newhead = NULL;
          if (head1->_data < head2->_data)
          {
              newhead = head1;
              newhead->_next=Merge(head1->_next, head2);
          }
          if (head1->_data>head2->data)
          {
              newhead = head2;
              newhead->_next = Merge(head1, head2->_next);
          }
     }

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

      查找链表的中间节点,但只能遍历一次链表,因此咱们会想到用快慢指针来解决这个问题。定义一个快指针,每次走两步,载定义一个慢指针,每次走一步。等到快指针走到链表尾,慢指针所指向的节点就是链表的中间节点。代码以下:

      ListNode* FindMidNode(ListNode* head)
     {
          ListNode* fast;

        ListNode* slow;
          fast = head;
          slow = head;
          while (fast&&fast->_next)
          {
              slow = slow->_next;
              fast = fase->_next->_next;
          }
          retrun slow;
     }

    10.查找单链表倒数第K个节点,要求只能遍历一次链表(剑指offer第15题)

      其实这个题跟上面的题很像,稍微转化一下就能想出思路。咱们能够定义两个指针,一个指针先走K步,而后两个指针同时移动,等到先走的指针走到链表尾部,后走的指针所指向的节点就是倒数第K个节点。要注意考虑链表的各类状况。代码以下:

      ListNode* FindKthNode(ListNode* head,int k)
      {
          if (head == NULL || k == 0)
              return NULL;    
          ListNode* fast;

       ListNode* slow;
          fast = head;
          slow = head;
          for (int i = 0; i < k - 1; ++i)   //要注意链表长度比K短的状况
          {
              if (fast->_next != NULL)
                  fast = fast->_next;
              else retrun NULL;
          }
          while (fast->_next != NULL)
          {
              fast = fast->_next;
              slow = slow->_next;
          }
          return slow;
     }

    11.判断单链表是否带环?若带环,求环的长度?求环的入口点?并计算每一个算法的时间复杂度&空间复杂度。(剑指offer第56题)

    12.判断两个链表是否相交,若相交,求交点(假设链表不带环)

    13.判断两个链表是否相交,若相交,求交点(假设链表带环)

    14.复杂链表的复制,一个链表的每一个节点,有一个指向next指针指向下一个节点,还有一个random指针指向这个链表中的一个随机节点或者NULL,如今要求实现复制这个链表(剑指offer第26题)

    15.求两个已排序链表中相同的数据。void UnionSet(ListNode* l1,ListNode* l2);

    16.在已排序的链表中删除链表中重复的结点(剑指offer第57题)

相关文章
相关标签/搜索