【算法】Java 中链表的基本操作
??鏈表是一種重要的數據結構,在工程項目中廣泛使用。對于鏈表,要搞清楚是否有頭節點,即第一個節點不存任何數據,只是表示鏈表的頭部,而首節點才是鏈表第一個真正存放數據的節點。通常情況下,對有頭節點的鏈表進行操作比較方便。如果原來的鏈表沒有頭節點,可以 new 一個 dummyHead 作為頭節點,這樣往往能減少大量邊界條件的判斷。下面通過幾個例題來學習鏈表。
1.力扣206.反轉鏈表[1]
給你單鏈表的頭節點 head ,請你反轉鏈表,并返回反轉后的鏈表。
示例 1:
輸入:head = [1,2,3,4,5] 輸出:[5,4,3,2,1]反轉鏈表是鏈表中最??嫉降膯栴}??梢允褂妙^插法反轉鏈表。具體解法如下:
class Solution {public ListNode reverseList(ListNode head) {ListNode pre = null; // 首節點while (head != null) {ListNode next = head.next; // 臨時存儲下次要操作的節點head.next = pre; // 頭插法的要點是每次在首節點的前面插入pre = head; // 插入后的節點變成新的首節點head = next; // 取下次要操作的節點}return pre;} }或者定義一個 dummyHead,每次在 dummyHead 的后面插入:
class Solution {public ListNode reverseList(ListNode head) {ListNode dummyHead = new ListNode(0, null);ListNode p = head;while (p != null) {ListNode next = p.next;p.next = dummyHead.next;dummyHead.next = p;p = next;}return dummyHead.next;} }2.力扣92. 反轉鏈表 II[2]
給你單鏈表的頭指針 head 和兩個整數 left 和 right ,其中 left <= right 。請你反轉從位置 left 到位置 right 的鏈表節點,返回 反轉后的鏈表 。
示例 1:
輸入:head = [1,2,3,4,5], left = 2, right = 4 輸出:[1,4,3,2,5]這道題和上一題基本一樣,只是需要先將指針移動到要操作的區間。
class Solution {public ListNode reverseBetween(ListNode head, int left, int right) {// 定義一個啞節點,這樣即使 left = 0 也不需要特殊處理ListNode dummy = new ListNode(0, head);// pre 用來指向要翻轉的第一個節點ListNode pre = dummy;// 將指針移動到要反轉的第一個節點的上一個節點for (int i = 1; i < left; i++) pre = pre.next;// cur 用來指向要翻轉的第一個節點,始終將 cur 的下一個節點進行頭插ListNode cur = pre.next;// 頭插法for (int i = 0; i < right - left; i++) {ListNode next = cur.next; // 將 cur 的下一個節點取出來,準備頭插cur.next = next.next; // cur 連接到后面剩余的節點// 頭插next.next = pre.next;pre.next = next;}return dummy.next;} }具體過程如下:
next = cur.next:
cur.next = next.next:
next.next = pre.next:
pre.next = next:
3.力扣25.K 個一組翻轉鏈表[3]
給你一個鏈表,每 k 個節點一組進行翻轉,請你返回翻轉后的鏈表。k 是一個正整數,它的值小于或等于鏈表的長度。如果節點總數不是 k 的整數倍,那么請將最后剩余的節點保持原有順序。
進階:
你可以設計一個只使用常數額外空間的算法來解決此問題嗎?
你不能只是單純的改變節點內部的值,而是需要實際進行節點交換。
示例 1:
輸入:head = [1,2,3,4,5], k = 2 輸出:[2,1,4,3,5]示例 2:
輸入:head = [1,2,3,4,5], k = 3 輸出:[3,2,1,4,5]若不考慮進階的要求,可以用 Map 來記錄下標:
class Solution {public ListNode reverseKGroup(ListNode head, int k) {if (head == null) return head;int idx = 0;Map<Integer, ListNode> map = new HashMap<>();ListNode p = head;while (p != null) {map.put(idx++, p);p = p.next;}int cnt = idx / k; // idx是節點總數,cnt表示有幾個段要翻轉for (int i = 0; i < cnt; i++) {// 每段第一個節點連接到下一段第一個,防止最后一段不夠k個導致不用翻轉map.get(i * k).next = map.get((i+1) * k);// 從倒數第1個節點翻轉到倒數第k-1個for (int j = 1; j < k; j++) {map.get((i+1) * k - j).next = map.get((i+1) * k - j - 1);}if (i > 0) { // 把前面一段的第一個節點的next連接到當前一段的最后一個節點map.get((i-1) * k).next = map.get((i+1) * k - 1);}}return map.get(k - 1);} }若考慮進階的要求,則不能借助 Map 記錄下標:
class Solution {public ListNode reverseKGroup(ListNode head, int k) {if (head == null || head.next == null){return head;}// dummy->1->2->3->4->5ListNode dummy=new ListNode(0, head);ListNode pre=dummy; // pre指每次要翻轉的鏈表的頭結點的上一個節點ListNode end=dummy; // end指每次要翻轉的鏈表的尾節點while(end.next!=null){//循環k次,找到需要翻轉的鏈表的結尾for(int i=0;i<k&&end != null;i++){end=end.next;}//如果end==null,即需要翻轉的鏈表的節點數小于k,不執行翻轉。if(end==null) break;ListNode next=end.next; // 下一次要翻轉的鏈表end.next=null; //然后斷開鏈表ListNode start=pre.next; //記錄下要翻轉鏈表的頭節點pre.next=reverse(start);//翻轉后頭節點變到最后。通過.next把斷開的鏈表重新鏈接。start.next=next;//將pre和end初始化,換成下次要翻轉的鏈表的頭結點的上一個節點。即startpre=start;end=start;}return dummy.next;}private ListNode reverse(ListNode head) {ListNode pre = null;ListNode curr = head;while (curr != null) {ListNode next = curr.next;curr.next = pre;pre = curr;curr = next;}return pre;} }歡迎關注公眾號。
Reference
[1]力扣206. 反轉鏈表:https://leetcode-cn.com/problems/reverse-linked-list/
[2]力扣92. 反轉鏈表 II:https://leetcode-cn.com/problems/reverse-linked-list-ii/
[3]力扣25.K 個一組翻轉鏈表:https://leetcode-cn.com/problems/reverse-nodes-in-k-group/
總結
以上是生活随笔為你收集整理的【算法】Java 中链表的基本操作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 绘制 频谱图
- 下一篇: Android-腾讯bugly符号表管理