演算法題解 —— 連結串列(11-14)
阿新 • • 發佈:2021-01-31
技術標籤:leetcode 刷題筆記資料結構演算法java連結串列
內容來源於自己的刷題筆記,對一些題目進行方法總結,用 java 語言實現。
11. 將單鏈表的每 K 個節點之間逆序:
-
題目描述:
給定一個單鏈表的頭節點 head,實現一個調整單鏈表的函式,使得每 K 個節點之間逆序,如果不夠 K 個節點一組,則不調整最後幾個節點。
-
解題思路:
-
方法一:利用棧結構
- 從左到右遍歷連結串列,如果棧的大小不等於 K,就將節點不斷壓入棧中
- 當棧的大小第一次達到 K 時,說明第一次湊齊了 K 個節點進行逆序,從棧中依次彈出這些節點,並根據彈出的順序重新連線,這一組逆序後,需要記錄一下新的頭部,同時第一組的最後一個節點(原來是頭節點)應該連線下一個節點
- 當棧的大小每次達到 K 時,說明又湊齊了一組應該逆序的節點,從棧中依次彈出這些節點,並根據彈出的順序重新連線,直到連結串列都被遍歷完
- 最後應該返回 newHead,作為連結串列新的頭節點
-
方法二:在原連結串列上進行調整
用變數記錄每一組開始的第一個節點和最後一個節點,然後直接進行調整,把這一組的節點都逆序。同樣,也是要注意第一組節點的特殊處理。
-
-
程式碼實現:
public class ReverseKNode { private class Node{ public int value; public Node next; public
12. 刪除無序連結串列中值重複出現的節點:
-
題目描述:
給定一個無序單鏈表的頭節點 head,刪除其中值重複出現的節點
-
解題思路:
-
方法一:利用雜湊表
- 生成一個雜湊表,因為頭節點是不需要刪除的節點,所以首先將頭節點的值放入雜湊表
- 從頭節點的下一個節點開始往後遍歷節點,假設遍歷到 cur 節點,先檢查 cur 的值是否在雜湊表中,如果在,則說明 cur 節點的值在之前就出現過,就將 cur 節點刪除,刪除的方式就是將最近一個沒有被刪除的節點 pre 連線到 cur 的下一個節點;如果不是,就將 cur 節點放入雜湊表中
-
方法二:類似選擇排序的過程
定義兩個迴圈,到第一個節點,往後迴圈是否有值相同的節點,有就刪除,以此類推,直到全部刪除完畢
-
-
程式碼實現:
public class RemoveRep { private class Node{ public int value; public Node next; public Node(int value){ this.value = value; } } public void removeRep1(Node head){ if (head == null){ return; } HashSet<Integer> set = new HashSet<>(); Node pre = head; Node cur = pre.next; set.add(head.value); while (cur != null){ if (set.contains(cur.value)){ pre.next = cur.next; }else{ pre = cur; } cur = cur.next; } } public void removeRep2(Node head){ Node cur = head; Node pre = null; Node next = null; while (cur != null){ pre = cur; next = cur.next; while (next != null){ if (cur.value == next.value){ pre.next = next.next; }else { pre = next; } next = next.next; } cur = cur.next; } } }
13. 在單鏈表中刪除指定值的節點:
-
題目描述:
給定一個連結串列的頭節點 head 和一個整數 num,請實現函式將值為 num 的節點全部刪除
-
解題思路:
-
方法一:利用棧結構
將值不等於 num 的節點用棧收集起來,收集完成後重新連線,最後將棧底的節點作為新的頭節點返回
-
方法二:在原連結串列中調整
- 從連結串列頭進行遍歷,找到第一個值不等於 num 的節點,作為新的頭節點,記為 newHead
- 繼續往後遍歷,假設當前節點的值等於 num,就刪除;不等於就更新到最近一個不等於 num 的節點
-
-
程式碼實現:
public class RemoveValue { private class Node{ public int value; public Node next; public Node(int value){ this.value = value; } } public Node removeValue1(Node head,int num){ Stack<Node> stack = new Stack<>(); while (head != null){ if (head.value != num){ stack.push(head); } head = head.next; } while (!stack.isEmpty()){ stack.peek().next = head; head = stack.pop(); } return head; } public Node removeValue2(Node head,int num){ while (head != null){ if (head.value != num){ break; } head = head.next; } Node pre = head; Node cur = head; while (cur != null){ if (cur.value == num){ pre.next = cur.next; }else { pre = cur; } cur = cur.next; } return head; } }
14. 將搜尋二叉樹轉換成雙向連結串列:
-
題目描述:
對二叉樹的節點來說,有本身的值域,有指向左孩子節點和右孩子節點的兩個指標;對雙向連結串列的節點來說,有本身的值域,有指向上一個節點和下一個節點的指標。在結構上,兩種結構有相似性,現在有一棵搜尋二叉樹,請將其轉換成一個有序的雙向連結串列
-
解題思路:
- 方法一:使用佇列和中序遍歷
- 生成一個佇列,記為 queue,按照二叉樹中序遍歷的順序,將每個節點放入 queue 中
- 從 queue 中彈出節點,並按照彈出的順序重新連線所有節點
- 方法二:利用遞迴函式
- 構建一個類,屬性為有序雙向連結串列的頭節點和尾節點
- 實現遞迴函式 process,先把 X 為頭的搜尋二叉樹的左子樹轉換成有序雙向連結串列,並且返回左子樹有序雙向連結串列的頭和尾,然後把以 X 為頭的搜尋二叉樹的右子樹轉換為有序雙向連結串列,並且返回右子樹有序雙向連結串列的頭和尾,接著通過 X 把兩部分連線起來
- 方法一:使用佇列和中序遍歷
-
程式碼實現:
public class Convert { private class Node{ public int value; public Node left; public Node right; public Node(int value){ this.value = value; } } /** * 使用佇列 * @param head * @return */ public Node convert1(Node head){ Queue<Node> queue = new LinkedList<>(); inOrderToQueue(head,queue); if (queue.isEmpty()){ return head; } Node pre = head; pre.left = null; Node cur = null; while (!queue.isEmpty()){ cur = queue.poll(); pre.right = cur; cur.left = pre; pre = cur; } pre.right = null; return head; } public void inOrderToQueue(Node head,Queue<Node> queue){ if (head == null){ return; } inOrderToQueue(head.left,queue); queue.offer(head); inOrderToQueue(head.right,queue); } private class RetrunType{ public Node start; public Node end; public RetrunType(Node start,Node end){ this.start = start; this.end = end; } } public Node convert2(Node head){ if (head == null){ return null; } return process(head).start; } /** * 遞迴函式 * @param head * @return */ public RetrunType process(Node head){ if (head == null){ return new RetrunType(null,null); } RetrunType leftList = process(head.left); RetrunType rightList = process(head.right); if (leftList.end != null){ leftList.end.right = head; } head.left = leftList.end; head.right = rightList.start; if (rightList.start != null){ rightList.start.left = head; } return new RetrunType(leftList.start != null ? leftList.start : head,rightList.end != null ? rightList.end : head); } }