鏈表相關筆試題


  在筆試面試考數據結構時,由於時間有限,所出的題不會是紅黑樹、平衡二叉樹等比較復雜的數據結構。鏈表結構簡單,題目規模小但需要仔細考慮細節,因此稱為筆試面試中的高頻考點。因此,下面總結出鏈表相關題目,以供復習。

    1.比較順序表和鏈表的優缺點,說說他們分別在什么場景下使用?

    2.從尾到頭打印單鏈表(劍指offer第五題)

    3.刪除一個無頭單鏈表的非尾節點

    4.在無頭單鏈表的一個非頭結點前插入一個節點

    5.單鏈表實現約瑟夫環

    6.逆置/反轉單鏈表

    7.單鏈表排序(冒泡排序&快速排序)

    8.合並兩個有序鏈表合並后依然有序

    9.查找鏈表的中間節點,要求只能遍歷一次鏈表

    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題)

      

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM