左神算法:将单链表的每K个节点之间逆序(Java版)
本題來自左神《程序員代碼面試指南》“將單鏈表的每K個節點之間逆序”題目。
題目
給定一個單鏈表的頭節點head,實現一個調整單鏈表的函數,使得每K 個節點之間逆序,如果最后不夠K 個節點一組,則不調整最后幾個節點。
例如:
鏈表:1->2->3->4->5->6->7->8->null,K=3。
調整后為:3->2->1->6->5->4->7->8->null。其中7、8 不調整,因為不夠一組。
題解
首先,如果K 的值小于2,不用進行任何調整。因為K<1 沒有意義,K==1 時,代表每1 個節點為1 組進行逆序,原鏈表也沒有任何變化。接下來介紹兩種方法,如果鏈表長度為N,方法一的時間復雜度為O(N),額外空間復雜度為O(K)。方法二的時間復雜度為O(N),額外空間復雜度為O(1)。本題考查面試者代碼實現不出錯的能力。
方法一:利用棧結構的解法。
1.從左到右遍歷鏈表,如果棧的大小不等于K,就將節點不斷壓入棧中。
2.當棧的大小第一次到達K 時,說明第一次湊齊了K 個節點進行逆序,從棧中依次彈出這些節點,并根據彈出的順序重新連接,這一組逆序完成后,需要記錄一下新的頭部,同時第一組的最后一個節點(原來是頭節點)應該連接下一個節點。
例如:鏈表 1->2->3->4->5->6->7->8->null,K = 3
第一組節點進入棧,從棧頂到棧底依次為 3 , 2 , 1 。逆序重連之后為 3->2->1->… , 然后節點 1 去連接節點 4 , 鏈表變為 3->2->1->4->5->6->7->8->null,之后從節點 4 開始不斷處理 K 個節點為一組的后續情況,也就是步驟3,并且需要記錄節點3,因為鏈表的頭部已經改變,整個過程結束后需要返回這個新的頭節點,記為newHead。
3.步驟2 之后,當棧的大小每次到達K 時,說明又湊齊了一組應該進行逆序的節點,從棧中依次彈出這些節點,并根據彈出的順序重新連接。這一組逆序完成后,該組的第一個節點(原來是該組最后一個節點)應該被上一組的最后一個節點連接上,這一組的最后一個節點(原來是該組第一個節點)應該連接下一個節點。然后繼續去湊下一組,直到鏈表都被遍歷完。
例如:鏈表3->2->1->4->5->6->7->8->null,K = 3,第一組已經處理完。第二組從棧頂到棧底依次為6,5,4。逆序重連之后為6->5->4,然后節點6 應該被節點1 連接,節點4 應該連接節點7,鏈表變為3->2->1->6->5->4->7->8->null。然后繼續從節點7 往下遍歷。
4.最后應該返回newHead,作為鏈表新的頭節點。
方法一的具體實現請參看如下代碼中的 reverseKNodes1 方法。
/*** 方法一:借助棧*/ public static Node reverseKNodes1(Node head, int K) {if (K < 2) {return head;}Stack<Node> stack = new Stack<Node>();Node newHead = head;Node cur = head;Node pre = null;Node next = null;while (cur != null) {next = cur.next;stack.push(cur);if (stack.size() == K) { // 湊齊一組pre = resign1(stack, pre, next); // 將棧中節點重新連接實現反轉newHead = newHead == head ? cur : newHead; // 僅第一組節點滿足 newHead == head,用于設置新頭結點為反轉后的返回節點。對于此后其他組,這一判斷均形同虛設。}cur = next;}return newHead; }/*** 將stack中的節點彈出并連接在left和right中間,形成形如 left -> (...stack...) -> right 的鏈表* @return 反轉后right節點的前驅結點*/ public static Node resign1(Stack<Node> stack, Node left, Node right) {Node cur = stack.pop();if (left != null) {left.next = cur;}Node next = null;while (!stack.isEmpty()) {next = stack.pop();cur.next = next;cur = next;}cur.next = right;return cur; }方法二:不需要棧結構,在原鏈表中直接調整。
用變量記錄每一組開始的第一個節點和最后一個節點,然后直接逆序調整,把這一組的節點都逆序。和方法一一樣,同樣需要注意第一組節點的特殊處理,以及之后的每個組在逆序重連之后,需要讓該組的第一個節點(原來是最后一個節點)被之前組的最后一個節點連接上,將該組的最后一個節點(原來是第一個節點)連接下一個節點。
方法二的具體實現請參看如下代碼中的 reverseKNodes2 方法。
/*** 方法二:在鏈表中原地翻轉*/ public static Node reverseKNodes2(Node head, int K) {if (K < 2) {return head;}Node cur = head;Node start = null;Node pre = null;Node next = null;int count = 1;while (cur != null) {next = cur.next;if (count == K) { // 湊齊一組start = pre == null ? head : pre.next;head = pre == null ? cur : head;resign2(pre, start, cur, next); // 反轉當前組。參數分別為:前驅,起始,終止,后繼pre = start;count = 0;}count++;cur = next;}return head; }public static void resign2(Node left, Node start, Node end, Node right) {Node pre = start;Node cur = start.next;Node next = null;while (cur != right) { // 反轉鏈表標準操作next = cur.next;cur.next = pre;pre = cur;cur = next;}if (left != null) {left.next = end;}start.next = right; }附:全部代碼(包含測試用例)
package chapter_2_listproblem;import java.util.Stack;public class Problem_12_ConvertEveryKNodesInList {public static class Node {public int value;public Node next;public Node(int data) {this.value = data;}}/*** 方法一:借助棧*/public static Node reverseKNodes1(Node head, int K) {if (K < 2) {return head;}Stack<Node> stack = new Stack<Node>();Node newHead = head;Node cur = head;Node pre = null;Node next = null;while (cur != null) {next = cur.next;stack.push(cur);if (stack.size() == K) { // 湊齊一組pre = resign1(stack, pre, next); // 將棧中節點重新連接實現反轉newHead = newHead == head ? cur : newHead; // 僅第一組節點滿足 newHead == head,用于設置新頭結點為反轉后的返回節點。對于此后其他組,這一判斷均形同虛設。}cur = next;}return newHead;}/*** 將stack中的節點彈出并連接在left和right中間,形成形如 left -> (...stack...) -> right 的鏈表* @return 反轉后right節點的前驅結點*/public static Node resign1(Stack<Node> stack, Node left, Node right) {Node cur = stack.pop();if (left != null) {left.next = cur;}Node next = null;while (!stack.isEmpty()) {next = stack.pop();cur.next = next;cur = next;}cur.next = right;return cur;}/*** 方法二:在鏈表中原地翻轉*/public static Node reverseKNodes2(Node head, int K) {if (K < 2) {return head;}Node cur = head;Node start = null;Node pre = null;Node next = null;int count = 1;while (cur != null) {next = cur.next;if (count == K) { // 湊齊一組start = pre == null ? head : pre.next;head = pre == null ? cur : head;resign2(pre, start, cur, next); // 反轉當前組。參數分別為:前驅,起始,終止,后繼pre = start;count = 0;}count++;cur = next;}return head;}public static void resign2(Node left, Node start, Node end, Node right) {Node pre = start;Node cur = start.next;Node next = null;while (cur != right) { // 反轉鏈表標準操作next = cur.next;cur.next = pre;pre = cur;cur = next;}if (left != null) {left.next = end;}start.next = right;}// for testpublic static void printLinkedList(Node head) {System.out.print("Linked List: ");while (head != null) {System.out.print(head.value + " ");head = head.next;}System.out.println();}public static void main(String[] args) {Node head = null;int K = 3;printLinkedList(head);head = reverseKNodes1(head, K);printLinkedList(head);head = reverseKNodes2(head, K);printLinkedList(head);System.out.println("=======================");head = new Node(1);K = 3;printLinkedList(head);head = reverseKNodes1(head, K);printLinkedList(head);head = reverseKNodes2(head, K);printLinkedList(head);System.out.println("=======================");head = new Node(1);head.next = new Node(2);K = 2;printLinkedList(head);head = reverseKNodes1(head, K);printLinkedList(head);head = reverseKNodes2(head, K);printLinkedList(head);System.out.println("=======================");head = new Node(1);head.next = new Node(2);K = 3;printLinkedList(head);head = reverseKNodes1(head, K);printLinkedList(head);head = reverseKNodes2(head, K);printLinkedList(head);System.out.println("=======================");head = new Node(1);head.next = new Node(2);head.next.next = new Node(3);head.next.next.next = new Node(4);K = 2;printLinkedList(head);head = reverseKNodes1(head, K);printLinkedList(head);head = reverseKNodes2(head, K);printLinkedList(head);System.out.println("=======================");head = new Node(1);head.next = new Node(2);head.next.next = new Node(3);head.next.next.next = new Node(4);head.next.next.next.next = new Node(5);head.next.next.next.next.next = new Node(6);head.next.next.next.next.next.next = new Node(7);head.next.next.next.next.next.next.next = new Node(8);K = 3;printLinkedList(head);head = reverseKNodes1(head, K);printLinkedList(head);head = reverseKNodes2(head, K);printLinkedList(head);System.out.println("=======================");} }輸出結果:
Linked List: Linked List: Linked List: ======================= Linked List: 1 Linked List: 1 Linked List: 1 ======================= Linked List: 1 2 Linked List: 2 1 Linked List: 1 2 ======================= Linked List: 1 2 Linked List: 1 2 Linked List: 1 2 ======================= Linked List: 1 2 3 4 Linked List: 2 1 4 3 Linked List: 1 2 3 4 ======================= Linked List: 1 2 3 4 5 6 7 8 Linked List: 3 2 1 6 5 4 7 8 Linked List: 1 2 3 4 5 6 7 8 =======================Process finished with exit code 0總結
以上是生活随笔為你收集整理的左神算法:将单链表的每K个节点之间逆序(Java版)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: leetcode 581. 最短无序连续
- 下一篇: 左神算法:将搜索二叉树转换成双向链表(J