一、重複的數
題目:
/** * 給定一個整數數組和一個整數 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;
}