【精选】JAVA算法题(十九)

一、重复的数

题目:

/**
 * 给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,
 * 使得 nums [i] = nums [j],并且 i 和 j 的差的绝对值最大为 k。
 *
 * 示例 1:
 * 输入: nums = [1,2,3,1], k = 3
 * 输出: true
 *
 * 示例 2:
 * 输入: nums = [1,0,1,1], k = 1
 * 输出: true
 *
 * 示例 3:
 * 输入: nums = [1,2,3,1,2,3], k = 2
 * 输出: false
 */

同样是判断数组中是否出现过相同的数,但给限定了一个条件,两数所在索引差值不能超过给定整数

最笨的方法肯定是暴力for循环了,不解释,开始循环~

    public boolean method1(int[] nums, int k) {
        for (int i=0;i<nums.length-1;i++){
            int end=i+k+1;
            if (end>nums.length){
                end=nums.length;
            }
            for (int j=i+1;j<end;j++){
                if (nums[j]==nums[i]){
                    return true;
                }
            }
        }
        return false;
    }

但这种办法肯定是低效的,对于判断有没有相同的元素来说Set是最适合的了。Set在添加元素时会返回是否已经包含该元素了,我们可以利用这个特点来进行判断是否含有重复元素。对于范围的限制可以使用remove来在遍历过程中不断移除前面的元素并添加后面的元素。

    public boolean method2(int[] nums,int k){
        HashSet<Integer> set = new HashSet<>();
        int i = 0;
        while (i < k && i < nums.length) {
            if (!set.add(nums[i])) {
                return true;
            }
            i++;
        }
        while (i < nums.length) {
            if (!set.add(nums[i])) {
                return true;
            }
            set.remove(nums[i - k]);
            i++;
        }
        return false;
    }

二、使用队列实现栈

题目:

/**
 * 使用队列实现栈的下列操作:
 *
 *     push(x) -- 元素 x 入栈
 *     pop() -- 移除栈顶元素
 *     top() -- 获取栈顶元素
 *     empty() -- 返回栈是否为空
 *
 * 注意:
 *
 *     你只能使用队列的基本操作-- 也就是 push to back, peek/pop from front, size, 和 is empty 这些操作是合法的。
 *     你所使用的语言也许不支持队列。 你可以使用 list 或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
 *     你可以假设所有操作都是有效的(例如, 对一个空的栈不会调用 pop 或者 top 操作)。
 */

很简单,栈是先进后出,队列是先进先出,你只要保证先添加进来的放在最后面就和栈一样了。你可以做这样的操作,当添加进来一个新元素的时候把它之前的所有元素poll出来再放进队列,这样新添加进来的元素便在最前面了,最先出的也就是最新添加进来的。

import java.util.ArrayDeque;

public class ImplementStackUsingQueues {

    ArrayDeque<Integer> queue=new ArrayDeque<Integer>();

    /** Push element x onto stack. */
    public void push(int x) {
        queue.add(x);
        for(int i=0;i<queue.size()-1;i++){
            queue.add(queue.poll());
        }
    }

    /** Removes the element on top of the stack and returns that element. */
    public int pop() {
        return queue.poll();
    }

    /** Get the top element. */
    public int top() {
        return queue.peek();
    }

    /** Returns whether the stack is empty. */
    public boolean empty() {
        return queue.isEmpty();
    }
}

三、最近祖先

题目:

/**
 * 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
 * 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,
 * 满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
 * <p>
 * 示例 1:
 * 输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
 * 输出: 6
 * 解释: 节点 2 和节点 8 的最近公共祖先是 6。
 * <p>
 * 示例 2:
 * 输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
 * 输出: 2
 * 解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。
 */
public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;

    TreeNode(int x) {
        val = x;
    }
}

 

我开始用了一种比较笨的方法,先用递归把找到p,q的路径节点都存到list里面,然后去遍历list找到最近的相同的两个点,正序遍历的话比较慢,需要遍历完,倒序遍历会快一些。我这里使用的是正序遍历。

    public TreeNode method1(TreeNode root, TreeNode p, TreeNode q) {
        ArrayList<TreeNode> treeNodesp=new ArrayList<>();
        ArrayList<TreeNode> treeNodesq=new ArrayList<>();
        createRoute(root,p,treeNodesp);
        createRoute(root,q,treeNodesq);
        for (int i=0;i<treeNodesp.size();i++){
            for (TreeNode treeNode:treeNodesq){
                if (treeNodesp.get(i).equals(treeNode)){
                    return treeNode;
                }
            }
        }
        return null;
    }

    private boolean createRoute(TreeNode root, TreeNode p, ArrayList<TreeNode> treeNodesp) {
        if (root==null)return false;
        if (root==p){
            treeNodesp.add(root);
            return true;
        }
        if (createRoute(root.left,p,treeNodesp)){
            treeNodesp.add(root);
            return true;
        }
        if (createRoute(root.right,p,treeNodesp)){
            treeNodesp.add(root);
            return true;
        }
        return false;
    }

题目中给了一个条件,二叉搜索树!二叉搜索树的特性,左边的数比右边小。可以利用这个特性来定位p,q所在的位置。找到一个节点的值的大小在p,q之间,这就是他俩最近的祖先节点。

    public TreeNode method2(TreeNode root, TreeNode p, TreeNode q) {
        while(root != null){
            // 自上而下 找分界点
            if(root.val > p.val && root.val > q.val) root = root.left;     // 在右子树
            else if(root.val < p.val && root.val < q.val) root = root.right;    // 在左子树
            else return root;
        }
        return null;
    }

同样的思想,换一种写法

    TreeNode res = null;
    public TreeNode method3(TreeNode root, TreeNode p, TreeNode q) {
        lca(root, p , q);
        return res;
    }

    private void lca(TreeNode root, TreeNode p , TreeNode q){
        if((root.val - p.val)*(root.val - q.val) <= 0){
            res = root;
        }else if(root.val < p.val && root.val < q.val){
            lca(root.right, p , q);
        }else{
            lca(root.left, p , q);
        }
    }

四、删除节点

题目:

/**
 * 请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。
 * 现有一个链表 -- head = [4,5,1,9]
 *
 * 示例 1:
 * 输入: head = [4,5,1,9], node = 5
 * 输出: [4,1,9]
 * 解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
 *
 * 示例 2:
 * 输入: head = [4,5,1,9], node = 1
 * 输出: [4,5,9]
 * 解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.
 *
 * 说明:
 * 链表至少包含两个节点。
 * 链表中所有节点的值都是唯一的。
 * 给定的节点为非末尾节点并且一定是链表中的一个有效节点。
 * 不要从你的函数中返回任何结果。
 */
public class ListNode {
    int val;
    ListNode next;

    ListNode(int x) {
        val = x;
    }
}

 

题看起来很复杂,其实非常简单,就是给你一个节点,这个节点存在于链表中,且不是头尾节点,链表长度也至少为2,为你规避了一切异常情况,删节点嘛,很简单,赋值该节点的值为下一个节点的值,让该节点的下一个指向下一个的下一个。so easy!

    public void method1(ListNode node) {
        node.val = node.next.val;
        node.next = node.next.next;
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章