1. 程式人生 > >java連結串列面試題

java連結串列面試題

本文包含連結串列的以下內容:

  1、單鏈表的建立和遍歷

  2、求單鏈表中節點的個數

  3、查詢單鏈表中的倒數第k個結點(劍指offer,題15)

  4、查詢單鏈表中的中間結點

  5、合併兩個有序的單鏈表,合併之後的連結串列依然有序【出現頻率高】(劍指offer,題17)

  6、單鏈表的反轉【出現頻率最高】(劍指offer,題16)

  7、從尾到頭列印單鏈表(劍指offer,題5)

  8、判斷單鏈表是否有環

  9、取出有環連結串列中,環的長度

  10、單鏈表中,取出環的起始點(劍指offer,題56)。本題需利用上面的第8題和第9題。

  11、判斷兩個單鏈表相交的第一個交點(劍指offer,題37)

1、單鏈表的建立和遍歷:

  1. publicclass LinkList {  
  2. public Node head;  
  3. public Node current;  
  4. //方法:向連結串列中新增資料
  5. publicvoid add(int data) {  
  6.  //判斷連結串列為空的時候
  7.  if (head == null) {//如果頭結點為空,說明這個連結串列還沒有建立,那就把新的結點賦給頭結點
  8.  head = new Node(data);  
  9.  current = head;  
  10.  } else {  
  11.  //建立新的結點,放在當前節點的後面(把新的結點合連結串列進行關聯)
  12.  current.next = new Node(data);  
  13.  //把連結串列的當前索引向後移動一位
  14.  current = current.next; //此步操作完成之後,current結點指向新新增的那個結點
  15.  }  
  16. }  
  17. //方法:遍歷連結串列(列印輸出連結串列。方法的引數表示從節點node開始進行遍歷
  18. publicvoid print(Node node) {  
  19.  if (node == null) {  
  20.  return;  
  21.  }  
  22. current = node;  
  23.  while (current != null) {  
  24.  System.out.println(current.data);  
  25.  current = current.next;  
  26.  }  
  27. }   
  28. class
     Node {  
  29. //注:此處的兩個成員變數許可權不能為private,因為private的許可權是僅對本類訪問。
  30.  int data; //資料域
  31.  Node next;//指標域
  32.  public Node(int data) {  
  33.  this.data = data;  
  34. }  
  35. }  
  36. publicstaticvoid main(String[] args) {  
  37. LinkList list = new LinkList();  
  38. //向LinkList中新增資料
  39.  for (int i = 0; i < 10; i++) {  
  40.  list.add(i);  
  41.  }  
  42.  list.print(list.head);// 從head節點開始遍歷輸出
  43. }  
  44. }  

上方程式碼中,這裡面的Node節點採用的是內部類來表示(33行)。使用內部類的最大好處是可以和外部類進行私有操作的互相訪問。

注:內部類訪問的特點是:內部類可以直接訪問外部類的成員,包括私有;外部類要訪問內部類的成員,必須先建立物件。

為了方便新增和遍歷的操作,在LinkList類中新增一個成員變數current,用來表示當前節點的索引(03行)。

這裡面的遍歷連結串列的方法(20行)中,引數node表示從node節點開始遍歷,不一定要從head節點遍歷。

2、求單鏈表中節點的個數:

注意檢查連結串列是否為空。時間複雜度為O(n)。這個比較簡單。

核心程式碼:

  1. //方法:獲取單鏈表的長度
  2. publicint getLength(Node head) {  
  3.  if (head == null) {  
  4.  return0;  
  5.  }  
  6.  int length = 0;  
  7.  Node current = head;  
  8.  while (current != null) {  
  9.  length++;  
  10.  current = current.next;  
  11.  }  
  12.  return length;  
  13. }  

3、查詢單鏈表中的倒數第k個結點:

3.1  普通思路:

先將整個連結串列從頭到尾遍歷一次,計算出連結串列的長度size,得到連結串列的長度之後,就好辦了,直接輸出第(size-k)個節點就可以了(注意連結串列為空,k為0,k為1,k大於連結串列中節點個數時的情況

)。時間複雜度為O(n),大概思路如下:

  1. publicint findLastNode(int index) { //index代表的是倒數第index的那個結點
  2.  //第一次遍歷,得到連結串列的長度size
  3.  if (head == null) {  
  4.  return -1;  
  5.  }  
  6.  current = head;  
  7.  while (current != null) {  
  8.  size++;  
  9.  current = current.next;  
  10. }  
  11.  //第二次遍歷,輸出倒數第index個結點的資料
  12.  current = head;  
  13.  for (int i = 0; i < size - index; i++) {  
  14.  current = current.next;  
  15.  }  
  16. return current.data;  
  17. }  

如果面試官不允許你遍歷連結串列的長度,該怎麼做呢?接下來就是。

 3.2  改進思路:(這種思路在其他題目中也有應用)

     這裡需要宣告兩個指標:即兩個結點型的變數first和second,首先讓first和second都指向第一個結點,然後讓second結點往後挪k-1個位置,此時first和second就間隔了k-1個位置,然後整體向後移動這兩個節點,直到second節點走到最後一個結點的時候,此時first節點所指向的位置就是倒數第k個節點的位置。時間複雜度為O(n)

程式碼實現:(初版)

  1. public Node findLastNode(Node head, int index) {  
  2.  if (node == null) {  
  3.  returnnull;  
  4.  }  
  5.  Node first = head;  
  6.  Node second = head;  
  7.  //讓second結點往後挪index個位置
  8.  for (int i = 0; i < index; i++) {  
  9.  second = second.next;  
  10.  }  
  11.  //讓first和second結點整體向後移動,直到second結點為Null
  12. while (second != null) {  
  13.  first = first.next;  
  14.  second = second.next;  
  15.  }  
  16.  //當second結點為空的時候,此時first指向的結點就是我們要找的結點
  17. return first;  
  18. }  

程式碼實現:(最終版)(考慮k大於連結串列中結點個數時的情況時,丟擲異常)

上面的程式碼中,看似已經實現了功能,其實還不夠健壯:

  要注意k等於0的情況;

  如果k大於連結串列中節點個數時,就會報空指標異常,所以這裡需要做一下判斷。

核心程式碼如下:

  1. public Node findLastNode(Node head, int k) {  
  2. if (k == 0 || head == null) {  
  3.  returnnull;  
  4.  }  
  5.  Node first = head;  
  6.  Node second = head;  
  7. //讓second結點往後挪k-1個位置
  8.  for (int i = 0; i < k - 1; i++) {  
  9.  System.out.println("i的值是" + i);  
  10.  second = second.next;  
  11.  if (second == null) { //說明k的值已經大於連結串列的長度了
  12.  //throw new NullPointerException("連結串列的長度小於" + k); //我們自己丟擲異常,給使用者以提示
  13.   returnnull;  
  14.  }  
  15. }  
  16.  //讓first和second結點整體向後移動,直到second走到最後一個結點
  17.  while (second.next != null) {  
  18.  first = first.next;  
  19.  second = second.next;  
  20.  }  
  21.  //當second結點走到最後一個節點的時候,此時first指向的結點就是我們要找的結點
  22. return first;  
  23. }  

4、查詢單鏈表中的中間結點:

同樣,面試官不允許你算出連結串列的長度,該怎麼做呢?

思路:

    和上面的第2節一樣,也是設定兩個指標first和second,只不過這裡是,兩個指標同時向前走,second指標每次走兩步,first指標每次走一步,直到second指標走到最後一個結點時,此時first指標所指的結點就是中間結點。注意連結串列為空,連結串列結點個數為1和2的情況。時間複雜度為O(n)。

程式碼實現:

  1. //方法:查詢連結串列的中間結點
  2. public Node findMidNode(Node head) {  
  3. if (head == null) {  
  4.  returnnull;  
  5. }  
  6. Node first = head;  
  7.  Node second = head;  
  8. //每次移動時,讓second結點移動兩位,first結點移動一位
  9. while (second != null && second.next != null) {  
  10.  first = first.next;  
  11.  second = second.next.next;  
  12. }  
  13. //直到second結點移動到null時,此時first指標指向的位置就是中間結點的位置
  14.  return first;  
  15. }  

上方程式碼中,當n為偶數時,得到的中間結點是第n/2 + 1個結點。比如連結串列有6個節點時,得到的是第4個節點。

5、合併兩個有序的單鏈表,合併之後的連結串列依然有序:

    這道題經常被各公司考察。

例如:

連結串列1:

  1->2->3->4

連結串列2:

  2->3->4->5

合併後:

  1->2->2->3->3->4->4->5

解題思路:

  挨著比較連結串列1和連結串列2。

  這個類似於歸併排序。尤其要注意兩個連結串列都為空、和其中一個為空的情況。只需要O (1) 的空間。時間複雜度為O (max(len1,len2))

程式碼實現:

  1. //兩個引數代表的是兩個連結串列的頭結點
  2. public Node mergeLinkList(Node head1, Node head2) {  
  3. if (head1 == null && head2 == null) { //如果兩個連結串列都為空
  4.  returnnull;  
  5. }  
  6.  if (head1 == null) {  
  7.  return head2;  
  8. }  
  9.  if (head2 == null) {  
  10.  return head1;  
  11. }  
  12. Node head; //新連結串列的頭結點
  13.  Node current; //current結點指向新連結串列 
  14. // 一開始,我們讓current結點指向head1和head2中較小的資料,得到head結點
  15.  if (head1.data < head2.data) {  
  16.  head = head1;  
  17.  current = head1;  
  18.  head1 = head1.next;  
  19.  } else {  
  20.  head = head2;  
  21.  current = head2;  
  22.  head2 = head2.next;  
  23. }  
  24.  while (head1 != null && head2 != null) {  
  25.  if (head1.data < head2.data) {  
  26.   current.next = head1; //新連結串列中,current指標的下一個結點對應較小的那個資料
  27.   current = current.next; //current指標下移
  28.   head1 = head1.next;  
  29.  } else {  
  30.  current.next = head2;  
  31.   current = current.next;  
  32.   head2 = head2.next;  
  33.  }  
  34.  }  
  35.  //合併剩餘的元素
  36.  if (head1 != null) { //說明連結串列2遍歷完了,是空的
  37.  current.next = head1;  
  38.  }  
  39. if (head2 != null) { //說明連結串列1遍歷完了,是空的
  40.  current.next = head2;  
  41. }  
  42.  return head;  
  43. }  

程式碼測試:

  1. publicstaticvoid main(String[] args) {  
  2. LinkList list1 = new LinkList();  
  3. LinkList list2 = new LinkList();  
  4. //向LinkList中新增資料
  5. for (int i = 0; i < 4; i++) {  
  6.  list1.add(i);  
  7.  }  
  8. for (int i = 3; i < 8; i++) {  
  9.  list2.add(i);  
  10. }  
  11. LinkList list3 = new LinkList();  
  12. list3.head = list3.mergeLinkList(list1.head, list2.head); //將list1和list2合併,存放到list3中
  13. list3.print(list3.head);// 從head節點開始遍歷輸出
  14. }  

上方程式碼中用到的add方法和print方法和第1小節中是一致的。

執行效果:

注:《劍指offer》中是用遞迴解決的,感覺有點難理解。

6、單鏈表的反轉:【出現頻率最高】

例如連結串列:

  1->2->3->4

反轉之後:

  4->2->2->1

思路:

  從頭到尾遍歷原連結串列,每遍歷一個結點,將其摘下放在新連結串列的最前端。注意連結串列為空和只有一個結點的情況。時間複雜度為O(n)

方法1:(遍歷)

  1. //方法:連結串列的反轉
  2. public Node reverseList(Node head) {  
  3. //如果連結串列為空或者只有一個節點,無需反轉,直接返回原連結串列的頭結點
  4.  if (head == null || head.next == null) {  
  5.  return head;  
  6. }  
  7. Node current = head;  
  8. Node next = null//定義當前結點的下一個結點
  9.  Node reverseHead = null//反轉後新連結串列的表頭
  10. while (current != null) {  
  11.  next = current.next; //暫時儲存住當前結點的下一個結點,因為下一次要用
  12.  current.next = reverseHead; //將current的下一個結點指向新連結串列的頭結點
  13.  reverseHead = current;   
  14.  current = next; // 操作結束後,current節點後移
  15. }  
  16. return reverseHead;  
  17. }  

上方程式碼中,核心程式碼是第16、17行。

方法2:(遞迴)

這個方法有點難,先不講了。

7、從尾到頭列印單鏈表:

  對於這種顛倒順序的問題,我們應該就會想到棧,後進先出。所以,這一題要麼自己使用棧,要麼讓系統使用棧,也就是遞迴。注意連結串列為空的情況。時間複雜度為O(n)

  注:不要想著先將單鏈表反轉,然後遍歷輸出,這樣會破壞連結串列的結構,不建議。

方法1:(自己新建一個棧)

  1. //方法:從尾到頭列印單鏈表
  2. publicvoid reversePrint(Node head) {  
  3. if (head == null) {  
  4.  return;  
  5.  }  
  6.  Stack<Node> stack = new Stack<Node>(); //新建一個棧
  7.  Node current = head;  
  8.  //將連結串列的所有結點壓棧
  9.  while (current != null) {-  
  10.  stack.push(current); //將當前結點壓棧
  11.  current = current.next;  
  12. }  
  13.  //將棧中的結點列印輸出即可
  14. while (stack.size() > 0) {  
  15.  System.out.println(stack.pop().data); //出棧操作
  16. }  
  17. }  

方法2:(使用系統的棧:遞迴,程式碼優雅簡潔)

  1. publicvoid reversePrint(Node head) {  
  2.  if (head == null) {  
  3.  return;  
  4.  }  
  5. reversePrint(head.next);  
  6. System.out.println(head.data);  
  7. }  

總結:方法2是基於遞迴實現的,戴安看起來簡潔優雅,但有個問題:當連結串列很長的時候,就會導致方法呼叫的層級很深,有可能造成棧溢位。而方法1的顯式用棧,是基於迴圈實現的,程式碼的魯棒性要更好一些。

8、判斷單鏈表是否有環:

  這裡也是用到兩個指標,如果一個連結串列有環,那麼用一個指標去遍歷,是永遠走不到頭的。

  因此,我們用兩個指標去遍歷:first指標每次走一步,second指標每次走兩步,如果first指標和second指標相遇,說明有環。時間複雜度為O (n)。

方法:

  1. //方法:判斷單鏈表是否有環
  2. publicboolean hasCycle(Node head) {  
  3.  if (head == null) {  
  4.  returnfalse;  
  5.  }  
  6.  Node first = head;  
  7.  Node second = head;  
  8.  while (second != null) {  
  9.  first = first.next; //first指標走一步
  10.  second = second.next.next; second指標走兩步  
  11.  if (first == second) { //一旦兩個指標相遇,說明連結串列是有環的
  12.   returntrue;  
  13.  }  
  14. }  
  15.  returnfalse;  
  16. }  

完整版程式碼:(包含測試部分)

這裡,我們還需要加一個過載的add(Node node)方法,在建立單向迴圈連結串列時要用到。

  1. LinkList.java:  
  2.  publicclass LinkList {  
  3.  public Node head;  
  4.  public Node current;  
  5.  //方法:向連結串列中新增資料
  6.  publicvoid add(int data) {  
  7.   //判斷連結串列為空的時候
  8.   if (head == null) {//如果頭結點為空,說明這個連結串列還沒有建立,那就把新的結點賦給頭結點
  9.   head = new Node(data);  
  10.   current = head;  
  11.  } else {  
  12.   //建立新的結點,放在當前節點的後面(把新的結點合連結串列進行關聯)
  13.   current.next = new Node(data);  
  14.   //把連結串列的當前索引向後移動一位
  15.   current = current.next;  
  16.   }  
  17.  }  
  18.  //方法過載:向連結串列中新增結點
  19.  publicvoid add(Node node) {  
  20.   if (node == null) {  
  21.   return;  
  22.   }  
  23.   if (head == null) {  
  24.   head = node;  
  25.   current = head;  
  26.   } else {  
  27.   current.next = node;  
  28.   current = current.next;  
  29.   }  
  30.  }  
  31.  //方法:遍歷連結串列(列印輸出連結串列。方法的引數表示從節點node開始進行遍歷
  32.  publicvoid print(Node node) {  
  33.   if (node == null) {  
  34.   return;  
  35.   }  
  36.   current = node;  
  37.   while (current != null) {  
  38.   System.out.println(current.data);  
  39.   current = current.next;  
  40.   }  
  41.  }  
  42.  //方法:檢測單鏈表是否有環
  43.  publicboolean hasCycle(Node head) {  
  44.   if (head == null) {  
  45.   returnfalse;  
  46.   }  
  47.   Node first = head;  
  48.   Node second = head;  
  49.   while (second != null) {  
  50.   first = first.next; //first指標走一步
  51.   second = second.next.next; //second指標走兩步
  52.   if (first == second) { //一旦兩個指標相遇,說明連結串列是有環的
  53.    returntrue;  
  54.   }  
  55.   }  
  56.   returnfalse;  
  57.  }  
  58.  class Node {  
  59.   //注:此處的兩個成員變數許可權不能為private,因為private的許可權是僅對本類訪問。
  60.   int data; //資料域
  61.   Node next;//指標域
  62.   public Node(int data) {  
  63.   this.data = data;  
  64.   }  
  65.  }  
  66.  publicstaticvoid main(String[] args) {  
  67.   LinkList list = new LinkList();  
  68.   //向LinkList中新增資料
  69.   for (int i = 0; i < 4; i++) {  
  70.   list.add(i);  
  71.   }  
  72.   list.add(list.head); //將頭結點新增到連結串列當中,於是,單鏈表就有環了。備註:此時得到的這個環的結構,是下面的第8小節中圖1的那種結構。
  73.   System.out.println(list.hasCycle(list.head));  
  74.  }  
  75.  }  

檢測單鏈表是否有環的程式碼是第50行。

88行:我們將頭結點繼續往連結串列中新增,此時單鏈表就環了。最終執行效果為true。

如果刪掉了88行程式碼,此時單鏈表沒有環,執行效果為false。

9、取出有環連結串列中,環的長度:

我們平時碰到的有環連結串列是下面的這種:(圖1)

上圖中環的長度是4。

但有可能也是下面的這種:(圖2)

此時,上圖中環的長度就是3了。

那怎麼求出環的長度呢?

思路:

    這裡面,我們需要先利用上面的第7小節中的hasCycle方法(判斷連結串列是否有環的那個方法),這個方法的返回值是boolean型,但是現在要把這個方法稍做修改,讓其返回值為相遇的那個結點。然後,我們拿到這個相遇的結點就好辦了,這個結點肯定是在環裡嘛,我們可以讓這個結點對應的指標一直往下走,直到它回到原點,就可以算出環的長度了。

方法:

  1. //方法:判斷單鏈表是否有環。返回的結點是相遇的那個結點
  2. public Node hasCycle(Node head) {  
  3.  if (head == null) {  
  4.  returnnull;  
  5.  }  
  6.  Node first = head;  
  7.  Node second = head;  
  8. while (second != null) {  
  9.  first = first.next;  
  10.  second = second.next.next;  
  11.  if (first == second) { //一旦兩個指標相遇,說明連結串列是有環的
  12.   return first; //將相遇的那個結點進行返回
  13.  }  
  14.  }  
  15.  returnnull;  
  16. }  
  17. //方法:有環連結串列中,獲取環的長度。引數node代表的是相遇的那個結點
  18. publicint getCycleLength(Node node) {  
  19.  if (head == null) {  
  20.  return0;  
  21.  }  
  22.  Node current = node;  
  23.  int length = 0;  
  24.  while (current != null) {  
  25.  current = current.next;  
  26.  length++;  
  27.  if (current == node) { //當current結點走到原點的時候
  28.   return length;  
  29.  }  
  30.  }  
  31.  return length;  
  32. }  

完整版程式碼:(包含測試部分)

  1. publicclass LinkList {  
  2.  public Node head;  
  3.  public Node current;  
  4.  publicint size;  
  5.  //方法:向連結串列中新增資料
  6.  publicvoid add(int data) {  
  7.   //判斷連結串列為空的時候
  8.   if (head == null) {//如果頭結點為空,說明這個連結串列還沒有建立,那就把新的結點賦給頭結點
  9.   head = new Node(data);  
  10.   current = head;  
  11.   } else {  
  12.   //建立新的結點,放在當前節點的後面(把新的結點合連結串列進行關聯)
  13.   current.next = new Node(data);  
  14.   //把連結串列的當前索引向後移動一位
  15.   current = current.next; //此步操作完成之後,current結點指向新新增的那個結點
  16.   }  
  17.  }  
  18.  //方法過載:向連結串列中新增結點
  19.  publicvoid add(Node node) {  
  20.   if (node == null) {  
  21.   return;  
  22.   }  
  23.   if (head == null) {  
  24.   head = node;  
  25.   current = head;  
  26.   } else {  
  27.   current.next = node;  
  28.   current = current.next;  
  29.   }  
  30.  }  
  31.  //方法:遍歷連結串列(列印輸出連結串列。方法的引數表示從節點node開始進行遍歷
  32.  publicvoid print(Node node) {  
  33.   if (node == null) {  
  34.   return;  
  35.   }  
  36.   current = node;  
  37.   while (current != null) {  
  38.   System.out.println(current.data);  
  39.   current = current.next;  
  40.   }  
  41.  }  
  42.  //方法:判斷單鏈表是否有環。返回的結點是相遇的那個結點
  43.  public Node hasCycle(Node head) {  
  44.   if (head == null) {  
  45.   returnnull;  
  46.   }  
  47.   Node first = head;  
  48.   Node second = head;  
  49.   while (second != null) {  
  50.   first = first.next;  
  51.   second = second.next.next;  
  52.   if (first == second) { //一旦兩個指標相遇,說明連結串列是有環的
  53.    return first; //將相遇的那個結點進行返回
  54.   }  
  55.   }  
  56.   returnnull;  
  57.  }  
  58.  //方法:有環連結串列中,獲取環的長度。引數node代表的是相遇的那個結點
  59.  publicint getCycleLength(Node node) {  
  60.   if (head == null) {  
  61.   return0;  
  62.   }  
  63.   Node current = node;  
  64.   int length = 0;  
  65.   while (current != null) {  
  66.   current = current.next;  
  67.   length++;  
  68.   if (current == node) { //當current結點走到原點的時候
  69.    return length;  
  70.   }  
  71.   }  
  72.   return length;  
  73.  }  
  74.  class Node {  
  75.   //注:此處的兩個成員變數許可權不能為private,因為private的許可權是僅對本類訪問。
  76.   int data; //資料域
  77.   Node next;//指標域
  78.   public Node(int data) {  
  79.   this.data = data;  
  80.   }  
  81.  }  
  82.  publicstaticvoid main(String[] args) {  
  83.   LinkList list1 = new LinkList();  
  84.   Node second = null//把第二個結點記下來
  85.   //向LinkList中新增資料
  86.   for (int i = 0; i < 4; i++) {  
  87.   list1.add(i);  
  88.   if (i == 1) {  
  89.    second = list1.current; //把第二個結點記下來
  90.   }  
  91.   }  
  92.   list1.add(second); //將尾結點指向連結串列的第二個結點,於是單鏈表就有環了,備註:此時得到的環的結構,是本節中圖2的那種結構
  93.   Node current = list1.hasCycle(list1.head); //獲取相遇的那個結點
  94.   System.out.println("環的長度為" + list1.getCycleLength(current));  
  95.  }  
  96.  }  

 執行效果:

如果將上面的104至122行的測試程式碼改成下面這樣的:(即:將圖2中的結構改成圖1中的結構)

  1. publicstaticvoid main(String[] args) {  
  2.   LinkList list1 = new LinkList();  
  3.   //向LinkList中新增資料
  4.   for (int i = 0; i < 4; i++) {  
  5.    list1.add(i);  
  6.   }  
  7.   list1.add(list1.head); //將頭結點新增到連結串列當中(將尾結點指向頭結點),於是,單鏈表就有環了。備註:此時得到的這個環的結構,是本節中圖1的那種結構。
  8.   Node current = list1.hasCycle(list1.head);  
  9.   System.out.println("環的長度為" + list1.getCycleLength(current));   
  10. }  

執行結果:

如果把上面的程式碼中的第8行刪掉,那麼這個連結串列就沒有環了,於是執行的結果為0。

10、單鏈表中,取出環的起始點:

我們平時碰到的有環連結串列是下面的這種:(圖1)

上圖中環的起始點1。

但有可能也是下面的這種:(圖2)

此時,上圖中環的起始點是2。

方法1:

這裡我們需要利用到上面第8小節的取出環的長度的方法getCycleLength,用這個方法來獲取環的長度length。拿到環的長度length之後,需要用到兩個指標變數first和second,先讓second指標走length步;然後讓first指標和second指標同時各走一步,當兩個指標相遇時,相遇時的結點就是環的起始點。

注:為了找到環的起始點,我們需要先獲取環的長度,而為了獲取環的長度,我們需要先判斷是否有環。所以這裡面其實是用到了三個方法。

程式碼實現:

方法1的核心程式碼:

  1. //方法:獲取環的起始點。引數length表示環的長度
  2. public Node getCycleStart(Node head, int cycleLength) {  
  3.  if (head == null) {  
  4.    returnnull;  
  5.  }  
  6.   Node first = head;  
  7.   Node second = head;  
  8.   //先讓second指標走length步
  9.  for (int i = 0; i < cycleLength; i++) {  
  10.    second = second.next;  
  11.   }  
  12.   //然後讓first指標和second指標同時各走一步
  13.   while (first != null && second != null) {  
  14.    first = first.next;  
  15.    second = second.next;  
  16.   if (first == second) { //如果兩個指標相遇了,說明這個結點就是環的起始點
  17.     return first;  
  18.    }  
  19.   }  
  20.   returnnull;  
  21.  }  

完整版程式碼:(含測試部分)

  1. publicclass LinkList {  
  2.  public Node head;  
  3.  public Node current;  
  4.  publicint size;  
  5.  //方法:向連結串列中新增資料
  6.  publicvoid add(int data) {  
  7.   //判斷連結串列為空的時候
  8.   if (head == null) {//如果頭結點為空,說明這個連結串列還沒有建立,那就把新的結點賦給頭結點
  9.    head = new Node(data);  
  10.    current = head;  
  11.   } else {  
  12.    //建立新的結點,放在當前節點的後面(把新的結點合連結串列進行關聯)
  13.    current.next = new Node(data);  
  14.    //把連結串列的當前索引向後移動一位
  15.    current = current.next; //此步操作完成之後,current結點指向新新增的那個結點
  16.   }  
  17.  }  
  18.  //方法過載:向連結串列中新增結點
  19.  publicvoid add(Node node) {  
  20.   if (node == null) {  
  21.    return;  
  22.   }  
  23.   if (head == null) {  
  24.    head = node;  
  25.    current = head;  
  26.   } else {  
  27.    current.next = node;  
  28.    current = current.next;  
  29.   }  
  30.  }  
  31.  //方法:遍歷連結串列(列印輸出連結串列。方法的引數表示從節點node開始進行遍歷
  32.  publicvoid print(Node node) {  
  33.   if (node == null) {  
  34.    return;  
  35.   }  
  36.   current = node;  
  37.   while (current != null) {  
  38.    System.out.println(current.data);  
  39.    current = current.next;  
  40.   }  
  41.  }  
  42.  //方法:判斷單鏈表是否有環。返回的結點是相遇的那個結點
  43.  public Node hasCycle(Node head) {  
  44.   if (head == null) {  
  45.    returnnull;  
  46.   }  
  47.   Node first = head;  
  48.   Node second = head;  
  49.   while (second != 

    相關推薦

    java連結串列試題

    本文包含連結串列的以下內容:  1、單鏈表的建立和遍歷  2、求單鏈表中節點的個數  3、查詢單鏈表中的倒數第k個結點(劍指offer,題15)  4、查詢單鏈表中的中間結點  5、合併兩個有序的單鏈表,合併之後的連結串列依然有序【出現頻率高】(劍指offer,題17)  6

    連結串列試題程式碼總結(java

    這是原文連結:http://www.jianshu.com/p/a64d1ef95980 最近總結了一下資料結構和演算法的題目,這是第二篇文章,關於連結串列的,第一篇文章關於二叉樹的參見 廢話少說,上鍊表的資料結構 class ListNode { ListNode ne

    連結串列試題集合

      連結串列面試題: 1. 比較順序表和連結串列的優缺點,說說它們分別在什麼場景下使用? 順序表一般用於查詢,可隨機訪問; 連結串列一般用於增刪改,不可隨機訪問; 如果資料元素不多,兩種方式沒有太大的差別 如果資料元素不定,建議使用連結串列 順序表

    連結串列試題C++

    注意頭結點的處理,如果倒敘記得將新的尾節點指向空。 示例一 給定一個整數num,如何在節點值有序的連結串列中插入一個節點值為num的節點,並且保證這個單鏈表依然有序。 #include<iostream> #include<thread> using

    單鏈表的基本操作及連結串列試題

    單鏈表的基本操作及連結串列面試題 程式程式碼如下: LinkList.h #ifndef __LINKLIST_H__ #define __LINKLIST_H__ #include <stdio.h> #include <stdlib.

    連結串列試題----約瑟夫環相關

    約瑟夫環相關連結串列面試題 繼上篇部落格之------------ 什麼是約瑟夫環 約瑟夫環 判斷是否成環 求環的長度 環的入口點

    一文搞定連結串列試題系列之二 —— Leetcode234. Palindrome Linked List迴文連結串列\

    連結串列薈萃二: 迴文連結串列 列表解法 迴文連結串列 題目:迴文連結串列給定一個連結串列,判斷該連結串列是否是迴文連結串列,要求O(n)時間複雜度,O(1)空間複雜度。 Given a singly linked

    連結串列試題

    關於連結串列已經學了有一段時間了,今天抽空進行了整理,列出來常見的有關連結串列的面試題,以下想法如有瑕疵望批評指出,希望能給初學者帶來一點參考和價值 從尾到頭列印單鏈表 遞迴列印 // 1、從尾到頭列印單鏈表 遞迴 void ListReversePrint(ListN

    連結串列試題】【進階】

    1、查詢倒數第 k 個連結串列 題目描述:給定一個單向連結串列 List ,要你設計演算法找出倒數第 K 個結點並列印 struct ListNode { DataType m_Value; ListNode* m_pNext; }; ListNode* FindKt

    【資料結構】連結串列試題升級版

    1、複雜連結串列的複製 複雜連結串列 一個連結串列的每個結點,有一個next指標指向下一個結點,還有一個random指標指向這個連結串列中的隨機一個結點或者NULL。 現在要求複製這個連結串列,並返回複製後的新連結串列。 思路如下: 複雜連結串列的資料型別: typedef str

    【資料結構】連結串列試題

    1、倒序列印連結串列 (1)非遞迴方式 程式碼如下: void ReversePrint(ListNode **pFirst) { ListNode *last = NULL; ListNode *cur = NULL; assert(*pFirst != NULL); w

    連結串列試題:返回倒數第k個節點

    題目來源:劍指offer 方法一: 首先我們先得出連結串列一共有多少個節點標記為count個,然後正數第count-k+1個就是倒數第k個節點,迴圈得到就可以。 程式碼: class Solution { public: ListNode*

    連結串列試題】合併兩個有序連結串列, 合併後依然有序

    程式碼 // 合併兩個有序連結串列 SListNode * MergeOrderedList(SListNode *p1First, SListNode *p2First) { SListNode*

    連結串列試題之合併兩個有序連結串列

    關於合併兩個有序的連結串列,假定該連結串列為升序排列,排列後的新表依舊升序。以下提供兩種方法進行排列。 建立新節點為空,依次後續插入法 Node* MergeTwoList1(N

    連結串列試題小結(待完成)

    以下程式碼使用的公共結構: 連結串列節點: struct ListNode { int data; ListNode *next; }; 輔助函式: //make a list ListNode *makeList(int *arr, int n) { if(a

    連結串列試題 ---- 複雜連結串列的複製

    一個連結串列的每個結點,有一個next指標指向下一個結點,還有一個random指標指向這個連結串列中的一個隨機結點或者NULL,題目要求實現複製這個連結串列,返回複製後的新連結串列 如圖所示: 1.先將單鏈表複製,如下圖 2.找複雜指標,對新節點複

    連結串列試題總結 C/C++

    資料結構和演算法,是我們程式設計最重要的兩大元素,可以說,我們的程式設計,都是在選擇和設計合適的資料結構來存放資料,然後再用合適的演算法來處理這些資料。       在面試中,最經常被提及的就是連結串列,因為它簡單,但又因為需要對指標進行操作,凡是涉及到指標的,都需要我

    關於連結串列試題的一些理解

        面試題一.刪除無頭單鏈表的非尾結點     Del_Nottail(pLinkNode pos);        顧名思義刪除非尾結點就是刪除的結點不是最後一個結點,之前在實現連結串列功能的時

    連結串列試題之快慢指標問題(一) 查詢連結串列中間節點

    查詢連結串列中間節點 一.問題描述         給點一個連結串列,返回該連結串列的中間節點。 二.問題分析         假定一個連結串列中節點資料依次為(單數):1->2->3-&

    螞蟻金服初級Java電話面試 試題

    ringbuf str eem 螞蟻 ren 電話 什麽 死鎖 hashmap 1.String, StringBuffer, StringBuilder 的區別,為什麽String 不可改變,StringBuilder可以改變? 2.HashMap 是不是有序的,有哪些有