1.定義一個數據類來存儲節點信息
public class Node {
Node next = null;
int data;
public Node(int data){
this.data=data;
}
}
2.單鏈表的基本操作
public class MyLinkedList {
Node 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;
}
/**
* 刪除節點
* @param index
* @return 成功返回true 失敗返回false
*/
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.next!=null){
if (i == index){
preNode.next = curNode.next;
return true;
}
preNode = curNode;
curNode = curNode.next;
i++;
}
return true;
}
/**
* 返回節點的長度
* @return
*/
public int length(){
int length = 0;
Node tmp = head;
while (tmp.next!=null){
length++;
tmp = tmp.next;
}
return length;
}
/**
* 對鏈表進行排序
* @return
*/
public Node orderNode(){
Node nextNode = null;
int temp =0;
Node curNode = head;
while (curNode.next!=null){
nextNode =curNode.next;
while (nextNode != null){
if (curNode.data > nextNode.data){
temp = curNode.data;
curNode.data = nextNode.data;
nextNode.data = temp;
}
nextNode = nextNode.next;
}
curNode = curNode.next;
}
return head;
}
/**
* 打印鏈表
*/
public void printList(){
Node tmp = head;
while(tmp!=null){
System.out.println(tmp.data);
tmp = tmp.next;
}
}
public static void main(String[] args) {
MyLinkedList list = new MyLinkedList();
list.addNode(5);
list.addNode(3);
list.addNode(1);
list.addNode(3);
System.out.println("listLength: "+list.length());
System.out.println("before order:");
list.printList();
list.orderNode();
System.out.println("after order:");
list.printList();
}
}
3.去掉重複的數據
- Hashtable法,通過key鍵去掉重複的數據
/**
* 去重
* @param head
*/
public void deleteDuplecate(Node head){
Hashtable<Integer,Integer> table = new Hashtable<>();
Node tmp = head;
Node preNode = null;
while(tmp!=null){
if (table.containsKey(tmp.data)){
preNode.next = tmp.next;
}else{
table.put(tmp.data,1);
preNode = tmp;
}
tmp=tmp.next;
}
}
- 對鏈表雙重循環,外層進行正常循環,內層循環進行比較,如果於外層相同,就刪掉
/**
* 去重
* @param head
*/
public void deleteDuplecate1(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;
}else{
q=q.next;
}
}
p=p.next;
}
}
4.找出單鏈表中的倒數第k個元素
設置兩個指針,讓其中一個先前移k-1步,然後兩個指針同時往前移動。直到先行的指針指到null,另一個就指到k。
/**
* 找出單鏈表中倒數第k個元素
* @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;
}
5.鏈表的反轉
/**
* 鏈表反轉
* @param head
*/
public void ReverseIteratively(Node head){
Node pReversedHead = head;
Node pNode = head;
Node pPrev = null;
while(pNode !=null){
Node pNext = pNode.next;
if (pNext==null){
pReversedHead = pNode;
}
pNode.next = pPrev;
pPrev = pNode;
pNode = pNext;
}
this.head = pReversedHead;
}
6.從尾到頭輸出單鏈表
用遞歸的方法實現的
/**
* 從尾到頭輸出單鏈表
* @param pListHead
*/
public void printListReversely(Node pListHead){
if (pListHead != null){
printListReversely(pListHead.next);
System.out.println(pListHead.data);
}
}
7.查找單鏈表的中間節點
設置兩個指針從頭遍歷,一個快指針走兩步,一個慢指針走一步。快指針走到頭,慢指針恰好到中間節點
/**
* 尋找單鏈表的中間節點
* @param head
* @return
*/
public Node searchMid(Node head){
Node p =head;
Node q =head;
while(p!= null && p.next!=null &&p.next.next!=null){
p = p.next.next;
q = q.next;
}
return q;
}
8.檢查一個鏈表是否有環及找出第一個入環口
設置兩個指針,fast快指針,走兩步,slow慢指針,走一步。比較兩個指針的值是否相等。當fast與slow的值相同,測試循環鏈表
/**
* 檢測一個鏈表是否有環
* @param head
* @return
*/
public boolean isLoop(Node head){
Node fast = head;
Node slow = head;
if (fast==null){
return false;
}
while(fast!=null && fast.next!=null){
fast = fast.next.next;
slow = slow.next;
if (fast.data==slow.data){
return true;
}
}
return !(fast==null || fast.next==null);
}
如何找到環的入口點呢?
如果單鏈表有環,按照上述思路,當走的快的指針fast與走的慢的指針slow相遇時,slow指針肯定沒有遍歷完鏈表,而fast指針已經在環內循環了n圈 ( n > = 1)。假設slow指針走了 s 步,則fast 指針走了 2s 步 (fast步數還等於 s 加上在環內多轉的 n 圈),設環長爲 r ,滿足如下關係表達式:
2s = s + nr;
s = nr;
設整個鏈表長L,入口環與相遇點距離 x ,起點到環入口點的距離爲a,則滿足如下關係表達式:
a + s = nr;
a + s = (n - 1)r + r = (n - 1)r + L - a ;
a = (n - 1)r + (L - a - x);
(L - a - x)爲相遇點到環入口點的距離,從鏈表頭到環入口點等於(n - 1)循環內環+ 相遇點到環入口點,於是在鏈表頭與相遇點分別設置一個指針,每一次各走一步,兩個指針必定相遇,且遇到的第一個點爲環入口點。
具體代碼如下:
/**
* 尋找循環鏈表的環入口
* @return
*/
public Node findLoopPort(Node head){
Node slow = head;
Node fast = 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;
}
9.在不知道頭結點情況下刪除指定節點
- 如果爲鏈表尾節點,則不能刪除,因爲不能設置前驅節點的next指針置爲空
- 可以交換節點與後續節點的值,然後刪除後續節點。
具體代碼如下:
/**
* 不知道頭結點的情況下,刪除指定節點
* @param n
* @return
*/
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;
}
10.判斷兩個鏈表是否相交及找出第一個相交點
如果兩個鏈表相交,那麼他們的尾結點一定相同
/**
* 判斷兩個鏈表是否相交
* @param h1
* @param h2
* @return
*/
public boolean isIntersect(Node h1,Node h2){
if (h1==null || h2==null){
return false;
}
Node tail1 =h1;
while (tail1!=null){
tail1 = tail1.next;
}
Node tail2 = h2;
while (tail2!=null){
tail2 = tail2.next;
}
return tail1==tail2;
}
如何找出第一個相交的點?
首先分別計算兩個鏈表的長度。長的鏈表先遍歷(len1 - len2)步,使剩下的長度相等,然後兩個鏈表一起遍歷。每一步都比較值是否相等。如果相等,就返回節點。
public static Node getFirstMeetNode(Node h1,Node h2){
if (h1==null||h2==null){
return null;
}
Node tail1 = h1;
int len1=1;
while (tail1!=null){
tail1=tail1.next;
len1++;
}
Node tail2 = h2;
int len2=1;
while (tail2.next!=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;
}