玩轉資料結構——第四章:連結串列和遞迴
阿新 • • 發佈:2018-11-04
內容概要:
- Leetcode中和連結串列相關的問題
- 測試自己的Leetcode連結串列程式碼
- 遞迴繼承與遞迴的巨集觀語意
- 連結串列的天然遞迴結構性質
- 遞迴執行機制:遞迴的微觀解讀
- 遞迴演算法的除錯
- 更多和連結串列相關的問題
1-Leetcode中和連結串列相關的問題
題目:
刪除連結串列中等於給定的val的所有元素。
示例:
給定:1—>2—>6—>3—>4—>5—>6,val=6
返回:1—>2—>3—>4—>5
給定的ListNode類
public class ListNode { public int val; public ListNode next; public ListNode(int x) { val = x; } }
問題分析:
- 第一種解決方案:連結串列沒有設定虛擬頭結點
首先,判斷連結串列是否為空(head!=null)為空則返回head,不為空執行下面刪除操作
處理特殊位置,連結串列頭部的刪除工作:如果head是val值(head.val=val)
ListNode delNode=head;
head=head.next;
delNode.next=null;
然後刪除中間位置的方法:
定義prev為要刪除節點的前一個節點
ListNode prev=head;
prev.next下一個節點不為空則且prve.next.val==val則執行刪除操作
ListNode delNode=prev.next;//定義要刪除的節點
prev.next=delNode.next;
delNode.next=null;
否則,prev=prev.next;繼續往下找
class Solution { public ListNode removeElements(ListNode head, int val) { //1假設head節點的val等於給定的val //1解決頭結點的刪除 while (head != null && head.val == val) { //定義要刪除的那個節點 ListNode delNode = head; head = head.next; delNode.next = null; } //如果頭結點為空 if (head == null) return null; //2解決中間部分的刪 ListNode prev = head;//頭結點為下一個節點的前一個節點 while (prev.next != null) {//下一個節點不為空 if (prev.val == val) {///中間部分的某一個節點的val等於給定val ListNode delNode = prev.next; prev.next = delNode.next; delNode.next = null; } else {//prev往下找 prev = prev.next; } } return head; } }
- 第二種解決方案:連結串列設定虛擬頭結點
在連結串列的頭部設定一個虛擬頭結點,則除了頭部刪除和中間刪除都是同一個操作
class Solution2 {
public ListNode removeElements(ListNode head, int val) {
ListNode dummyhead = new ListNode(-1);//設定虛擬節點,值任意
dummyhead.next = head;//虛擬節點成頭結點
//解決頭結點和中間部分的刪
ListNode prev = dummyhead;//頭結點為下一個節點的前一個節點
while (prev.next != null) {//下一個節點不為空
if (prev.next.val == val) {///中間部分的某一個節點的val等於給定val
// ListNode delNode = prev.next;
// prev.next = delNode.next;
// delNode.next = null;
prev.next = prev.next.next;//和上面三句等效
} else {//prev往下找
prev = prev.next;
}
}
return dummyhead.next;//虛擬頭結點不展示
}}
2-測試自己的Leetcode連結串列程式碼
在給定的ListNode類中,寫一個帶參建構函式,引數傳進來一個整形陣列,轉成連結串列
public class ListNode {
public int val;
public ListNode next;
public ListNode(int x) {
val = x;
}
//連結串列節點的建構函式
//使用arr為引數。建立一個連結串列。當前的ListNode為連結串列的頭結點
public ListNode(int[] arr) {
if (arr == null || arr.length == 0)
throw new IllegalArgumentException("arr cannot be empt");
this.val = arr[0];
ListNode cur = this;//this用來儲存cur的狀態 //裡面記錄的所以cur的狀態1->NUll /1->2->NULL/1->2->3-NULL....
for (int i = 1; i < arr.length; i++) {
cur.next = new ListNode(arr[i]);//每次傳入新的引數生成新的物件,賦值給cur.next
cur = cur.next;//將cur.next傳給cur作為新的cur的位置,傳遞
}
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
ListNode cur = this;//this通過上面的方法村裡
while (cur != null)
{
res.append(cur.val + "->");
cur = cur.next;
}
res.append("NULL");
return res.toString();
}
}
在Solution中實現測試
public static void main (String[] args){
int[] nums={1,2,3,6,4,5,6};
//利用帶參建構函式將陣列變成新的連結串列
ListNode node=new ListNode(nums);
System.out.println(node);
ListNode newNode=new Solution().removeElements(node,6);
System.out.println(newNode);
}
結果:
1->2->3->6->4->5->6->NULL
1->2->3->4->5->NULL
3-遞迴繼承與遞迴的巨集觀語意
遞迴:本質上,將原來的問題,轉化為更小的同一問題
/**
* 採用遞迴的方式來計算陣列的和
*/
public class Sum {
public static int sum(int[] arr) {
return sum(arr, 0);//遞迴的呼叫,從0到n-1的和
}
//真正的遞迴
//計算arr[l,n)這個區間內所有數字的和
protected static int sum(int[] arr, int l) {
if (l == arr.length)
return 0;
//解決的問題規模變小了一層一層的呼叫(計算【l+1,n)的和...計算[n-1,n)的和)
return arr[l] + sum(arr, l + 1);
}}
測試函式: 結果36
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
System.out.println(sum(arr));
}
4-連結串列的天然遞迴結構性質
用遞迴演算法的兩個步驟:
- 1.先求解最基本問題
- 2.把原問題轉化成更小的問題
用遞迴的演算法解決刪除連結串列元素中為e的值
/**
* 使用遞迴解決刪除連結串列中為e的元素
*/
class Solution2 {
public ListNode removeElements(ListNode head, int val) {
//第一步:解決最基本的問題
if (head == null)
return null;
head.next = removeElements(head.next, val);
return head.val == val ? head.next : head;
}
public static void main(String[] args) {
int[] nums = {1, 2, 3, 6, 4, 5, 6};
//利用帶參建構函式將陣列變成新的連結串列
ListNode node = new ListNode(nums);
System.out.println(node);
ListNode newNode = new Solution2().removeElements(node, 6);
System.out.println(newNode);
}
}
5-遞迴執行機制:遞迴的微觀解讀
如果不解決函式的基本問題(最後那一層,head==null),遞迴將永遠進行下去,直到佔滿系統棧的空間。
6-遞迴演算法的除錯
使用列印輸出的方式來理解從連結串列移除val元素的值的執行過程
/**
* 使用列印輸出的方式來輸出連結串列移除元素val的值的執行過程
*/
class Solution2 {
public ListNode removeElements(ListNode head, int val, int depth) {
String depthString = generateDepthString(depth);
System.out.print(depthString);
System.out.println("Call:remove " + val + " in " + head);
//第一步:解決最基本的問題
if (head == null) {
System.out.print(depthString);
System.out.println("Return:remove " + head);
return null;
}
ListNode res = removeElements(head.next, val, depth + 1);
System.out.print(depthString);
System.out.println("After:remove " + val + " in " + res);
ListNode ret;//暫存
if (head.val == val)
ret = res;
else
head.next = res;
ret = head;
System.out.print(depthString);
System.out.println("Return: " + ret);
return ret;
}
private String generateDepthString(int depth) {
StringBuilder res = new StringBuilder();
for (int i = 0; i < depth; i++) {
res.append("-|");
}
return res.toString();
}
public static void main(String[] args) {
int[] nums = {1, 2, 3, 6, 4, 5, 6};
//利用帶參建構函式將陣列變成新的連結串列
ListNode node = new ListNode(nums);
System.out.println(node);
ListNode newNode = new Solution2().removeElements(node, 6, 0);
System.out.println(newNode);
}
}
輸出結果:
1->2->3->6->4->5->6->NULL
Call:remove 6 in 1->2->3->6->4->5->6->NULL
-|Call:remove 6 in 2->3->6->4->5->6->NULL
-|-|Call:remove 6 in 3->6->4->5->6->NULL
-|-|-|Call:remove 6 in 6->4->5->6->NULL
-|-|-|-|Call:remove 6 in 4->5->6->NULL
-|-|-|-|-|Call:remove 6 in 5->6->NULL
-|-|-|-|-|-|Call:remove 6 in 6->NULL
-|-|-|-|-|-|-|Call:remove 6 in null
-|-|-|-|-|-|-|Return:remove null
-|-|-|-|-|-|After:remove 6 in null
-|-|-|-|-|-|Return: 6->NULL
-|-|-|-|-|After:remove 6 in 6->NULL
-|-|-|-|-|Return: 5->6->NULL
-|-|-|-|After:remove 6 in 5->6->NULL
-|-|-|-|Return: 4->5->6->NULL
-|-|-|After:remove 6 in 4->5->6->NULL
-|-|-|Return: 6->4->5->6->NULL
-|-|After:remove 6 in 6->4->5->6->NULL
-|-|Return: 3->6->4->5->6->NULL
-|After:remove 6 in 3->6->4->5->6->NULL
-|Return: 2->3->6->4->5->6->NULL
After:remove 6 in 2->3->6->4->5->6->NULL
Return: 1->2->3->6->4->5->6->NULL
1->2->3->6->4->5->6->NULL
7-更多和連結串列相關的問題
(轉自發條魚)