【精選】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;
    }

 

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