JAVA中關於連結串列的操作和基本演算法
阿新 • • 發佈:2019-01-26
import java.util.HashMap; import java.util.Scanner; import java.util.Stack; /** * * @author kerryfish * 關於java中連結串列的操作 * 1. 求單鏈表中結點的個數: getListLength * 2. 將單鏈表反轉: reverseList(遍歷),reverseListRec(遞迴) * 3. 查詢單鏈表中的倒數第K個結點(k > 0): reGetKthNode * 4. 查詢單鏈表的中間結點: getMiddleNode * 5. 從尾到頭列印單鏈表: reversePrintListStack,reversePrintListRec(遞迴) * 6. 已知兩個單鏈表pHead1 和pHead2 各自有序,把它們合併成一個連結串列依然有序: mergeSortedList, mergeSortedListRec * 7. 對單鏈表進行排序,listSort(歸併),insertionSortList(插入) * 8. 判斷一個單鏈表中是否有環: hasCycle * 9. 判斷兩個單鏈表是否相交: isIntersect * 10. 已知一個單鏈表中存在環,求進入環中的第一個節點: getFirstNodeInCycle, getFirstNodeInCycleHashMap * 11. 給出一單鏈表頭指標head和一節點指標delete,O(1)時間複雜度刪除節點delete: deleteNode * 12. 以k個數為一組,反轉連結串列。 */ public class LinkedListSummary { /** * @param args * */ public static class Node{ int value; Node next; public Node(int n){ this.value=n; this.next=null; } } public static void main(String[] args) { // TODO Auto-generated method stub Scanner in=new Scanner(System.in); Node head=null; if(in.hasNextInt()){ head=new Node(in.nextInt()); } Node temp=head; while(in.hasNextInt()){ temp.next=new Node(in.nextInt()); temp=temp.next; } in.close(); //int len=getListLength(head); //Node reHead=reverseList(head); //reHead=reverseListRec(reHead); //Node node_k=reGetKthNode(head,3); //Node mid=getMiddleNode(head); //reversePrintListRec(head); //reversePrintListStack(head); //Node mergeHead=mergeSortedList(head,null); //Node sortHead=listSort(head); } //求單鏈表中結點的個數: getListLength public static int getListLength(Node head){ int len=0; while(head!=null){ len++; head=head.next; } return len; } //將單鏈表反轉,迴圈 public static Node reverseList(Node head){ if(head==null||head.next==null)return head; Node pre=null; Node nex=null; while(head!=null){ nex=head.next; head.next=pre; pre=head; head=nex; } return pre; } //將單鏈表反轉,遞迴 public static Node reverseListRec(Node head){ if(head==null||head.next==null)return head; Node reHead=reverseListRec(head.next); head.next.next=head; head.next=null; return reHead; } //查詢單鏈表中的倒數第K個結點(k > 0) public static Node reGetKthNode(Node head,int k){ if(head==null)return head; int len=getListLength(head); if(k>len)return null; Node target=head; Node nexk=head; for(int i=0;i<k;i++){ nexk=nexk.next; } while(nexk!=null){ target=target.next; nexk=nexk.next; } return target; } //查詢單鏈表的中間結點 public static Node getMiddleNode(Node head){ if(head==null||head.next==null)return head; Node target=head; Node temp=head; while(temp!=null&&temp.next!=null){ target=target.next; temp=temp.next.next; } return target; } //從尾到頭列印單鏈表,遞迴 public static void reversePrintListRec(Node head){ if(head==null)return; else{ reversePrintListRec(head.next); System.out.println(head.value); } } //從尾到頭列印單鏈表,棧 public static void reversePrintListStack(Node head){ Stack<Node> s=new Stack<Node>(); while(head!=null){ s.push(head); head=head.next; } while(!s.isEmpty()){ System.out.println(s.pop().value); } } //合併兩個有序的單鏈表head1和head2,迴圈 public static Node mergeSortedList(Node head1,Node head2){ if(head1==null)return head2; if(head2==null)return head1; Node target=null; if(head1.value>head2.value){ target=head2; head2=head2.next; } else{ target=head1; head1=head1.next; } target.next=null; Node mergeHead=target; while(head1!=null && head2!=null){ if(head1.value>head2.value){ target.next=head2; head2=head2.next; } else{ target.next=head1; head1=head1.next; } target=target.next; target.next=null; } if(head1==null)target.next=head2; else target.next=head1; return mergeHead; } //合併兩個有序的單鏈表head1和head2,遞迴 public static Node mergeSortedListRec(Node head1,Node head2){ if(head1==null)return head2; if(head2==null)return head1; if(head1.value>head2.value){ head2.next=mergeSortedListRec(head2.next,head1); return head2; } else{ head1.next=mergeSortedListRec(head1.next,head2); return head1; } } //對單鏈表進行排序,歸併排序,在排序裡面不建議選用遞迴的合併有序連結串列演算法,如果連結串列長度較長,很容易出現棧溢位 public static Node listSort(Node head){ Node nex=null; if(head==null||head.next==null)return head; else if(head.next.next==null){ nex=head.next; head.next=null; } else{ Node mid=getMiddleNode(head); nex=mid.next; mid.next=null; } return mergeSortedList(listSort(head),listSort(nex));//合併兩個有序連結串列,不建議遞迴 } //對單鏈表進行排序,插入排序 public Node insertionSortList(Node head) { if(head==null||head.next==null)return head; Node pnex=head.next; Node pnex_nex=null; head.next=null; while(pnex!=null){ pnex_nex=pnex.next; Node temp=head; Node temp_pre=null; while(temp!=null){ if(temp.value>pnex.value)break; temp_pre=temp; temp=temp.next; } if(temp_pre==null){ head=pnex; pnex.next=temp; } else{ temp_pre.next=pnex; pnex.next=temp; } pnex=pnex_nex; } return head; } //判斷一個單鏈表中是否有環,快慢指標 public static boolean hasCycle(Node head){ boolean flag=false; Node p1=head; Node p2=head; while(p1!=null&&p2!=null){ p1=p1.next; p2=p2.next.next; if(p2==p1){ flag=true; break; } } return flag; } //判斷兩個單鏈表是否相交,如果相交返回第一個節點,否則返回null //如果單純的判斷是否相交,只需要看最後一個指標是否相等 public static Node isIntersect(Node head1,Node head2){ Node target=null; if(head1==null||head2==null)return target; int len1=getListLength(head1); int len2=getListLength(head2); if(len1>=len2){ for(int i=0;i<len1-len2;i++){ head1=head1.next; } }else{ for(int i=0;i<len2-len1;i++){ head2=head2.next; } } while(head1!=null&&head2!=null){ if(head1==head2){ target=head1; break; } else{ head1=head1.next; head2=head2.next; } } return target; } //已知一個單鏈表中存在環,求進入環中的第一個節點,利用hashmap,不要用ArrayList,因為判斷ArrayList是否包含某個元素的效率不高 public static Node getFirstNodeInCycleHashMap(Node head){ Node target=null; HashMap<Node,Boolean> map=new HashMap<Node,Boolean>(); while(head!=null){ if(map.containsKey(head))target=head; else{ map.put(head, true); } head=head.next; } return target; } //已知一個單鏈表中存在環,求進入環中的第一個節點,不用hashmap //用快慢指標,與判斷一個單鏈表中是否有環一樣,找到快慢指標第一次相交的節點,此時這個節點距離環開始節點的長度和連結串列投距離環開始的節點的長度相等 public static Node getFirstNodeInCycle(Node head){ Node fast=head; Node slow=head; while(fast!=null&&fast.next!=null){ slow=slow.next; fast=fast.next.next; if(slow==fast)break; } if(fast==null||fast.next==null)return null;//判斷是否包含環 //相遇節點距離環開始節點的長度和連結串列投距離環開始的節點的長度相等 slow=head; while(slow!=fast){ slow=slow.next; fast=fast.next; }//同步走 return slow; } //給出一單鏈表頭指標head和一節點指標delete,O(1)時間複雜度刪除節點delete //可惜採用將delete節點value值與它下個節點的值互換的方法,但是如果delete是最後一個節點,則不行,但是總得複雜度還是O(1) public static void deleteNode(Node head,Node delete){ //首先處理delete節點為最後一個節點的情況 if(delete==null)return; if(delete.next==null){ if(head==delete)head=null; else{ Node temp=head; while(temp.next!=delete){ temp=temp.next; } temp.next=null; } } else{ delete.value=delete.next.value; delete.next=delete.next.next; } return; } //以k個數為一組,反轉連結串列 public ListNode reverseKGroup(ListNode head, int k) { ListNode curr = head; int count = 0; while (curr != null && count != k) { // find the k+1 node curr = curr.next; count++; } if (count == k) { // if k+1 node is found curr = reverseKGroup(curr, k); // reverse list with k+1 node as head // head - head-pointer to direct part, // curr - head-pointer to reversed part; while (count-- > 0) { // reverse current k-group: 迴圈賦值,head.next -> temp -> head - >cur -> head.next ListNode tmp = head.next; // tmp - next head in direct part head.next = curr; // preappending "direct" head to the reversed list curr = head; // move head of reversed part to a new node head = tmp; // move "direct" head to the next node in direct part } head = curr; } return head; } }