链表
1 逆序打印链表
剑指 Offer 06. 从尾到头打印链表https://leetcode-cn.com/problems/cong-wei-dao-tou-da-yin-lian-biao-lcof/直接利用栈的特性~
package jzof.Day02;
import jzof.ListNode;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Stack;
/**
* @author ahan
* @create_time 2021-11-04-1:42 下午
*/
public class _06 {
public static void main(String[] args) {
ListNode head = new ListNode(1);
ListNode node0 = new ListNode(2);
ListNode node1 = new ListNode(3);
head.next = node0;
node0.next = node1;
int[] ints = reversePrint_2(head);
System.out.println(ints[0]);
System.out.println(ints[1]);
System.out.println(ints[2]);
}
public static int[] reversePrint(ListNode head) {
Deque<Integer> stack = new LinkedList<>();
while (head != null){
stack.push(head.val);
head = head.next;
}
// 获得栈的大小 size
int [] result = new int[stack.size()];
int i = 0;
while (!stack.isEmpty()){
result[i]=stack.pop();
i++;
}
return result;
}
// 栈里保存节点
public int[] reversePrint_1(ListNode head) {
Stack<ListNode> stack = new Stack<ListNode>();
ListNode temp = head;
while (temp != null) {
stack.push(temp);
temp = temp.next;
}
int size = stack.size();
int[] print = new int[size];
for (int i = 0; i < size; i++) {
print[i] = stack.pop().val;
}
return print;
}
// 不使用栈,不使用递归,扫描两趟
public static int[] reversePrint_2(ListNode head) {
ListNode node = head;
int count = 0;
while (node != null) {
++count;
node = node.next;
}
int[] nums = new int[count];
node = head;
for (int i = count - 1; i >= 0; --i) {
nums[i] = node.val;
node = node.next;
}
return nums;
}\
// 简便写法 流式编程 .stream().mapToInt(x->x).toArray();
public int[] reversePrint_3(ListNode head) {
Deque<Integer> stack = new LinkedList<Integer>();
ListNode node = head;
while(node != null){
stack.push(node.val);
node = node.next;
}
return stack.stream().mapToInt(x -> x).toArray();
}
}
一般不建议修改用户的输入
2 反转链表
剑指 Offer 24. 反转链表https://leetcode-cn.com/problems/fan-zhuan-lian-biao-lcof/
package jzof.Day02;
import jzof.ListNode;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
/**
* @author ahan
* @create_time 2021-11-04-2:57 下午
*/
public class _24 {
public static void main(String[] args) {
ListNode head = new ListNode(1);
ListNode node0 = new ListNode(2);
ListNode node1 = new ListNode(3);
head.next = node0;
node0.next = node1;
ListNode listNode = reverseList(head);
System.out.println(listNode.val);
// System.out.println(listNode.next.val);
}
public static ListNode reverseList(ListNode head) {
if(head==null){
return null;
}
Stack<ListNode> stack= new Stack<>();
while (head!=null){
stack.push(head);
head=head.next;
}
ListNode temp = stack.pop();
ListNode p = temp;
temp.next = null;
while (!stack.isEmpty()){
temp.next = stack.pop();
temp = temp.next;
temp.next = null;
}
return p;
}
// 迭代
public static ListNode reverseList_0(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
// 递归
public ListNode reverseList(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode newHead = reverseList(head.next);
head.next.next = head;
head.next = null;
return newHead;
}
// 栈
// public static ListNode reverseList_2(ListNode head) {
//
// }
}
1. 迭代法
2. 递归法![](https://i-blog.csdnimg.cn/blog_migrate/a4523db5e5628b7ca97ac598ca8d4388.jpeg)
3 复杂链表复制
剑指 Offer 35. 复杂链表的复制https://leetcode-cn.com/problems/fu-za-lian-biao-de-fu-zhi-lcof/
1. 遍历+hashmap
关键是随机next 可能还没建立~
想到了用hashmap存储新旧节点对应关系!
package jzof.Day02;
import jzof.Node;
import java.util.HashMap;
/**
* @author ahan
* @create_time 2021-11-04-5:30 下午
* 请实现 copyRandomList 函数,复制一个复杂链表。
* 在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
*/
public class _35 {
public static void main(String[] args) {
Node head = new Node(1);
Node node1 = new Node(2);
head.random = node1;
node1.random = node1;
head.next = node1;
node1.next = null;
head = null;
Node node = copyRandomList(head);
// node = head;
while(node!= null){
System.out.print(node.val);
System.out.print(" ");
System.out.println(node.random.val);
node = node.next;
}
}
public static Node copyRandomList(Node head) {
if(head == null)
return null;
HashMap map = new HashMap();
Node newNode = new Node(head.val);
Node head_temp = head;
Node newNode_temp = newNode;
map.put(head,newNode);
while(head_temp.next != null){
Node new_node = new Node(head_temp.next.val);
map.put(head_temp.next,new_node);
newNode_temp.next = new_node;
newNode_temp = newNode_temp.next;
head_temp = head_temp.next;
}
head_temp = head;
newNode_temp = newNode;
while(head_temp!=null){
if (head_temp.random == null){
newNode_temp.random = null;
}else{
newNode_temp.random = (Node) map.get(head_temp.random);
}
head_temp = head_temp.next;
newNode_temp = newNode_temp.next;
}
return newNode;
}
}
2. 回溯法+hashmap
用哈希表记录每一个节点对应新节点的创建情况。
- 遍历该链表的过程中,检查「当前节点的后继节点」和「当前节点的随机指针指向的节点」的创建情况。
- 如果这两个节点中的任何一个节点的新节点没有被创建,立刻递归地进行创建。
- 当拷贝完成,回溯到当前层时,即可完成当前节点的指针赋值。
- 注意一个节点可能被多个其他节点指向,因此可能递归地多次尝试拷贝某个节点,为了防止重复拷贝,需要首先检查当前节点是否被拷贝过,如果已经拷贝过,可以直接从哈希表中取出拷贝后的节点的指针并返回即可。
在实际代码中,需要特别判断给定节点为空节点的情况。
/*
// Definition for a Node.
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
*/
class Solution {
Map<Node, Node> cachedNode = new HashMap<Node, Node>();
public Node copyRandomList(Node head) {
if (head == null) {
return null;
}
if (!cachedNode.containsKey(head)) {
Node headNew = new Node(head.val);
cachedNode.put(head, headNew);
headNew.next = copyRandomList(head.next);
headNew.random = copyRandomList(head.random);
}
return cachedNode.get(head);
}
}
- 时间复杂度 为 O(n),其中 n 是链表的长度。对于每个节点,至多访问其「后继节点」和「随机指针指向的节点」各一次,均摊每个点至多被访问两次。
- 空间复杂度 为 O(n) ,其中 n 是链表的长度。为哈希表的空间开销。
3. 迭代+节点拆分
- 先将该链表中每一个节点拆分为两个相连的节点
- 例如对于链表 A→B→C,可以将其拆分为 A→A′→B→B′→C→C′。对于任意一个原节点 S,其拷贝节点 S' 即为其后继节点。
- 这样可以直接找到每一个拷贝节点 S'的随机指针应当指向的节点,即为其原节点 S 的随机指针指向的节点 T 的后继节点 T'。
- 需要注意原节点的随机指针可能为空,需要特别判断这种情况。
- 当完成了拷贝节点的随机指针的赋值,只需要将这个链表按照原节点与拷贝节点的种类进行拆分即可,只需要遍历一次。同样需要注意最后一个拷贝节点的后继节点为空,需要特别判断这种情况。
public static Node copyRandomList_2(Node head) {
if (head == null) {
return null;
}
// 建立新的次节点
for (Node node = head; node != null; node = node.next.next) {
Node nodeNew = new Node(node.val);
nodeNew.next = node.next;
node.next = nodeNew;
}
// 搭 random线
for (Node node = head; node != null; node = node.next.next) {
Node nodeNew = node.next;
nodeNew.random = (node.random != null) ? node.random.next : null;
}
// 拆分
Node headNew = head.next;
for (Node node = head; node != null; node = node.next) {
Node nodeNew = node.next;
node.next = node.next.next;
nodeNew.next = (nodeNew.next != null) ? nodeNew.next.next : null;
}
return headNew;
}
- 时间复杂度 为 O(n),其中 n 是链表的长度。只需要遍历该链表三次。
- 空间复杂度 为 O(1) ,注意返回值不计入空间复杂度
后面两种都是LeetCode官方解法~太强了!