鏈表的定義
鏈表是鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)泣懊,可以用任意一組存儲(chǔ)單元來存儲(chǔ)單鏈表中的數(shù)據(jù)元素吁津,存儲(chǔ)單元可以是不連續(xù)的。除了存儲(chǔ)每個(gè)數(shù)據(jù)元素的值之外晌块,還必須存儲(chǔ)知識(shí)其直接后繼元素的信息。定義如下的數(shù)據(jù)類存儲(chǔ)結(jié)點(diǎn)信息帅霜。
//鏈表結(jié)點(diǎn)的定義
class Node{
Node next = null; // 結(jié)點(diǎn)域
int data; // 數(shù)據(jù)域
public Node(int data){
this.data = data;
}
}
鏈表的創(chuàng)建
給定一個(gè)數(shù)組匆背,用鏈表來存儲(chǔ)這個(gè)數(shù)組。循環(huán)遍歷數(shù)組身冀,把每個(gè)元素鏈接到鏈表中钝尸,最后返回頭結(jié)點(diǎn)。
public class TestNode{
// 輸入一個(gè)整型數(shù)組搂根,創(chuàng)建成鏈表
public Node createLinkList(int[] arr){
Node head = new Node(arr[0]); //鏈表頭
Node r = head; // 指向鏈表尾結(jié)點(diǎn)
for(int i=1; i<arr.length; i++){
Node pNode = new Node(arr[i]); // 新結(jié)點(diǎn)的產(chǎn)生
r.next = pNode; // 鏈表尾部增加新結(jié)點(diǎn)
r = pNode; // r更新為新加的結(jié)點(diǎn)
}
return head;
}
// 返回鏈表的長(zhǎng)度
public int length(Node head) {
int length = 0;
Node tmp = head;
while(tmp != null){
length++;
tmp = tmp.next;
}
return length;
}
// 順序打印鏈表元素信息
public void printLinkList(Node head){
Node p = head;
while(p != null){
System.out.print(p.data+"->");
p = p.next;
}
System.out.println();
}
public static void main(String[] args) {
int[] arr = {1,3,6,8,12,15,20};
TestNode testNode = new TestNode();
Node head = testNode.createLinkList(arr);
testNode.printLinkList(head);
}
}
鏈表的相關(guān)算法
1. 找出單鏈表中的倒數(shù)第k個(gè)元素
設(shè)置兩個(gè)指針p1蝶怔、p2,p1先前移k-1步兄墅,然后p1踢星、p2同時(shí)向前移動(dòng)。循環(huán)直到先行的p1值為NULL時(shí)隙咸,p2所指的位置就是所要找的倒數(shù)第k個(gè)位置沐悦。
//找出單鏈表中的倒數(shù)第k個(gè)元素
public Node findElem(Node head, int k){
if(k < 1 || k > this.length(head))
return null;
Node p1 = head;
Node p2 = head;
for(int i=0; i<k-1; i++) // p1先走k-1步
p1 = p1.next;
while(p1 != null){ // p1,p2同時(shí)走,直到p1為null
p1 = p1.next;
p2 = p2.next;
}
return p2;
}
2. 實(shí)現(xiàn)鏈表的反轉(zhuǎn)
反轉(zhuǎn)一個(gè)鏈表五督,需要調(diào)整指針的指向藏否,具體需要操作3個(gè)相鄰的結(jié)點(diǎn)。
//如何實(shí)現(xiàn)鏈表的反轉(zhuǎn)
public Node ReverseIteratively(Node head){
Node pReverseHead = head;
Node pNode = head;
Node pPrev = null;
while(pNode != null){
Node pNext = pNode.next;
if(pNext == null)
pReverseHead = pNode;
pNode.next = pPrev;
pPrev = pNode;
pNode = pNext;
}
return pReverseHead;
}
3. 尋找單鏈表的中間結(jié)點(diǎn)
定義兩個(gè)指針p充包、q副签,從頭開始遍歷遥椿,p一次走兩步,q一次走一步淆储,p先到鏈表尾部冠场,q則恰好到達(dá)鏈表中部。
// 尋找單鏈表的中間結(jié)點(diǎn)
public Node SearchMid(Node head){
Node p = head;
Node q = head; // 定義兩個(gè)指針
while(p != null && p.next != null && p.next.next != null){
p = p.next.next; // 一個(gè)每次走兩步
q = q.next; // 一個(gè)每次走一步
} // 每次走兩步的指針p到結(jié)尾時(shí)本砰,每次走一步的指針q剛好到中間結(jié)點(diǎn)
return q;
}
4. 合并兩個(gè)有序鏈表碴裙,并使合并后的鏈表也有序
// 合并兩個(gè)有序鏈表,并使合并后的鏈表也有序
public Node mergeLink(Node head1, Node head2){
Node head;
Node p1 = head1;
Node p2 = head2;
if(head1.data < head2.data){
head = new Node(head1.data);
p1 = head1.next;
}else{
head = new Node(head2.data);
p2 = head2.next;
}
Node pNode = head;
while(p1 != null && p2 != null){
if(p1.data > p2.data){
pNode.next = p2;
p2 = p2.next;
}else{
pNode.next = p1;
p1 = p1.next;
}
pNode = pNode.next;
}
if(p1 != null)
pNode.next = p1;
if(p2 != null)
pNode.next = p2;
return head;
}
// 合并兩個(gè)有序鏈表的遞歸方法
public Node mergeLink2(Node head1, Node head2){
if(head1 == null) // 檢測(cè)head1是否為空
return head2;
else if(head2 == null) // 檢測(cè)head2是否為空
return head1;
Node head = null;
if(head1.data < head2.data){
head = head1;
head.next = mergeLink2(head1.next, head2); // 遞歸調(diào)用
}else{
head = head2;
head.next = mergeLink2(head1, head2.next); // 遞歸調(diào)用
}
return head;
}
5. 判斷兩個(gè)鏈表是否相交
如果兩個(gè)鏈表相交点额,那么一定有相同的尾結(jié)點(diǎn)舔株。所以只要判斷尾結(jié)點(diǎn)是否相等。
public boolean isIntersect(Node h1, Node h2){
if(h1 == null || h2 == null)
return false;
Node tail1 = h1;
while(tail1.next != null) // 找到鏈表h1的最后一個(gè)節(jié)點(diǎn)
tail1 = tail1.next;
Node tail2 = h2;
while(tail2.next != null) // 找到鏈表h2的最后一個(gè)節(jié)點(diǎn)
tail2 = tail2.next;
return tail1 == tail2;
}
6. 找到兩個(gè)鏈表相交的第一個(gè)節(jié)點(diǎn)
// 找到兩個(gè)鏈表相交的第一個(gè)節(jié)點(diǎn)
public static Node getFirstMeetNode(Node h1, Node h2){
if(h1 == null || h2 == null)
return null;
Node tail1 = h1;
int len1 = 1;
while(tail1.next != null){ // 找到鏈表h1的最后一個(gè)結(jié)點(diǎn)
tail1 = tail1.next;
len1++;
}
Node tail2 = h2;
int len2 = 1;
while(tail2.next != null){ // 找到鏈表h2的最后一個(gè)結(jié)點(diǎn)
tail2 = tail2.next;
len2++;
}
if(tail1 != tail2) // 兩鏈表不相交
return null;
Node t1 = h1;
Node t2 = h2;
if(len1 > len2){ // 找出較長(zhǎng)的鏈表先遍歷
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;
}
7. 檢測(cè)一個(gè)鏈表是否有環(huán)
兩個(gè)指針slow还棱、fast一個(gè)一次走一步载慈,一個(gè)一次走兩步,如果有環(huán)珍手,它們一定會(huì)相遇办铡。
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 == slow)
return true;
}
return !(fast == null || fast.next == null);
}