資料結構和演算法-常見演算法題
參考:
連結串列反轉:
https://blog.csdn.net/weixin_40807247/article/details/91435275
https://blog.csdn.net/qq_33958946/article/details/84326965
https://blog.csdn.net/qq_37117521/article/details/80808631
連結串列反轉
理解單鏈表的反轉(java實現)
遍歷法
遍歷法就是在連結串列遍歷的過程中將指標順序置換
//如何實現連結串列的反轉 public static Node ReverseIteratively(Node head){ Node pre = null; Node next = null; while (head != null) { next = head.next; head.next = pre; pre = head; head = next; } return pre; }
依舊是1->2->3->4
準備兩個空結點 pre用來儲存先前結點、next用來做臨時變數
在頭結點node遍歷的時候此時為1結點
next = 1結點.next(2結點)
1結點.next=pre(null)
pre = 1結點
node = 2結點
進行下一次迴圈node=2結點
next = 2結點.next(3結點)
2結點.next=pre(1結點)=>即完成2->1
pre = 2結點
node = 3結點
進行迴圈…
遞迴法
總體來說,遞迴法是從最後一個Node開始,在彈棧的過程中將指標順序置換的。
遞迴實質上就是系統幫你壓棧的過程,系統在壓棧的時候會保留現場。
我們來看是怎樣的一個遞迴過程:1->2->3->4
程式到達Node newHead = reverse(head.next);時進入遞迴
我們假設此時遞迴到了3結點,此時head=3結點,temp=3結點.next(實際上是4結點)
執行Node newHead = reverse(head.next);傳入的head.next是4結點,返回的newHead是4結點。
接下來就是彈棧過程了
程式繼續執行 temp.next = head就相當於4->3
head.next = null 即把3結點指向4結點的指標斷掉。
返回新連結串列的頭結點newHead
//遞迴實質上就是系統幫你壓棧的過程,系統在壓棧的時候會保留現場 public Node reverse(Node head) { //遞迴 if (null == head ||null == head.next) return head; Node temp = head.next; Node newHead = reverse(head.next); temp.next = head; head.next = null; return newHead; }
@Test
public void testCase() {
LinkedList list = new LinkedList();
System.out.println("測試頭插法");
list.addNodeHead(4);
list.addNodeHead(3);
list.display();
list.addNodeHead(2);
list.addNodeHead(1);
/*list.display();
list.addNodeTail(4);
list.display();
System.out.println("測試某個位置刪除法");
list.deleteNode(3);
list.display();
System.out.println("測試刪除重複資料前");
list.addNodeHead(1);
list.display();
System.out.println("測試刪除重複資料");
list.deleteDuplecate1(list.head);
list.display();
System.out.println("測試刪除重複資料1");
list.addNodeIndex(2,1);
list.display();
list.deleteDuplecate(list.head);*/
list.display();
System.out.println("連結串列的反轉");
//Node node = LinkedList.ReverseIteratively(list.head);
Node node1 = list.reverse(list.head);
while (node1 != null){
System.out.println(node1.data);
node1 = node1.next;
}
}
java經典面試題:單鏈表反轉問題詳解(含遞迴法)
java經典面試題:單鏈表反轉問題,有兩種方法,一種為迴圈遍歷法,一種遞迴法。
1、迴圈遍歷法
首先設定三個節點,把當前節點的下一節點指向它前面的節點,此時你會發現指標鏈會斷,所以要先把它後面一個節點用nextNode儲存下來,之後把節點向後移動遍歷即可。
程式碼如下:
//定義單鏈表節點類
public class ListNode {
int value;
ListNode next;
public ListNode() {
this.value=0;
this.next=null;
}
public ListNode(int value) {
this.value=value;
}
}
//定義連結串列反轉方法
public ListNode reverseList(ListNode root) {
if(root==null)return root;
ListNode newhead=null; //反轉後的新連結串列頭結點
ListNode preNode = null; //前一節點
ListNode nextNode=null; //下一節點
ListNode curNode=root; //當前節點
while(curNode.next!=null) {
nextNode=curNode.next; //儲存當前節點的下一節點
curNode.next=preNode; //使當前節點指向前一節點
preNode=curNode; //把前一節點往後移
curNode=nextNode; //把當前節點往後移
}
newhead=curNode;
return newhead;
}
2、遞迴法
如果當前節點或者當前節點的下一節點為空,直接返回該節點;
否則使該節點的後一節點指向該節點,以此遞迴。
public BinaryNode reverseList2(BinaryNode root) {
if(root==null||root.next==null)return root;
BinaryNode nextNode=root.next; //得到當前節點的下一節點
root.next=null; //打斷當前指標鏈
BinaryNode re=reverseList2(nextNode); //每次遞迴下一節點進行反轉
nextNode.next=root; //反轉指標域
return re;
}
使用遞迴反轉連結串列
連結串列反轉:
(圖1)
把問題規模減小,並且減小的量為1
(圖2)
假設我們的程式能夠正常的反轉:則反轉後為
(圖3)
反轉後,1元素並沒有任何操作,所以反轉1的next仍然指向2,
(圖4)
假設2開頭的連結串列已經反轉成功,接下來只要將2的next指向1,
(圖5)
而1的next指向null即可。
(圖6)
看似複雜的問題,把如此多的指標反過來指,其實只要將兩個節點反過來即可。
程式碼如下:
package com.sise.recursion; import java.util.ArrayList; import java.util.Arrays; public class LinkedListReverser { /* * 反轉一個連結串列 * head為待反轉連結串列的頭結點 * @return 反轉後的連結串列頭結點,當然該連結串列也是以null結尾 */ public Node reverseLinkedList(Node head) { /* *特殊處理 */ // //空連結串列,sise==0 // if(head==null){ // return null; // } // //只有一個結點的時候,size==1 // if(head.getNext()==null){ // return head; // } //把上兩個特殊情況合起來 if(head==null||head.getNext()==null){ return head; } //假設函式能夠反轉連結串列,返回頭結點 //---------------此處head有可能是null,head。getNext()有可能是null----------- Node newHead=reverseLinkedList(head.getNext()); //此時如圖4狀態,1的getNext就是第二個結點2, //把第二結點2的next指向head則實現把2的指標指向1,如圖5 //------------此處的getNext()有可能是null------ head.getNext().setNext(head); head.setNext(null);//最後指向null,如圖6 return newHead; } public static void main(String[] args) { LinkedListCreator creator=new LinkedListCreator(); LinkedListReverser reverser=new LinkedListReverser(); ArrayList arrayList=new ArrayList<>(); Node.printLinkedList( reverser.reverseLinkedList(creator.createLinkedList(arrayList)) ); Node.printLinkedList( reverser.reverseLinkedList(creator.createLinkedList(Arrays.asList(1))) ); Node.printLinkedList( reverser.reverseLinkedList(creator.createLinkedList(Arrays.asList(1,2,3,4,5))) ); } }
package com.sise.recursion; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class LinkedListCreator { /* * 建立一個連結串列 * @return 連結串列的頭結點,返回連結串列的最後一個結點的getNext()==null. */ public Node createLinkedList(List<Integer> data){ //假設傳入空的List if(data.isEmpty()){ return null; } //取出傳入資料的第一個結點 Node firstNode=new Node(data.get(0)); //取走一個元素後,從第二個元素建立一個連結串列, //因為返回的是Node,所以用Node來接收 //假設傳入來的List有一個元素,則走到這裡時sublist傳入的兩個引數相等 //但是sublist函式的定義可以看到fromIndex==toIndex時,返回null /* * <tt>fromIndex</tt> and <tt>toIndex</tt> are equal, the returned list is */ //與我們期望返回值一致 // Node headOfSublistNode= // createLinkedList(data.subList(1, data.size())); // //第一個結點的next指向規模縮小的連結串列返回來的頭結點 // firstNode.setNext(headOfSublistNode); //上面兩行程式碼清理成如下程式碼 firstNode.setNext(createLinkedList(data.subList(1, data.size()))); return firstNode; } public static void main(String[] args) { LinkedListCreator creator=new LinkedListCreator(); ArrayList arrayList=new ArrayList<>(); Node.printLinkedList( creator.createLinkedList(arrayList) ); Node.printLinkedList( creator.createLinkedList(Arrays.asList(1)) ); Node.printLinkedList( creator.createLinkedList(Arrays.asList(1,2,3,4,5)) ); } }
package com.sise.recursion; public class Node { private final int value;//使用者定義之後就不能修改 private Node next; public Node(int value){ this.value=value; this.next=null;//這樣建立出來的結點都是單點Node } public Node getNext() { return next; } public void setNext(Node next) { this.next = next; } public int getValue() { return value; } //列印函式 public static void printLinkedList(Node head) { while(head!=null){ System.out.print(head.getValue());; System.out.print(" "); head=head.getNext(); } System.out.println(); } }
執行結果: