栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

剑指offerⅠ(Java 持续更新...)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

剑指offerⅠ(Java 持续更新...)

DAY1 09: 用两个栈实现队列

思路:队列特点是先进先出,所以将栈A依次弹出到B,那么B里存放的就是倒序的A,再将其弹出即可实现先进先出。

注意:

  • deleteHead()函数中语句的顺序不能颠倒,必须先判断B是否为空,若不为空直接弹出。
  • isEmpty和empty的区别在于:字符串中只有isEmpty()方法,而栈中两种方法均可以实现。
class CQueue {
    LinkedList A,B;

    public CQueue() {
        A = new LinkedList();
        B = new LinkedList();
    }
    
    public void appendTail(int value) {
        A.addLast(value);
    }
    
    public int deleteHead() {
        if(!B.isEmpty()) return B.removeLast(); //顺序不能换!先判断B是否为空
        if(A.isEmpty()) return -1;
        while(!A.isEmpty())
        B.addLast(A.removeLast());
        return B.removeLast();
    }
}
30:包含min函数的栈

思路:利用两个栈,当最新插入的数字小于min栈里的首元素,则将其插入min中

  • peek方法:返回栈的head元素
  • add(int index,E element)可以指定添加元素的位置,默认添加到末尾,与addLast相同
  • 添加到头部使用addFirst()
class MinStack {
    
    Stack stack,min;
    public MinStack() {
        stack = new Stack<>();
        min = new Stack<>();
    }
    
    public void push(int x) {
        stack.add(x);
        if(min.isEmpty() || min.peek() >= x) min.add(x);
    }
    
    public void pop() {
        if(stack.pop().equals(min.peek())) min.pop();
    }
    
    public int top() {
        return stack.peek();
    }
    
    public int min() {
        return min.peek();
    }
}
16:从尾到头打印列表

思路:利用一个栈一个数组,将列表每个节点的值依次存入栈中,再从末尾依次弹出尾元素并存入数组中即可。

class Solution {
    public int[] reversePrint(ListNode head) {
        LinkedList stack = new LinkedList();
        while(head != null){
            stack.addLast(head.val);
            head = head.next;
        }
        int[] res = new int[stack.size()];
        for(int i = 0; i < res.length; i++)
            res[i] = stack.removeLast();
        return res;
    }
}
DAY2 24:反转列表

思路:返回值要求也是节点类型。采用双指针思想,将当前指针cur的next指向前一个指针pre。

(这道题真的死活不能彻底悟透思路 555555)

最后temp和cur都指向了null

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode cur = head, pre = null;
        while(cur != null){
            ListNode temp = cur.next;  //暂存后继结点
            cur.next = pre;  //修改引用指向
            pre = cur;  //暂存当前节点
            cur = temp;  //访问下一个节点
        }
        return pre;
    }
}
05:替换空格

思路:遇到空格替换成%20

注:

  • StringBuilder:可变字符序列
  • str.toCharArray():字符串转换为数组

class Solution {
    public String replaceSpace(String s) {
        StringBuilder res = new StringBuilder();
        for(Character c : s.toCharArray()){  //foreach循环
            if(c == ' ') res.append("%20");
            else res.append(c);
        }
        return res.toString();
    }
}
58:左旋转字符串

思路:直接用截取字符串函数substring()很方便!!!

class Solution {
    public String reverseLeftWords(String s, int n) {
        return s.substring(n, s.length()) + s.substring(0, n);
    }
}

DAY3 03:数组中重复的数字

思路:利用哈希表,将数组nums中的元素一次放入hash表中,若已经包含在hash表中的则为重复的数字,输出即可。

class Solution {
    public int findRepeatNumber(int[] nums) {
        Set hash = new HashSet<>();
        for(int num : nums){
            if(hash.contains(num)) return num;
            hash.add(num);
        }
        return -1;
    }
}
53:在排序数组中查找数字Ⅰ

思路:笨办法,遍历数组查匹配。

class Solution {
    public int search(int[] nums, int target) {
        Set hash = new HashSet<>();
        int count = 0;
        for(int num : nums){
            if(num == target) count++;
        }
        return count;
    }
}
53:0~n-1中缺失的数字(重点)

思路:因为是升序依次排列,所以采用二分查找效率会更高一些。首先要想到的是索引和元素不相等即为缺失的。所以设置r,l分别从头和尾开始,若索引与元素相等则说明前半部分没有错位,所以 r 移动在后半部分查询;若不相等则说明前半部分有错位,l 移动在前半部分查询。

class Solution {
    public int missingNumber(int[] nums) {
        //[0,1,3,4,5,6,7,8,9]
        int r = 0, l = nums.length - 1;
        while(r <= l){
            int m = (r + l) / 2;
            if(nums[m] == m) r = m+1;
            else l = m-1;
        } 
        return r;
    }
}
DAY4 04:二维数组中的查找

思路:由题可知数组从左到右从上而下增大,所以从左下角开始查找效率最高。若该值大于target,那么target只存在于该值所在行的上方;若该值小于target,则target存在于该值所在列的右侧。

class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        //从左下角开始找,小的话向上移,大的话向左移
        int i = matrix.length - 1, j = 0;
        while(i >= 0 && j  target) i--;
            else return true;
        }
        return false;
    }
}
11:旋转数组中的最小数字

思路:利用二分查找

  • 若middle小于high,则说明旋转点在左半部分
  • 若middle大于high,则旋转点在右半部分
  • 若相等则不能确定,可能有重复的数字,所以high--缩小范围继续二分查询

我有一点不明白就是为什么low移动后是m+1,而high移动就是m???好像是会造成死循环。

向下取整的时候可能会造成m左偏,所以low移动时候需要m+1,不然会使m陷在左边的循环中。

参考博文:https://blog.csdn.net/leokingszx/article/details/79464472

class Solution {
    public int minArray(int[] numbers) {
        int low = 0, high = numbers.length -1;
        while(low != high){
            int m = (low + high) / 2;
            if(numbers[m] > numbers[high]) low = m + 1;
            else if(numbers[m] < numbers[high]) high = m;
            else high--;
        }
        return numbers[low];
    }
}
50:第一个只出现一次的字符

思路:利用哈希表(存放字符和布尔值),遍历字符串

  • 若dic中不包含c,则往dic中添加键值对(c,true),代表字符c出现次数为1
  • 若dic中包含c,则修改键值对为(c,false),即字符出现次数>1
  • 再遍历字符串,输出第一个是true的字符

class Solution {
    public char firstUniqChar(String s) {
        HashMap dic = new HashMap<>();
        char[] sc = s.toCharArray();
        for(char c : sc)
        dic.put(c, !dic.containsKey(c));
        for(char c : sc)
        if(dic.get(c)) return c;
        return ' ';
    }
}
32-Ⅰ:从上到下打印二叉树(BFS)

思路:用队列暂存每一层的结点,动态数组添加遍历结果。

多看这道题啊!!!万物基础!

class Solution {
    public int[] levelOrder(TreeNode root) {
        if(root == null) return new int[0];
        Queue queue = new LinkedList();
        List list = new ArrayList();
        queue.add(root);
        while(!queue.isEmpty()){
            TreeNode node = queue.poll();  //取出头节点
            list.add(node.val);
            if(node.left != null) queue.add(node.left);
            if(node.right != null) queue.add(node.right);
        }
        //将List转换为int数组,下面这句一样相同
        //return list.stream().mapToInt(Integer::valueOf).toArray();
        int[] res = new int[list.size()];
        for(int i = 0; i < list.size(); i++) res[i] = list.get(i); 
        return res;
    }
}
 32-Ⅱ:从上到下打印二叉树(多看几遍!!!)

思路:返回数组是一个二维数组,每层返回一个组,用queue.size()获取长度。

这个思路牛逼就牛逼在for循环的条件,每次大循环都会把下一层的结点都存入queue中,然后将初值设置为queue的size直接i--,因为poll出根结点后queue的size会变,所以直接用递减会更好理解。妙啊妙啊

class Solution {
    public List> levelOrder(TreeNode root) {
        Queue queue = new LinkedList<>();
        List> res = new ArrayList<>(); //二维数组
        if(root != null) queue.add(root);
        while(!queue.isEmpty()){
            List temp = new ArrayList<>();
            for(int i = queue.size(); i > 0; i--){
                TreeNode node = queue.poll();
                temp.add(node.val);
                if(node.left != null) queue.add(node.left);
                if(node.right != null) queue.add(node.right);
            }
            res.add(temp);
        }
        return res;
    }
}
DAY5  32-Ⅲ:从上到下打印二叉树

思路:和2大同小异,只不过在将temp数组存入res数组的时候当为第奇数个元素时候用reverse反转存入,想法简单,只不过这个函数我第一次见。

class Solution {
    public List> levelOrder(TreeNode root) {
        Queue queue = new LinkedList<>();
        List> res = new ArrayList<>();
        if(root != null) queue.add(root);
        while(!queue.isEmpty()){
            List temp = new ArrayList<>();
            for(int i = queue.size(); i > 0; i--){
                TreeNode node = queue.poll();
                temp.add(node.val);
                if(node.left != null) queue.add(node.left);
                if(node.right != null) queue.add(node.right);
            }
            if(res.size() % 2 == 1) Collections.reverse(temp);
            res.add(temp);
        }
        return res;
    }
}

26:树的子结构(重点看,思路有点复杂)

思路:递归。分情况讨论

isSubStructure(A,B):判断B是否为A的子树

  • 以节点A为根结点的树包含B,即recur(A,B)
  • B是A左子树的子结构
  • B是A右子树的子结构

以上三种条件任意满足一种即可,但前提条件是A,B均不为空树。若A,B中存在空树则没有子结构。

recur(A,B):判断以节点A为根结点的树是否包含B

  • 若B为空,则匹配返回true。执行终止。
  • 若A为空或节点A与B的值不相等,则不匹配,返回false。执行终止。

返回判断A和B的左右子节点是否对应相等的结果。

class Solution {
    public boolean isSubStructure(TreeNode A, TreeNode B) {
        return (A != null && B != null) && (recur(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B));
    }

    boolean recur(TreeNode A, TreeNode B){
        if(B == null) return true;
        if(A == null || A.val != B.val) return false;
        return recur(A.left, B.left) && recur(A.right, B.right);
    }
}
27:二叉树的镜像

思路:递归,一次交换每个节点的左右节点即可得到镜像。思路比较简单,但是注意第三行一定要写,低轨道叶结点需要终止。

唉,递归写起来简单,想明白能自己写真的好难。

class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        if(root == null) return null; // 这个不能少 因为到进行递归 遇到叶结点终止一次递归
        //设置一个temp 类同最简单的交换ab位置
        TreeNode temp = root.left;
        root.left = mirrorTree(root.right);
        root.right = mirrorTree(temp);
        return root;
    }
}
28:对称的二叉树

思路:递归。对称的条件:

  • 对称的两个节点的val相等
  • 对称的两个节点的左右节点满足:l.left == r.right && l.right == r.left

类同上一题

class Solution {
    public boolean isSymmetric(TreeNode root) {
        return root == null ? true : check(root.left, root.right);
    }
    public boolean check(TreeNode l, TreeNode r){
        if(l == null && r == null) return true;
        if(l == null || r == null || l.val != r.val) return false;
        return check(l.left, r.right) && check(l.right, r.left);
    }
}
树的题得好好看看!!!
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1007502.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号