Java如何編寫鏈表程式__Java

來源:互聯網
上載者:User

          最近在訓練ACM時用C語言編寫了很多的鏈表程式。因為最近在學java,於是便想編寫java的鏈表程式,結果遇到了很多困難。下面的這篇文章很好的概括了整個編寫的過程,值得收藏。

       1、單鏈表的建立和遍曆

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

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

  4、尋找單鏈表中的中間結點

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

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

       7、刪除單鏈表中的重複元素

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

  9、判斷單鏈表是否有環

  10、取出有環鏈表中,環的長度

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

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

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

public class LinkList{public Node head;public Node current;//方法:向鏈表中添加資料public void add(int data){//判斷鏈表為空白的時候if(head == null) {//如果頭結點為空白,說明這個鏈表還沒有建立,那就把新的結點賦給頭結點head= new Node(data);current = head;}else{//建立新的結點,放在當前節點的後面(把新的結點合鏈表進行關聯)current.next = new Node(data);//把鏈表的當前索引向後移動一位current = current.next; //此步操作完成之後,current結點指向新添加的那個結點}} //方法:遍曆鏈表(列印輸出鏈表。方法的參數表示從節點node開始進行遍曆public void print(Node node) {if(node == null){return;} current = node;while(current != null) {System.out.println(current.data);current = current.next;}} class Node {//註:此處的兩個成員變數許可權不能為private,因為private的許可權是僅對本類訪問。int data;//資料域Node next;//指標域 public Node(int data) {this.data = data;}}  public static void main(String[] args) {LinkList list = new LinkList();//向LinkList中添加資料for(int i = 0; i < 10; i++) {list.add(i);        }                        list.print(list.head);//從head節點開始遍曆輸出}}

 

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

註:內部類訪問的特點是:內部類可以直接存取外部類的成員,包括私人;外部類要訪問內部類的成員,必須先建立對象。

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

這裡面的遍曆鏈表的方法(36行)中,參數node表示從node節點開始遍曆,不一定要從head節點遍曆。

 

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

注意檢查鏈表是否為空白。時間複雜度為O(n)。這個比較簡單。

核心代碼:

//方法:擷取單鏈表的長度public int getLength(Node head) {if(head == null){return 0;}int length = 0;Node current = head;while(current != null){length++;current = current.next;}return length;}


3、尋找單鏈表中的倒數第k個結點:

3.1  普通思路:

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

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

public int findLastNode(int index){ //index代表的是倒數第index的那個結點//第一次遍曆,得到鏈表的長度sizeif(head == null){return-1;}int size=0;current = head;while(current!= null){size++;current = current.next;}//第二次遍曆,輸出倒數第index個結點的資料current = head;for(int i = 0;i < size - index; i++) {current = current.next;}return current.data;}


如果面試官不允許你遍曆鏈表的長度,該怎麼做呢。接下來就是。

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

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

代碼實現:(初版)

public Node findLastNode(Node head, int index){if(head == null){return null;} Node first = head;Node second = head;//讓second結點往後挪index個位置for(int i= 0;i < index; i++) {second = second.next;} //讓first和second結點整體向後移動,直到second結點為Nullwhile(second != null){first = first.next;second = second.next;} //當second結點為空白的時候,此時first指向的結點就是我們要找的結點return first;} 


代碼實現:(最終版)(考慮k大於鏈表中結點個數時的情況時,拋出異常)

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

  要注意k等於0的情況;

  如果k大於鏈表中節點個數時,就會報null 指標異常,所以這裡需要做一下判斷。

核心代碼如下:

 

public Node findLastNode(Node head, int k){        if(k == 0|| head == null) {            return null;        }            Node first = head;        Node second = head;            //讓second結點往後挪k-1個位置        for(int i = 0; i < k - 1; i++) {            System.out.println("i的值是"+ i);            second = second.next;            if(second == null) { //說明k的值已經大於鏈表的長度了                //throw              new NullPointerException("鏈表的長度小於" + k); //我們自己拋出異常,給使用者以提示                return null;            }        }          //讓first結點向後挪k-1個位置    for(int i=0; i < k-1;i++) {        first=first.next;        }        return first;//返回此時first的值即為要找點的值}


4、尋找單鏈表中的中間結點:

同樣,面試官不允許你算出鏈表的長度,該怎麼做呢。

思路:

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

代碼實現:

//方法:尋找鏈表的中間結點public Node findMidNode(Node head) { if(head == null) {return null;}Node first = head;Node second = head;//每次移動時,讓second結點移動兩位,first結點移動一位while(second != null&& second.next != null) {first = first.next;second = second.next.next;}//直到second結點移動到null時,此時first指標指向的位置就是中間結點的位置return first;}

 

上方代碼中,當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))

代碼實現:

//兩個參數代表的是兩個鏈表的頭結點public Node mergeLinkList(Node head1, Node head2) { if(head1 == null&& head2 == null) { //如果兩個鏈表都為空白return null;}if(head1 == null) {return head2;}if(head2 == null) {return head1;} Node head; //新鏈表的頭結點Node current; //current結點指向新鏈表// 一開始,我們讓current結點指向head1和head2中較小的資料,得到head結點if(head1.data < head2.data) {head = head1;current = head1;head1 = head1.next;}else{head = head2;current = head2;head2 = head2.next;} while(head1 != null && head2 != null) {if(head1.data < head2.data) {current.next = head1; //新鏈表中,current指標的下一個結點對應較小的那個資料current = current.next; //current指標下移head1 = head1.next;}else{current.next = head2;current = current.next;head2 = head2.next;}}  //合并剩餘的元素if(head1 != null) { //說明鏈表2遍曆完了,是空的current.next = head1;} if(head2 != null) { //說明鏈表1遍曆完了,是空的current.next = head2;} return head;} 


代碼測試:

public static void main(String[] args) {LinkList list1 = newLinkList();LinkList list2 = newLinkList();//向LinkList中添加資料for(int i = 0; i < 4; i++) {list1.add(i);} for(int i = 3; i < 8; i++) {list2.add(i);} LinkList list3 = newLinkList();list3.head = list3.mergeLinkList(list1.head, list2.head); //將list1和list2合并,存放到list3中list3.print(list3.head);// 從head節點開始遍曆輸出} 


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


6、單鏈表的反轉:

 public static Node reverse(Node head)      {          if (null == head)          {             return null;         }          Node pre = head;          Node cur = head.next;          Node next;          while (null != cur)          {              next = cur.next;              cur.next=pre;              pre = cur;              cur = next;          }            // 將原鏈表的前端節點的下一個節點置為null,再將反轉後的前端節點賦給head          head.next=null;          return pre;    }  }


6、刪除單鏈表中的重複元素:

public static int deleteDupsNew(Node head) {      if (head == null) return 0;      Node current = head;    int x=0;    while(current != null){          Node runner = current;          while (runner.next != null){              if(runner.next.data == current.data){                  runner.next = runner.next.next;                x++;            }              else{                  runner = runner.next;              }          }          current = current.next;      }    return x;}


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.