牛客题目——链表中倒数最后k个结点、两个链表的第一个公共结点、二叉搜索树的最近公共祖先


题目1——链表中倒数最后k个结点

输入一个长度为n的链表,设链表中的元素的值为ai,返回该链表中倒数第k个结点。
如果该链表长度小于k,请返回一个长度为0的链表。
要求:空间复杂度O(n),时间复杂度O(n)

示例
输入:{1,2,3,4,5}, 2
输出:{4,5}
输入:{2}, 8
输出:{}

解题思路

使用快慢指针,快指针先移动k步,慢指针再从头移动,这时候两个指针同时移动,当快指针到链表的末尾的时候,返回第二个指针即可。

利用栈,首先将原链表的结点全部压入到栈当中,然后再把栈中最上面的k个结点出栈,出栈的结点重新串成一个新的链表。

代码实现

import java.util.*;
/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 *   public ListNode(int val) {
 *     this.val = val;
 *   }
 * }
 */
public class Solution {
    public ListNode FindKthToTail (ListNode pHead, int k) {
        ListNode dummy = pHead;
        ListNode prek = new ListNode(0);
        prek = pHead;
        for(int i=0;i<k;i++){
            if(prek != null)
                prek  = prek.next;
            else return null;
        }
        while(prek != null){
            dummy = dummy.next;
            prek = prek.next;
        }
        return dummy;  
    }
}
import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 *   public ListNode(int val) {
 *     this.val = val;
 *   }
 * }
 */
public class Solution {
    public ListNode FindKthToTail (ListNode pHead, int k) {
        ListNode res = new ListNode(0);
        Stack<ListNode> st = new Stack<ListNode>();
        while(pHead != null){
            st.push(pHead);
            pHead = pHead.next;
        }
        for(int i=0;i<k;i++){
            if(st.isEmpty())
                return null;
            ListNode tmp = st.pop();
            if(res.next == null){
                res.next = tmp;
            }else{
                tmp.next = res.next;
                res.next = tmp;
            }
        }
        return res.next;
    }
}

题目2——两个链表的第一个公共结点

输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共结点则返回空。
要求:空间复杂度O(1),时间复杂度O(n)

在这里插入图片描述

解题思路

利用双指针,若两个链表存在公共结点,则两个指针以同样的速度走完同样的长度(链表1+链表2),一定会相遇;而没有公共结点的时候,两个指针都会走到终点,此时都为空。

或者是利用集合,将第一个链表中的所有结点存放到集合中,然后遍历第二个链表,判断其是否存在集合中,如果存在,则返回这个结点,否则遍历后没有找到,则说明没有相交,返回null即可。

代码实现

import java.util.*;
/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        if(pHead1 == null || pHead2 == null)
            return null;
        ListNode p1 = pHead1;
        ListNode p2 = pHead2;
        while(p1 != p2){
            p1 = (p1 == null)?pHead2:p1.next;
            p2 = (p2 == null)?pHead1:p2.next;
        }
        return p1;
    }
}
import java.util.*;
/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        if(pHead1 == null || pHead2 == null)
            return null;
        ListNode p1 = pHead1;
        ListNode p2 = pHead2;
        Set<ListNode> set = new HashSet<ListNode>();
        while(p1 != null){
            set.add(p1);
            p1 = p1.next;
        }
        while(p2 != null){
            if(!set.contains(p2)){
                p2 = p2.next;
            }else
                return p2;
        }
        return null;
    }
}

题目3——二叉搜索树的最近公共祖先

给定一个二叉搜索树,找到该树种两个指定结点的最近公共祖先。
最近公共祖先定义:对于有根树T的两个结点p、q,最近公共祖先LCA(T,p,q)表示一个结点x,满足x是p和q的祖先且x的深度尽可能大。在这里,一个结点也可以是它自己的祖先。

示例
输入:{7,1,12,0,4,11,14,#,#,3,5},1,12
输入:7
在这里插入图片描述

解题思路

首先检查空结点,空树没有公共祖先;
比较两个结点的值,若都小于根结点,则p,q结点都在该结点的左侧,公共祖先在左子树中,递归进入左子树;
若都大于根结点,则公共祖先都在右子树中,递归进入右子树;
否则,返回最近公共祖先即为根结点。

找到两个结点的路径,然后将路径中的结点存放到数组当中,比较两个数组,最后一个相等的元素就是最近的公共祖先。
寻找路径时,根据二叉搜索树的性质,当前结点的值比目标大,则进入左子树,否则进入右子树。

代码实现

import java.util.*;
/*
 * public class TreeNode {
 *   int val = 0;
 *   TreeNode left = null;
 *   TreeNode right = null;
 *   public TreeNode(int val) {
 *     this.val = val;
 *   }
 * }
 */
public class Solution {
    public int lowestCommonAncestor (TreeNode root, int p, int q) {
        if(root == null)
            return -1;
        if(p<root.val && q<root.val)
            return lowestCommonAncestor(root.left,p,q);
        if(p>root.val && q>root.val)
            return lowestCommonAncestor(root.right,p,q);
        return root.val;
    }
}
import java.util.*;
/*
 * public class TreeNode {
 *   int val = 0;
 *   TreeNode left = null;
 *   TreeNode right = null;
 *   public TreeNode(int val) {
 *     this.val = val;
 *   }
 * }
 */
public class Solution {
    public ArrayList<Integer> getPath(TreeNode root,int target){
        ArrayList<Integer> path = new ArrayList<Integer>();
        while(root.val != target){
            path.add(root.val);
            if(target<root.val)
                root = root.left;
            else
                root = root.right;
        }
        path.add(root.val);
        return path;
    }
    public int lowestCommonAncestor (TreeNode root, int p, int q) {
        ArrayList<Integer> path_p = getPath(root,p);
        ArrayList<Integer> path_q = getPath(root,q);
        int res = 0;
        for(int i=0;i<Math.min(path_p.size(),path_q.size());i++){
            int x = path_p.get(i);
            int y = path_q.get(i);
            if(x == y)
                res = path_p.get(i);
            else
                break;
        }
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值