第4节 链表问题
#####今天迟了,比预想速度慢很多,主要是得动手练习…
##这是数据结构与算法新手班-左程云第四节课的笔记##
单链表
-
给定一个单链表的头head,完成链表的逆序调整
class Test{ public static class Node{ public int value; public Node next; public Node(int v){ value = v; } } // 传进来的是head地址的一个备份,因为在Java里面都是按值传递 public static Node reverseLinkedList(Node head){ Node pre = null; Node next = null; while(head != null){ // 先把下面这个结点记住 next = head.next; // 让next变成原先的前面 head.next = pre; // 往后挪动 pre = head; head = next; } return pre; } public static void main(String[] args){ // JVM会释放什么样的结点呢? // JVM会释放掉那些不可达的结点。比如head -> A -> B <- C,JVM会把C结点释放掉 Node node1 = new Node(1); node1.next = new Node(2); node1.next.next = new Node(3); node1 = reverseLinkedList(node1); // 这段是打印呗! while(node1 != null){ System.out.println(node1.value + " "); node1 = node1.next; } System.out.println(); } }
-
给定一个双链表的头head,完成链表的逆序调整
class Test{ public static class Node{ public int value; public Node last; public Node next; public Node(int v){ value = v; } } // 传进来的是head地址的一个备份,因为在Java里面都是按值传递 public static Node reverseDoubleLinkedList(Node head){ Node pre = null; Node next = null; while(head != null){ // 先把下面这个结点记住 next = head.next; // 让next变成原先的前面 head.next = pre; head.last = next; // 往后挪动 pre = head; head = next; } return pre; } public static void main(String[] args){ // JVM会释放什么样的结点呢? // JVM会释放掉那些不可达的结点。比如head -> A -> B <- C,JVM会把C结点释放掉 Node node1 = new Node(1); node1.next = new Node(2); node1.next.next = new Node(3); node1 = reverseDoubleLinkedList(node1); // 这段是打印呗! while(node1 != null){ System.out.println(node1.value + " "); node1 = node1.next; } System.out.println(); } }
-
单链表实现队列(时间复杂度0(1))
import java.util.LinkedList; import java.util.Queue; class Test{ public static class Node<V>{ public V value; public Node<V> next; public Node(V v){ value = v; next = null; } } // 定义一个队列 public static class MyQueue<V> { private Node<V> head; private Node<V> tail; private int size; public MyQueue(){ head = null; tail = null; size = 0; } public boolean isEmpty(){ return size == 0; } public int size(){ return size; } // 载入值 public void offer(V value){ Node<V> cur = new Node<V>(value); if(tail == null){ head = cur; tail = cur; }else{ tail.next = cur; tail = cur; } size++; } // 弹出值,C++和C的同学记得析构,因为是手动释放的 public V poll(){ V ans = null; if(head != null){ ans = head.value; head = head.next; size--; } if(head == null){ tail = null; } return ans; } public V peek(){ V ans = null; if(head != null){ ans = head.value; } return ans; } } public static void main(String[] args){ MyQueue<Integer> myQueue = new MyQueue<>(); Queue<Integer> test = new LinkedList<>(); int testTime = 5000000; int maxValue = 200000000; System.out.println("测试开始!"); for (int i = 0; i < testTime; i++) { if (myQueue.isEmpty() != test.isEmpty()) { System.out.println("Oops!"); } if (myQueue.size() != test.size()) { System.out.println("Oops!"); } double decide = Math.random(); if (decide < 0.33) { int num = (int) (Math.random() * maxValue); myQueue.offer(num); test.offer(num); } else if (decide < 0.66) { if (!myQueue.isEmpty()) { int num1 = myQueue.poll(); int num2 = test.poll(); if (num1 != num2) { System.out.println("Oops!"); } } } else { if (!myQueue.isEmpty()) { int num1 = myQueue.peek(); int num2 = test.peek(); if (num1 != num2) { System.out.println("Oops!"); } } } } if (myQueue.size() != test.size()) { System.out.println("Oops!"); } while (!myQueue.isEmpty()) { int num1 = myQueue.poll(); int num2 = test.poll(); if (num1 != num2) { System.out.println("Oops!"); } } System.out.println("测试结束!"); } }
-
单链表实现栈(时间复杂度0(1))
class Test{ public static class Node<V>{ public V value; public Node<V> next; public Node(V v){ value = v; next = null; } } // 定义一个栈 public static class MyStack<V> { private Node<V> top; private int size; public MyStack(){ top = null; size = 0; } public boolean isEmpty(){ return size == 0; } public int size(){ return size; } // 载入值 public void offer(V value){ Node<V> cur = new Node<V>(value); if(top == null){ top = cur; }else{ cur.next = top; top = cur; } size++; } // 弹出值 public V poll(){ V ans = null; if(top != null){ ans = top.value; top = top.next; size--; } return ans; } public V peek(){ V ans = null; if(top != null){ ans = top.value; } return ans; } } public static void main(String[] args){ MyStack<Integer> queue = new MyStack<>(); queue.offer(1); queue.offer(2); queue.offer(3); queue.poll(); queue.poll(); System.out.println(queue.size); System.out.println(); } }
双链表
-
用双链表结构实现双端队列(什么是双端队列呢?同时允许两头进行进和出操作)
import java.util.LinkedList; import java.util.Queue; /* 要想实现这个效果,只能用双链表结构实现,为什么?我们分析一下! */ // 单链表结构只允许头部进行加减,尾部只可以加可以减,但是减操作很难,得遍历呀!!满足不了o(1)了! // 双链表就可以了! class Test{ public static class Node<V>{ public V value; public Node<V> last; public Node<V> next; public Node(V v){ value = v; last = null; next = null; } } // 定义一个队列 public static class MyDeque<V> { private Node<V> head; private Node<V> tail; private int size; public MyDeque(){ head = null; tail = null; size = 0; } public boolean isEmpty(){ return size == 0; } public int size(){ return size; } // 从头部加 public void pushHead(V value){ Node<V> cur = new Node<>(value); if(head != null){ cur.next = head; head.last = cur; head = cur; }else{ // 之前没元素 head = cur; tail = cur; } } // 从尾部加 public void pushTail(V value){ Node<V> cur = new Node<>(value); if(tail != null){ tail.next = cur; cur.last = tail; tail = cur; }else{ // 之前没元素 head = cur; tail = cur; } } // 从头部弹 public V pollHead(){ V ans = null; if(head == null){ return ans; } // 不管是1个还是几个,都执行这一句 ans = head.value; if(head != tail){ head = head.next; head.last = null; }else{ // 只有一个 head = null; tail = null; } size--; return ans; } // 从尾部弹 public V pollTail(){ V ans = null; if(tail == null){ return ans; } // 不管是1个还是几个,都执行这一句 ans = tail.value; if(head != tail){ tail = tail.last; tail.last = null; }else{ // 只有一个 head = null; tail = null; } size--; return ans; } } public static void main(String[] args){ MyDeque<Integer> deque = new MyDeque<>(); deque.pushHead(1); deque.pollHead(); } }
题目
-
K个结点的组内逆序调整。给定一个单链表的头节点head和一个整数k,实现k个结点的小组内部逆序,如果最后一组不够k个就不调整
调整前:1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 ,k = 3
调整后:3 → 2 → 1 → 6 → 5 → 4 → 7 → 8
// 测试链接:https://leetcode.com/problems/reverse-nodes-in-k-group/ public class Code04_ReverseNodesInKGroup { // 不要提交这个类 public static class ListNode { public int val; public ListNode next; } public static ListNode reverseKGroup(ListNode head, int k) { ListNode start = head; ListNode end = getKGroupEnd(start, k); if (end == null) { return head; } // 第一组凑齐了! head = end; reverse(start, end); // 上一组的结尾节点 ListNode lastEnd = start; while (lastEnd.next != null) { start = lastEnd.next; end = getKGroupEnd(start, k); if (end == null) { return head; } reverse(start, end); lastEnd.next = end; lastEnd = start; } return head; } public static ListNode getKGroupEnd(ListNode start, int k) { while (--k != 0 && start != null) { start = start.next; } return start; } public static void reverse(ListNode start, ListNode end) { end = end.next; ListNode pre = null; ListNode cur = start; ListNode next = null; while (cur != end) { next = cur.next; cur.next = pre; pre = cur; cur = next; } start.next = end; } }
-
给定两个链表的头节点head1和head2,认为从左到右是某个数字从低位到高位,返回相加之后的链表
例子:4 → 3 → 6 2 → 5 → 3
返回:6 → 8 → 9
解释:634 + 352 = 986
// 测试链接:https://leetcode.com/problems/add-two-numbers/ public class Code05_AddTwoNumbers { // 不要提交这个类 public static class ListNode { public int val; public ListNode next; public ListNode(int val) { this.val = val; } public ListNode(int val, ListNode next) { this.val = val; this.next = next; } } public static ListNode addTwoNumbers(ListNode head1, ListNode head2) { // 长链表和短链表重定向 int len1 = listLength(head1); int len2 = listLength(head2); ListNode l = len1 >= len2 ? head1 : head2; ListNode s = l == head1 ? head2 : head1; // 总共三个阶段 // 1. 长有短有 // 2. 长有短无 // 3. 长无短无(判断截止条件:进位为0) ListNode curL = l; ListNode curS = s; ListNode last = curL; int carry = 0; int curNum = 0; // 1. 长有短有 while (curS != null) { curNum = curL.val + curS.val + carry; curL.val = (curNum % 10); // 进位变量为carry carry = curNum / 10; last = curL; curL = curL.next; curS = curS.next; } // 2. 长有短无 while (curL != null) { curNum = curL.val + carry; curL.val = (curNum % 10); carry = curNum / 10; last = curL; curL = curL.next; } // 3. 长无短无(判断截止条件:进位为0) if (carry != 0) { last.next = new ListNode(1); } return l; } // 求链表长度 public static int listLength(ListNode head) { int len = 0; while (head != null) { len++; head = head.next; } return len; } }
-
给定两个链表的头节点head1和head2,返回合并之后的大链表,要求依然有序
例子:1 → 3 → 3 → 5 → 7 2 → 2 → 3 → 3 → 7
返回:1 → 2 → 2 → 3 → 3 → 3 → 3 → 5 → 7
// 测试链接:https://leetcode.com/problems/merge-two-sorted-lists public class Code06_MergeTwoSortedLinkedList { // 不要提交这个类 public static class ListNode { public int val; public ListNode next; } public static ListNode mergeTwoLists(ListNode head1, ListNode head2) { if (head1 == null || head2 == null) { return head1 == null ? head2 : head1; } ListNode head = head1.val <= head2.val ? head1 : head2; ListNode cur1 = head.next; ListNode cur2 = head == head1 ? head2 : head1; ListNode pre = head; while (cur1 != null && cur2 != null) { if (cur1.val <= cur2.val) { pre.next = cur1; cur1 = cur1.next; } else { pre.next = cur2; cur2 = cur2.next; } pre = pre.next; } pre.next = cur1 != null ? cur1 : cur2; return head; } }