算法练习day10——190328(根据指定值划分单链表、复制含有rand指针节点的链表、两个单链表相交)
1.將單向鏈表按某值劃分成左邊小、 中間相等、 右邊大的形式
【題目】 給定一個單向鏈表的頭節點head, 節點的值類型是整型, 再給定一個整數pivot。 實現一個調整鏈表的函數, 將鏈表調整為左部分都是值小于 pivot的節點, 中間部分都是值等于pivot的節點, 右部分都是值大于 pivot的節點。除這個要求外, 對調整后的節點順序沒有更多的要求。 例如: 鏈表9->0->4->5->1, pivot=3。 調整后鏈表可以是1->0->4->9->5, 也可以是0->1->9->5->4。 總之, 滿 足左部分都是小于3的節點, 中間部分都是等于3的節點(本例中這個部分為空) , 右部分都是大于3的節點即可。 對某部分內部的節點順序不做 要求。
進階: 在原問題的要求之上再增加如下兩個要求。在左、 中、 右三個部分的內部也做順序要求, 要求每部分里的節點從左 到右的順序與原鏈表中節點的先后次序一致。 例如: 鏈表9->0->4->5->1, pivot=3。調整后的鏈表是0->1->9->4->5。 在滿足原問題要求的同時, 左部分節點從左到右為0、 1。 在原鏈表中也 是先出現0, 后出現1; 中間部分在本例中為空, 不再討論; 右部分節點 從左到右為9、 4、 5。 在原鏈表中也是先出現9, 然后出現4,最后出現5。如果鏈表長度為N, 時間復雜度請達到O(N), 額外空間復雜度請達到O(1)。
1.1 分析
方法一:可用數組,轉換為荷蘭國旗問題,但是需要額外空間,并且不具有穩定性。
方法二:三個輔助節點:less、equal、more
- 先遍歷一遍:
- 找到第一個小于privot的節點,賦值給less;
- 找到第一個等于privot的節點,賦值給equal;
- 找到第一個大于privot的節點,賦值給more;
- 第二次遍歷時:
- 發現小于privot的,看是不是less,不是,就續在less后面;
- 發現等于privot的,看是不是equal,不是,就續在equal后面;
- 發現大于privot的,看是不是more,不是,就續在more后面;
- 最后,把equal連在less鏈表的后面,more連在equal鏈表的后面。
- 注意便捷問題:某個部分為空。
1.2 代碼實現
此處為方法二的代碼實現:
package Solution;public class ListPartation {public static void main(String[] args) {Node head1 = new Node(7);head1.next = new Node(9);head1.next.next = new Node(1);head1.next.next.next = new Node(8);head1.next.next.next.next = new Node(5);head1.next.next.next.next.next = new Node(2);head1.next.next.next.next.next.next = new Node(5);printLinkedList(head1);head1 = listPartation(head1, 5);printLinkedList(head1);}public static Node listPartation(Node head,int num) {Node help=head;Node next=null;Node less=null;Node equal=null;Node more=null;Node endl=null;Node ende=null;Node endm=null;while(help!=null) {//要斷開,否則連接時后面不好判斷next = help.next;help.next = null;if(help.value<num) {if(less==null) {less=help;endl=less;}else {endl.next=help;endl=endl.next;}}else if(help.value==num) {if(equal==null) {equal=help;ende=equal;}else {ende.next=help;ende=ende.next;}}else {if(more==null) {more=help;endm=more;}else {endm.next=help;endm=endm.next;}}help=next;}//分區//連接Node newhead=null;if(less!=null) {//有lessnewhead=less;if(equal!=null) {//有less/equalendl.next=equal;if(more!=null)//三者都有ende.next=more;else//只有less/equalende.next=null;}else {//有less無equalif(more!=null)//有less/moreendl.next=more;else//只有lessendl.next=null;}}else {//沒有lessif(equal!=null) {//有equalnewhead=equal;if(more!=null)//有equal/moreende.next=more;else//只有equalende.next=null;}else {//無less/equalif(more!=null)//只有morenewhead=more;}}return newhead;}public static void printLinkedList(Node node) {System.out.print("Linked List: ");while (node != null) {System.out.print(node.value + " ");node = node.next;}System.out.println();} }運行結果:
連接時代碼的簡化版:
// small and equal reconnect if (endl != null) {endl.next = equal;ende = ende == null ? endl : ende; } // all reconnect if (ende != null) {ende.next = more; } return less != null ? less : equal != null ? equal : more;注意:分區時,記得斷開原鏈表的next,否則后面再連接時判斷是否為null時就會混亂。——代碼實現中輔助節點next作用就是這個。
2.?復制含有隨機指針節點的鏈表
【題目】 一種特殊的鏈表節點類描述如下:
public class Node {public int value; public Node next;public Node rand;public Node(int data) {this.value = data;} }- Node類中的value是節點值;
- next指針和正常單鏈表中next指針的意義一 樣, 都指向下一個節點;
- rand指針是Node類中新增的指針, 這個指針可能指向鏈表中的任意一個節點, 也可能指向null。
給定一個由Node節點類型組成的無環單鏈表的頭節點head, 請實現一個函數完成這個鏈表中所有結構的復制, 并返回復制的新鏈表的頭節點。
進階:不使用額外的數據結構, 只用有限幾個變量, 且在時間復雜度為 O(N)內完成原問題要實現的函數。
2.1 分析
考慮使用哈希表(<K,V>)結構,<當前節點,它的拷貝節點>。
哈希表的增刪改查均為常數時間。
藍色為next,紅色為rand。
首先建立哈希表,存放<當前節點,它的拷貝節點>:
然后建立關系:
- 取出1和1'。
- 然后根據原鏈表得到1的next——2和rand——3。
- 查哈希表,得到2和3的對應拷貝節點2'和3'。
- 根據原節點建立拷貝節點的next和rand。
2.2 代碼實現
package Solution;import java.util.HashMap;class RNode{int value;RNode next;RNode rand;public RNode(int value) {this.value=value;} } public class RandListCopy {public static void main(String[] args) {RNode head = null;RNode res1 = null;RNode res2 = null;printRandLinkedList(head);res1 = randListCopy(head);printRandLinkedList(res1);res2 = randListCopy(head);printRandLinkedList(res2);printRandLinkedList(head);System.out.println("=========================");head = new RNode(1);head.next = new RNode(2);head.next.next = new RNode(3);head.next.next.next = new RNode(4);head.next.next.next.next = new RNode(5);head.next.next.next.next.next = new RNode(6);head.rand = head.next.next.next.next.next; // 1 -> 6head.next.rand = head.next.next.next.next.next; // 2 -> 6head.next.next.rand = head.next.next.next.next; // 3 -> 5head.next.next.next.rand = head.next.next; // 4 -> 3head.next.next.next.next.rand = null; // 5 -> nullhead.next.next.next.next.next.rand = head.next.next.next; // 6 -> 4printRandLinkedList(head);res1 = randListCopy(head);printRandLinkedList(res1);res2 = randListCopy(head);printRandLinkedList(res2);printRandLinkedList(head);System.out.println("=========================");}public static RNode randListCopy(RNode head) {HashMap<RNode,RNode> map=new HashMap<RNode,RNode>();RNode cur=head; while(cur!=null) {map.put(cur, new RNode(cur.value));cur=cur.next;}cur=head;while(cur!=null) {map.get(cur).next=map.get(cur.next);map.get(cur).rand=map.get(cur.rand);cur=cur.next;}return map.get(head);}public static void printRandLinkedList(RNode head) {RNode cur = head;System.out.print("order: ");while (cur != null) {System.out.print(cur.value + " ");cur = cur.next;}System.out.println();cur = head;System.out.print("rand: ");while (cur != null) {System.out.print(cur.rand == null ? "- " : cur.rand.value + " ");cur = cur.next;}System.out.println();} }運行結果:
上述方法需要O(N)的額外存儲空間(哈希表)。
2.3 分析(不用額外輔助空間的)
將拷貝節點放在原節點的next上。以這種方式省略哈希表的使用。
然后依次取出兩個節點1和1'。
1的rand指向3,則取出3的拷貝節點3'。令1'的rand指向3'。
如此建立2'和3'的rand:
最后分離原鏈表的節點。
設置cur(當前原節點)、next(下一個原節點)、copy(當前原節點的拷貝節點)以及拷貝鏈表的表頭result。
- cur=head;
- result=head.next;
- next=cur.next.next;
- copy=cur.next;
- 確定當前節點拷貝節點的后繼:copy.next=next==null?null:next;
- 當前節點的后繼指向下一個原節點:cur.next=netx;
- 當前節點后移cur=next;
2.4 代碼實現
package Solution;public class RandListCopy {public static RNode randListCopy(RNode head) {if (head == null) {return null;}RNode cur=head;RNode next=null;RNode copy=null;while(cur!=null) {next=cur.next;copy=new RNode(cur.value);cur.next=copy;copy.next=next;cur=next;}cur=head;while(cur!=null) {copy=cur.next;copy.rand=cur.rand==null?null:cur.rand.next;cur=copy.next;}RNode result=head.next;cur=head;while(cur!=null) {//要分離兩個鏈表next=cur.next.next;//下一個原節點copy=cur.next;copy.next=next==null?null:next.next;cur.next=next;cur=next;}return result;} }3.兩個單鏈表相交的一系列問題
【題目】 在本題中, 單鏈表可能有環, 也可能無環。 給定兩個單鏈表的頭節點 head1和head2, 這兩個鏈表可能相交, 也可能不相交。 請實現一個函數, 如果兩個鏈表相交, 請返回相交的第一個節點; 如果不相交, 返回null 即可。
要求: 如果鏈表1的長度為N, 鏈表2的長度為M, 時間復雜度請達到 O(N+M), 額外空間復雜度請達到O(1)。
3.1 判斷一個單鏈表是否有環
有環:返回第一個入環節點;
無環:返回空。
3.1.1 方法一:使用哈希表
用HashSet,只需要存key。
遍歷鏈表,把當前遍歷到的節點存入表。存之前需查看表中是否已有這個節點。
- 有,鏈表存在環,且這個已存在的節點是環的第一個入口節點;
- 沒有,繼續遍歷;
- 最終next為空,則說明沒有環。
3.1.2 方法二:使用兩個指針
快指針F和慢指針S:
- F每次走兩步;
- S每次走一步。
兩個相遇時,肯定有環,此時F回到head處,一次走一步,當F與S再次相遇時,肯定是入環的頭一個節點。
若F首先碰到空,則無環。
對head1和head2都調用判斷是否有環的這個方法,
- 若都返回null:判斷兩個無環鏈表是否相交。
- 若一個有環,一個無環:不可能相交。
- 兩個都有環:判斷兩個有環的鏈表是否相交。
3.2?判斷兩個無環鏈表是否相交
3.2.1 方法一:用Map
將鏈表1的所有節點放到map中;
遍歷鏈表2,對于鏈表2的每一個節點,都查這個節點是否在map中:
- 第一個在的,就是第一個相交的節點;
- 若鏈表2已經遍歷到空,還沒有已存在于map中的節點,說明不相交。
3.2.2 方法二:
遍歷鏈表1,得到其長度len1,以及拿到最后一個節點end1;
遍歷鏈表2,得到其長度len2,以及拿到最后一個節點end2;
判斷end1和end2是否相等,本題比較的都是內存地址(是否為同一個節點)。
- end1≠end2,不可能相交。
- end1=end2,說明相交,但這個點未必是第一個相交的節點。需要繼續處理:
- 若len1=100,len2=80,則head1先走20步;
- 然后head1和head2一起走,他們一定可以一塊走到相交的節點處。
3.3?判斷兩個有環的鏈表是否相交
有三種拓撲結構:
若loop1==loop2(入環節點一樣),則是第二種情況;
- 截斷環,如下圖:
- 變為了3.2
若loop1!=loop2,可能是結構①,也可能是結構③。
區分方法:
- 讓loop1一直往下走(取next);
- 若loop1都走一圈走到自己了,還沒遇到loop2,則是結構1;
- 若遇到loop2,則是結構3。
- 返回loop1或loop2都行
- loop1是距head1最近的相交的節點;
- loop2是距head2最近的相交的節點。
3.4 代碼實現
package Solution;import Solution.Test.Node;public class GetIntersectNode {public static void main(String[] args) {// 1->2->3->4->5->6->7->nullNode head1 = new Node(1);head1.next = new Node(2);head1.next.next = new Node(3);head1.next.next.next = new Node(4);head1.next.next.next.next = new Node(5);head1.next.next.next.next.next = new Node(6);head1.next.next.next.next.next.next = new Node(7);// 0->9->8->6->7->nullNode head2 = new Node(0);head2.next = new Node(9);head2.next.next = new Node(8);head2.next.next.next = head1.next.next.next.next.next; // 8->6System.out.println(getIntersectNode(head1, head2).value);//輸出6// 1->2->3->4->5->6->7->4...head1 = new Node(1);head1.next = new Node(2);head1.next.next = new Node(3);head1.next.next.next = new Node(4);head1.next.next.next.next = new Node(5);head1.next.next.next.next.next = new Node(6);head1.next.next.next.next.next.next = new Node(7);head1.next.next.next.next.next.next = head1.next.next.next; // 7->4// 0->9->8->2...head2 = new Node(0);head2.next = new Node(9);head2.next.next = new Node(8);head2.next.next.next = head1.next; // 8->2System.out.println(getIntersectNode(head1, head2).value);//輸出2// 0->9->8->6->7->4->5->6..head2 = new Node(0);head2.next = new Node(9);head2.next.next = new Node(8);head2.next.next.next = head1.next.next.next.next.next; // 8->6System.out.println(getIntersectNode(head1, head2).value);//輸出6或4}public static Node getIntersectNode(Node head1,Node head2) {if(head1==null||head2==null)return null;Node loop1=getLoopNode(head1);Node loop2=getLoopNode(head2);if(loop1==null&&loop2==null)//都無環,3.1return noLoop(head1,head2);if(loop1!=null&&loop2!=null)//都有環,三種結構return bothLoop(head1,loop1,head2,loop2);return null;}public static Node getLoopNode(Node head) {if(head==null||head.next==null||head.next.next==null)return null;Node quick=head.next.next;Node slow=head.next;while(quick!=slow) {if(quick.next==null||quick.next.next==null)//無環return null;quick=quick.next.next;slow=slow.next;}//快指針和慢指針相遇,有環quick=head;while(quick!=slow) { quick=quick.next;slow=slow.next;} return quick;}public static Node noLoop(Node head1,Node head2) {int len=0;Node cur1=head1;Node cur2=head2;while(cur1.next!=null) {len++;cur1=cur1.next;}while(cur2.next!=null) {len--;cur2=cur2.next;}if(cur1!=cur2)//末尾節點不在一塊,肯定不相交return null;//cur1為長鏈表cur1=len>0?head1:head2;cur2=cur1==head1?head2:head1;len=Math.abs(len);while(len>0) {cur1=cur1.next;len--;}while(cur1!=cur2) {cur1=cur1.next;cur2=cur2.next;}return cur1;}public static Node bothLoop(Node head1,Node loop1,Node head2,Node loop2) {Node cur1=head1;Node cur2=head2;if(loop1==loop2) {//結構2int len=0;while(cur1!=loop1) {len++;cur1=cur1.next;}while(cur2!=loop2) {len--;cur2=cur2.next;}//cur1為長鏈表cur1=len>0?head1:head2;cur2=cur1==head1?head2:head1;len=Math.abs(len);while(len>0) {cur1=cur1.next;len--;}while(cur1!=cur2) {cur1=cur1.next;cur2=cur2.next;}return cur1;}else {//判斷結構1還是結構3cur1=loop1.next;while(cur1!=loop2) {if(cur1==loop1)//沒遇到loop2,先遇到自己,結構1,return null;cur1=cur1.next; }//遇到loop2,結構3return loop1;}} }運行結果:
三種示例結構為:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
?
?
總結
以上是生活随笔為你收集整理的算法练习day10——190328(根据指定值划分单链表、复制含有rand指针节点的链表、两个单链表相交)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 算法练习day9——190327(“之”
- 下一篇: 算法练习day10——190328(二叉