原創不易,如需轉載,請註明出處http://www.javashuo.com/article/p-szxcuqsm-dk.html,不然將追究法律責任!!! html
public class SingleLinkedList { //head爲頭節點,他不存聽任何的數據,只是充當一個指向鏈表中真正存放數據的第一個節點的做用 public Node head = new Node(); //內部類,定義node節點,使用內部類的最大好處是能夠和外部類進行私有操做的互相訪問 class Node{ public int val; //int類型會致使head節點的val爲0,不影響咱們學習 public Node next; public Node(){} public Node(int val){ this.val = val; } } //下面就能夠自定義各類鏈表操做。。。 }
//找到鏈表的末尾結點,把新添加的數據做爲末尾結點的後續結點 public void add(int data){ if (head.next == null){ head.next = new Node(data); return; } Node temp = head; while (temp.next != null){ temp = temp.next; } temp.next = new Node(data); }
//把要刪除結點的前結點指向要刪除結點的後結點,即直接跳過待刪除結點 public boolean deleteNode(int index){ if (index < 0 || index > length() ){ return false; } if (index == 1){ //刪除頭結點 head = head.next; return true; } Node preNode = head; Node curNode = preNode.next; int i = 2; while (curNode!=null){ if (index == i){ preNode.next = curNode.next; //指向刪除節點的後一個節點 break; } preNode = curNode; curNode = preNode.next; i++; } return true; }
//獲取鏈表長度 public int length(){ int length = 0; Node temp = head; while (temp.next!=null){ length++; temp = temp.next; } return length; } //獲取最後一個節點 public Node getLastNode(){ Node temp = head; while (temp.next != null){ temp = temp.next; } return temp; } //獲取第index節點 public Node getNodeByIndex(int index){ if(index<1 || index>length()){ return null; } Node temp = head; int i = 1; while (temp.next != null){ temp = temp.next; if (index==i){ break; } i++; } return temp; } //打印節點 public void printLink(){ Node curNode = head; while(curNode !=null){ System.out.print(curNode.val+" "); curNode = curNode.next; } }
//兩個指針,第一個指針向前移動k-1次,以後兩個指針共同前進,當前面的指針到達末尾時,後面的指針所在的位置就是倒數第k個位置 public Node findReverNode(int index){ if(index<1 || index>length()){ return null; } Node first = head; Node second = head; for (int i = 0; i < index - 1; i++) { second = second.next; } while (second.next != null){ first = first.next; second = second.next; } return first; }
//也是設置兩個指針first和second,只不過這裏是,兩個指針同時向前走,second指針每次走兩步, //first指針每次走一步,直到second指針走到最後一個結點時,此時first指針所指的結點就是中間結點。 public Node findMiddleNode(){ Node slowPoint = head; Node quickPoint = head; //鏈表結點個數爲奇數時,返回的是中間結點;鏈表結點個數爲偶數時,返回的是中間兩個結點中的前個 while(quickPoint != null && quickPoint.next != null){ slowPoint = slowPoint.next; quickPoint = quickPoint.next.next; } return slowPoint; }
//方法一:先反轉鏈表,再輸出鏈表,須要鏈表遍歷兩次(不建議這麼作,改變了鏈表的結構) 。。。 //方法2、經過遞歸來實現(鏈表很長的時候,就會致使方法調用的層級很深,有可能形成StackOverflowError) public void reservePrt(Node node){ if(node != null){ reservePrt(node.next); System.out.print(node.val+" "); } } //方法3、把鏈表中的元素放入棧中再輸出,須要維護額外的棧空間 public void reservePrt2(Node node){ if(node != null){ Stack<Node> stack = new Stack<Node>(); //新建一個棧 Node current = head; //將鏈表的全部結點壓棧 while (current != null) { stack.push(current); //將當前結點壓棧 current = current.next; } //將棧中的結點打印輸出便可 while (stack.size() > 0) { System.out.print(stack.pop().val+" "); //出棧操做 } } }
//從頭至尾遍歷原鏈表,每遍歷一個結點,將其摘下放在新鏈表的最前端。注意鏈表爲空和只有一個結點的狀況。時間複雜度爲O(n) public void reserveLink(){ Node curNode = head; Node preNode = null; while (curNode.next != null){ Node nextNode = curNode.next; //主要理解如下邏輯 curNode.next = preNode; //將current的下一個結點指向新鏈表的頭結點 preNode = curNode; //將改變了指向的cruNode賦值給preNode curNode = nextNode; } curNode.next = preNode; preNode = curNode; head = preNode; }
//設置快指針和慢指針,慢指針每次走一步,快指針每次走兩步,當快指針與慢指針相等時,就說明該鏈表有環 public boolean isRinged(){ if(head == null){ return false; } Node slow = head; Node fast = head; while(fast.next != null && fast.next.next != null){ slow = slow.next; fast = fast.next.next; if(fast == slow){ return true; } } return false; }
//獲取環的相遇點 public Node getFirstMeet(){ if(head == null){ return null; } Node slow = head; Node fast = head; while(fast.next != null && fast.next.next != null){ slow = slow.next; fast = fast.next.next; if(fast == slow){ return slow; } } return null; } //首先獲得相遇的結點,這個結點確定是在環裏,咱們可讓這個結點對應的指針一直往下走,直到它回到原點,就能夠算出環的長度 public int getCycleLength(){ Node current = getFirstMeet(); //獲取相遇點 int length = 0; while (current != null) { current = current.next; length++; if (current == getFirstMeet()) { //當current結點走到原點的時候 return length; } } return length; }
//兩個鏈表相交,則它們的尾結點必定相同,比較兩個鏈表的尾結點是否相同便可 public boolean isCross(Node head1, Node head2){ Node temp1 = head1; Node temp2 = head2; while(temp1.next != null){ temp1 = temp1.next; } while(temp2.next != null){ temp2 = temp2.next; } if(temp1 == temp2){ return true; } return false; }
/** * 若是鏈表相交,求鏈表相交的起始點: * 一、首先判斷鏈表是否相交,若是兩個鏈表不相交,則求相交起點沒有意義 * 二、求出兩個鏈表長度之差:len=length1-length2 * 三、讓較長的鏈表先走len步 * 四、而後兩個鏈表同步向前移動,每移動一次就比較它們的結點是否相等,第一個相等的結點即爲它們的第一個相交點 */ public Node findFirstCrossPoint(SingleLinkedList linkedList1, SingleLinkedList linkedList2){ //鏈表不相交 if(!isCross(linkedList1.head,linkedList2.head)){ return null; }else{ int length1 = linkedList1.length();//鏈表1的長度 int length2 = linkedList2.length();//鏈表2的長度 Node temp1 = linkedList1.head;//鏈表1的頭結點 Node temp2 = linkedList2.head;//鏈表2的頭結點 int len = length1 - length2;//鏈表1和鏈表2的長度差 if(len > 0){//鏈表1比鏈表2長,鏈表1先前移len步 for(int i=0; i<len; i++){ temp1 = temp1.next; } }else{//鏈表2比鏈表1長,鏈表2先前移len步 for(int i=0; i<len; i++){ temp2 = temp2.next; } } //鏈表1和鏈表2同時前移,直到找到鏈表1和鏈表2相交的結點 while(temp1 != temp2){ temp1 = temp1.next; temp2 = temp2.next; } return temp1; } }
//兩個參數表明的是兩個鏈表的頭結點 //方法一 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.val <= head2.val) { head = head1; current = head1; head1 = head1.next; } else { head = head2; current = head2; head2 = head2.next; } while (head1 != null && head2 != null) { if (head1.val <= head2.val) { 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 Node merge(Node head1, Node head2) { if(head1 == null){ return head2; } if(head2 == null){ return head1; } Node head = null; if(head1.val <= head2.val){ head = head1; head.next = merge(head1.next,head2); }else{ head = head2; head.next = merge(head1,head2.next); } return head; }
到此單鏈表的一些常見操做展現的差很少了,若有興趣可繼續深刻研究~~~前端
雙向鏈表(雙鏈表)是鏈表的一種。和單鏈表同樣,雙鏈表也是由節點組成,它的每一個數據結點中都有兩個指針,分別指向直接後繼和直接前驅。因此,從雙向鏈表中的任意一個結點開始,均可以很方便地訪問它的前驅結點和後繼結點。通常咱們都構造雙向循環鏈表。java
public class DoubleLink<T> { // 表頭 private DNode<T> mHead; // 節點個數 private int mCount; // 雙向鏈表「節點」對應的結構體 private class DNode<T> { public DNode prev; public DNode next; public T value; public DNode(T value, DNode prev, DNode next) { this.value = value; this.prev = prev; this.next = next; } } // 構造函數 public DoubleLink() { // 建立「表頭」。注意:表頭沒有存儲數據! mHead = new DNode<T>(null, null, null); mHead.prev = mHead.next = mHead; // 初始化「節點個數」爲0 mCount = 0; } // 返回節點數目 public int size() { return mCount; } // 返回鏈表是否爲空 public boolean isEmpty() { return mCount==0; } // 獲取第index位置的節點 private DNode<T> getNode(int index) { if (index<0 || index>=mCount) throw new IndexOutOfBoundsException(); // 正向查找 if (index <= mCount/2) { DNode<T> node = mHead.next; for (int i=0; i<index; i++) node = node.next; return node; } // 反向查找 DNode<T> rnode = mHead.prev; int rindex = mCount - index -1; for (int j=0; j<rindex; j++) rnode = rnode.prev; return rnode; } // 獲取第index位置的節點的值 public T get(int index) { return getNode(index).value; } // 獲取第1個節點的值 public T getFirst() { return getNode(0).value; } // 獲取最後一個節點的值 public T getLast() { return getNode(mCount-1).value; } // 將節點插入到第index位置以前 public void insert(int index, T t) { if (index==0) { DNode<T> node = new DNode<T>(t, mHead, mHead.next); mHead.next.prev = node; mHead.next = node; mCount++; return ; } DNode<T> inode = getNode(index); DNode<T> tnode = new DNode<T>(t, inode.prev, inode); inode.prev.next = tnode; inode.next = tnode; mCount++; return ; } // 將節點插入第一個節點處。 public void insertFirst(T t) { insert(0, t); } // 將節點追加到鏈表的末尾 public void appendLast(T t) { DNode<T> node = new DNode<T>(t, mHead.prev, mHead); mHead.prev.next = node; mHead.prev = node; mCount++; } // 刪除index位置的節點 public void del(int index) { DNode<T> inode = getNode(index); inode.prev.next = inode.next; inode.next.prev = inode.prev; inode = null; mCount--; } // 刪除第一個節點 public void deleteFirst() { del(0); } // 刪除最後一個節點 public void deleteLast() { del(mCount-1); } }
我的博客地址:node
cnblogs:https://www.cnblogs.com/baixianlong
csdn:https://blog.csdn.net/tiantuo6513
segmentfault:https://segmentfault.com/u/baixianlong
github:https://github.com/xianlongbaigit