一、重复的数
题目:
/** * 给定一个整数数组和一个整数 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;
}