剑指offer-链表

链表:快慢指针,创建虚拟头节点,考虑哈希表建立映射关系

一、链表

1.

描述

输入一个链表的头节点,按链表从尾到头的顺序返回每个节点的值(用数组返回)。

如输入{1,2,3}的链表如下图:

返回一个数组为[3,2,1]

0 <= 链表长度 <= 10000

/**
*    public class ListNode {
*        int val;
*        ListNode next = null;
*
*        ListNode(int val) {
*            this.val = val;
*        }
*    }
*
*/
import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> list = new ArrayList<>();
        ListNode cur = listNode;
        while(cur!=null){
            int value = cur.val;
            //ArrayList 中有个方法是 add(index,value),可以指定 index 位置插入 value 值
            //遍历 listNode 的同时将每个遇到的值插入到 list 的 0 位置,最后输出 listNode 即可得到逆序链表
            list.add(0,value);
            cur = cur.next;
        }
        return list;
    }
}

2.反转链表:快慢指针

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode ReverseList(ListNode head) {
    //反转链表:快慢双指针
       ListNode fast = head;
       ListNode slow = null;
        while(fast!=null){
            //记录fast的next
            ListNode next = fast.next;
            fast.next = slow;
            slow = fast;
            fast = next;
            
        }
        return slow;
    }
}

3.合并两个有序链表(递归)

输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。

数据范围: 0 \le n \le 10000≤n≤1000,-1000 \le 节点值 \le 1000−1000≤节点值≤1000
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)

如输入{1,3,5},{2,4,6}时,合并后的链表为{1,2,3,4,5,6},所以对应的输出为{1,2,3,4,5,6},

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        //递归做题
        //退出条件
        ListNode head;
        if(list1==null){
            return list2;
        }
        if(list2 == null){
            return list1;
        }
        
        if(list1.val>list2.val){
            head =list2;
            head.next = Merge(list1,list2.next);
        }else{
            head = list1;
            head.next = Merge(list2,list1.next);
        }
        return head;
    }
}

4.两个链表的交点

输入描述:

输入分为是3段,第一段是第一个链表的非公共部分,第二段是第二个链表的非公共部分,第三段是第一个链表和二个链表的公共部分。 后台会将这3个参数组装为两个链表,并将这两个链表对应的头节点传入到函数FindFirstCommonNode里面,用户得到的输入只有pHead1和pHead2。

返回值描述:

返回传入的pHead1和pHead2的第一个公共结点,后台会打印以该节点为头节点的链表。

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
     //到达公共节点时,走的距离相,节点的引用也相同
        ListNode n1 = pHead1;
        ListNode n2 = pHead2;
        while(n1!=n2){
            //这里判段的是n1,不是n1.next;
            n1= n1 !=null? n1.next:pHead2;
            n2 = n2 !=null?n2.next:pHead1;
        }
        return n1;
        
    }
}

 5.链表的第一个入环节点

注意点:判断是否是,有环的while循环;

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {

    public ListNode EntryNodeOfLoop(ListNode pHead){
        //快慢指针
        ListNode fast = pHead;
        ListNode slow = pHead;
        //要判断是不是没有环
        while(fast !=null && fast.next!=null){
            fast =fast.next.next;
            slow = slow.next;
            if(fast ==slow){
               ListNode n = pHead;
               while(n!=slow){
                    n = n.next;
                    slow = slow.next;
                }
                return slow;
            }
        }
        
        
        return null;
        
    }
}

 6.返回地倒数n个节点,当n值大于链表长度时,返回空

import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 *   public ListNode(int val) {
 *     this.val = val;
 *   }
 * }
 */

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param pHead ListNode类 
     * @param k int整型 
     * @return ListNode类
     */
    public ListNode FindKthToTail (ListNode pHead, int k) {
        // write code here
        //快慢指针,快指针先走k步
        //虚拟指针
//         ListNode n1 = new ListNode(-1);
//         n1.next = pHead;
        ListNode n1 =pHead;
        ListNode n2 = pHead;
        //快指针先走k步
        int num=0;
        
        for(int i = 0;i<k&& n2!=null;i++){
            n2 = n2.next;
            num++;
        }
        if(num<k) return null;
        while(n2 != null){
            n1 = n1.next;
            n2 = n2.next;
        }
        return n1;
       
    }
}

7.将搜索二叉树转为循环双向链表

算法流程:
dfs(cur): 递归法中序遍历;

终止条件: 当节点 cur 为空,代表越过叶节点,直接返回;
递归左子树,即 dfs(cur.left) ;
构建链表:
当 pre 为空时: 代表正在访问链表头节点,记为 head ;
当 pre 不为空时: 修改双向节点引用,即 pre.right = cur , cur.left = pre ;
保存 cur : 更新 pre = cur ,即节点 cur 是后继节点的 pre ;

递归右子树,即 dfs(cur.right) ;

treeToDoublyList(root):

特例处理: 若节点 root 为空,则直接返回;
初始化: 空节点 pre ;
转化为双向链表: 调用 dfs(root) ;
构建循环链表: 中序遍历完成后,head 指向头节点, pre 指向尾节点,因此修改 head 和 pre 的双向节点引用即可;
返回值: 返回链表的头节点 head 即可;

/*
// Definition for a Node.
class Node {
    public int val;
    public Node left;
    public Node right;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val,Node _left,Node _right) {
        val = _val;
        left = _left;
        right = _right;
    }
};
*/
class Solution {
    Node pre,head;
    public Node treeToDoublyList(Node root) {
        if(root == null) return null;
        dfs(root);
        head.left = pre;
        pre.right = head;
        return head;
        
    }
    //将搜索二叉树转化为双向链表
    public void dfs(Node cur){
        if(cur == null) return;
        dfs(cur.left);
        //当递归到父节点时
        //注意循环中只是判断pre的存不存在
        if(pre== null) {
            head = cur;
        }else{
            pre.right = cur;   
        }
        cur.left = pre;
        pre = cur;
        dfs(cur.right);
    }

}

8.复制复杂链表-哈希表中建立新的节点

复制链表是指新的链表节点全是new的,不是复制旧链表的地址

请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。

题目链接:力扣

/*
// 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 {
    public Node copyRandomList(Node head) {
        //定义一个指针,遍历旧的的链表,在map中建立于新节点的映射关系
        Node cur=head;
        Map<Node,Node> map = new HashMap<>();
        //创建一个新的链表
        while(cur != null){
            //在map中建立新的节点
            map.put(cur,new Node(cur.val));
            cur = cur.next;
        }
        //如何找到链表的头节点?找到key=head时的value;
        cur = head;
        while(cur != null){
            //
            map.get(cur).next = map.get(cur.next);
            map.get(cur).random = map.get(cur.random);
            cur = cur.next;
        }
        
        return map.get(head);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值