阿翰 剑指offer 之 Day02 链表

链表

1 逆序打印链表

2 反转链表

1. 迭代法

 2. 递归法​

 3 复杂链表复制

1. 遍历+hashmap

2. 回溯法+hashmap

3. 迭代+节点拆分


链表

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. 递归法

 

 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. 迭代+节点拆分

  1. 先将该链表中每一个节点拆分为两个相连的节点
    • 例如对于链表  A→B→C,可以将其拆分为  A→A′→B→B′→C→C′。对于任意一个原节点 S,其拷贝节点 S' 即为其后继节点。
    • 这样可以直接找到每一个拷贝节点 S'的随机指针应当指向的节点,即为其原节点 S 的随机指针指向的节点 T 的后继节点 T'。
    • 需要注意原节点的随机指针可能为空,需要特别判断这种情况。
  2. 当完成了拷贝节点的随机指针的赋值,只需要将这个链表按照原节点与拷贝节点的种类进行拆分即可,只需要遍历一次。同样需要注意最后一个拷贝节点的后继节点为空,需要特别判断这种情况。
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官方解法~太强了!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值