1. 程式人生 > >連結串列---------插入、查詢、刪除重複元素、找到倒數第K個值、找到反轉、列印連結串列、查詢中間元素、是否有環、是否相交

連結串列---------插入、查詢、刪除重複元素、找到倒數第K個值、找到反轉、列印連結串列、查詢中間元素、是否有環、是否相交

package JBLinkList;

public class MyLinkList {
	Node head ;
	public MyLinkList(){
		head = null;
	}

	/**
	 * 新增結點(尾部)
	 * @param d
	 */
	public void addNode(int d){
		Node newNode = new Node(d);
		if(head == null){
			head = newNode;
			return;
		}
		Node tmp = head;
		while(tmp.next!= null){
			tmp = tmp.next;
		}
		tmp.next = newNode;
	}
	
	/**
	 * 刪除結點
	 * 1、判斷位置是否合理
	 * 2、刪除頭結點i=1  head =head.next
	 * 3、其他結點     i=2....
	 *    判斷是否為空
	 *        不為空
	 *            是否是要刪除的結點,刪除 preNode.next = curNode.next
	 *            不是則向右移                               preNode = curNode ; curNode = curNode.next
	 *        為空返回true
	 * @param index
	 * @return
	 */
	public boolean deleteNode(int index){
		if (index<1 || index>length()) {
			return false;
		}
		if (index==1) {
			head = head.next;
			return true;
		}
		int i = 2;
		Node preNode = head;
		Node curNode = preNode.next;
		while(curNode!=null){
			if (i==index) {
				preNode.next = curNode.next;
				return true;
			}
			preNode = curNode ;
			curNode = curNode.next;
		}
		return true;
	}
	
	/**
	 * 查詢結點
	 */
	public Node finNode(int i){
		Node node = head;
		while(node.data != i){
			if(node.next!=null){
				node = node.next;
			}
			return null;
		}
		return node;
	}

	/**
	 * 返回長度
	 * tmp = tmp.next
	 * length++
	 * @return
	 */
	public int length() {
		int length = 0;
		Node tmp = head;
		while(tmp!=null){
			length++;
			tmp = tmp.next;
		}
		return length;
	}
	
	/**
	 * 排序
	 * 選擇排序
	 * @return
	 */
	public Node orderList(){
		Node nextNode = null;
		int temp = 0;
		Node curNode = head;
		//curNode作為頭結點迴圈的次數
		while(curNode.next!=null){
			nextNode = curNode.next;
			//除頭結點以外其他結點迴圈比較找到最小的結點與當前頭結點資料交換
			while(nextNode.next!=null){
				if (curNode.data>nextNode.data) {
					temp = curNode.data;
					curNode.data = nextNode.data;
					nextNode.data = temp;
				}
				//右移遍歷結點
				nextNode = nextNode.next;
			}
			//迴圈起始點右移
			curNode=curNode.next;	
		}
		return head;	
	}
	
	/**
	 * 列印結點
	 * 從頭結點開始
	 * 向右移動tmp = tmp.next
	 */
	public void printList(){
		Node tmp = head;
		while(tmp !=null){
			System.out.println(tmp.data);
			tmp = tmp.next;		
		}
	}

	/**
	 * 刪除重複元素
	 * @param head
	 */
	public void deleteDuplecate(Node head){
		Node p = head;
		while (p!=null) {
			Node q = p;
			while (q.next!=null) {
				if(p.data == q.next.data){
					q.next = q.next.next;
				}
				q = q.next;
			}
			p = p.next;
		}	
	}
	/**
	 * 找到連結串列中倒數第K個元素
	 * 1、設定兩個指標,其中一個指標比另一個先走K-1步
	 * 2、若先行指標的下一元素不為空,兩個指標同時往前移動
	 * 3、迴圈直到先行指標的下一元素為空,返回後行指標的位置。
	 * @param head
	 * @param k
	 * @return
	 */
	public Node findElem(Node head, int k){
		if(k<1){
			return null;
		}
		Node p1 = head;
		Node p2 = head;
		for (int i = 0; i <k-1 && p1!=null; i++) {
			p1 = p1.next;
			if (p1==null) {
				System.out.println("K不合法");
				return null;
			}
		}
		
		while (p1.next!=null) {
			p1 = p1.next;
			p2 = p2.next;
			
		}
		return p2;
	}
	
	/**
	 * 實現連結串列的反轉
	 * @param args
	 */
	
	 public Node ReverseList1(Node head) {
	        if(head==null)
	            return null;
	       Node newHead = null;
	       Node pNode = head;
	       Node pPrev = null;
	        while(pNode!=null){
	            Node pNext = pNode.next;
	            if(pNext==null)
	                newHead = pNode;
	            pNode.next = pPrev;
	            pPrev = pNode;
	            pNode = pNext;
	        }
	        return newHead;
	    }
	 
	/* public ListNode ReverseList(ListNode head){
			ListNode pReversedHead = null;
			ListNode pNode = head;
			ListNode pPre = null;
			while (pNode != null) {
				ListNode pNext = pNode.next;
				if(pNode.next==null){//當前結點已經是最後一個節點
					pReversedHead = pNode;//需要把最後這個節點的值儲存下來,因為前面的鏈斷了就無法查詢到最後這個結點
				}
				pNode.next=pPre ;//不是最後一個結點就把這個結點指向後一個結點的鏈斷開指向前一個結點
				pPre = pNode;//向後移迴圈
				pNode = pNext;//向後移迴圈
			}
			return pReversedHead;//已經到最後一個結點,將儲存的最後一個結點設為該連結串列的頭結點
		}*/
	
	public void ReverseIteratively(Node head){
		Node pReversedHead = head;
		Node pNode = head;
		Node pPre = null;
		while (pNode != null) {
			Node pNext = pNode.next;
			if(pNode.next==null){//當前結點已經是最後一個節點
				pReversedHead = pNode;//需要把最後這個節點的值儲存下來,因為前面的鏈斷了就無法查詢到最後這個結點
			}
			pNode.next=pPre ;//不是最後一個結點就把這個結點指向後一個結點的鏈斷開指向前一個結點
			pPre = pNode;//向後移迴圈
			pNode = pNext;//向後移迴圈
		}
		this.head = pReversedHead;//已經到最後一個結點,將儲存的最後一個結點設為該連結串列的頭結點
	}
	
	/**
	 * 從尾到頭打印出連結串列
	 */
	public void printListReversely(Node pListHead){
		if (pListHead!=null) {
			printListReversely(pListHead.next);//遞迴列印後面的結點
			System.out.print(pListHead.data);//再輸出結點本身
		}
	}
	
	/**
	 * 查詢中間元素
	 * 1、兩個指標同時從頭結點遍歷
	 * 2、一個快指標一次走兩步,一個慢指標一次走一步
	 * 3、當快指標走到尾部時,慢指標恰好到達連結串列中部
	 * @param head
	 * @return
	 */
	public Node SearchMid(Node head){
		Node p = this.head;
		Node q = this.head;
		while(p!=null&&p.next!=null&&p.next.next!=null){
			p = p.next.next;
			q=q.next;
		}
		return q;
	}
	
	/**
	 * 檢測一個連結串列是否有環和環的入口
	 * 1、宣告兩個指標,fast slow
	 * 2、fast一次走兩步,慢指標一次走一步,當快指標等於慢指標是證明有環
	 * 3、找到入口方法
	 *    3.1 r代表環長,L代表鏈長,x代表環入口到相遇點的距離,a代表起點到環入口距離
	 *        slow走了s,fast走了2s,
	 *        2s=s+nr,
	 *        s=a+x=nr=(n-1)r+r,
	 *        L=r+a,r=L-a
	 *        a+x=(n-1)r+L-a
	 *        a=(n-1)r+(L-a-x)
	 *        (L-a-x)代表相遇點到環入口的距離。
	 *        一指標從起始點開始
	 *        另一指標從相遇點開始
	 *        每次各走一步兩個指標必定相遇,相遇點則是環入口   
	 */
	public  Node  FindLoopPort(Node head){
		Node fast = head;
		Node slow = head;
		if(fast==null && fast.next==null){
			return null;
		}
		while (fast!=null&&fast.next!=null) {
			fast = fast.next.next;
			slow = slow.next;
			if (slow.data == fast.data) {
				fast = head;
				while(slow.data != fast.data){
					slow = slow.next;
					fast = fast.next;
				}
				return slow;
			}
		}
		return null;
		
	}
	
	
	/**
	 * 不知道頭結點的情況下刪除指標
	 * 1、如果刪除結點是尾結點,無法刪除,因為無法使其前驅的next為空
	 * 2、如果刪除結點不是尾結點,則交換該結點與其後繼結點的值,然後刪除後繼結點
	 */
	public boolean deleteNode(Node n){
		if (n==null||n.next==null) {
			return false;
		}
		int tmp = n.data;
		n.data = n.next.data;
		n.next.data = tmp;
		n.next = n.next.next;
		return true;
	}
	
	/**
	 * 	判斷兩個連結串列是否相交,找到相交點
	 *  1、判斷是否相交
	 *    1.1 迴圈找到兩個連結串列的尾結點,並記錄各自長度len1 、len2
	 *    1.2 尾結點相同說明相交
	 *  2、找相交點
	 *    2.1 先遍歷到較長的連結串列第len2-len1位置的結點P(假設2較長)
	 *    2.2 然後再同時遍歷兩個連結串列,直到遇到相同結點,此結點便是相交點
	 */
	
	public Node getFirstMeetNode(Node h1, Node h2){
		if(h1==null || h2==null){
			return null;
		}
		Node tail1 = h1;
		int len1 = 1;
		while(tail1.next!=null){
			tail1 = tail1.next;
			len1++;
		}
		Node tail2=h2;
		int len2 = 2;
		while(tail2!=null){
			tail2 = tail2.next;
			len2++;
		}
		if(tail1!=tail2){
			return null;
		}
		Node t1 = h1;
		Node t2 = h2;
		if(len1>len2){
			int d = len1-len2;
			while(d!=0){
				t1 = t1.next;
				d--;
			}
		}
		else{
			int d = len2-len1;
			while(d!=0){
				t2 = t2.next;
				d--;
			}
		}
		while(t1 != t2){
			t1 = t1.next;
			t2 = t2.next;
			
		}
		return t1;
	}
}