刷题记录之剑指offer

(还差几题,感觉没太大意义就不写了(

文章目录

2020.10.16

剑指 Offer 68 - I. 二叉搜索树的最近公共祖先

二叉搜索树的最近公共祖先

二叉查找树:根节点的值大于其左子树中任意一个节点的值,小于其右节点中任意一节点的值,这一规则适用于二叉查找树中的每一个节点。

附加:二叉查找树要删除的节点既有左节点又有右节点,在这种情况下,我们只需要将找到待删节点的右子树中值最小的节点,将其删除并且获取其值,并用其值替换待删节点的值即可。【该值一定位于该右子树的最左子节点

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null){
            return root;
        }
        if(root.val > p.val && root.val > q.val){
            return lowestCommonAncestor(root.left,p,q);
        }
        if(root.val < p.val && root.val < q.val){
            return lowestCommonAncestor(root.right,p,q);
        }
        return root;   
    }
}

2020.10.17

剑指 Offer 68 - II. 二叉树的最近公共祖先

二叉树不一定有序,不能用通过节点的值来比较。
节点p,q的最近公共祖先的两种情况:

  • p,q在root两侧
  • p,q在root一侧(要么都在root左子树,要么都在右子树),此时p=root或q=root
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null){
            return null;
        }
        if(p == root || q == root){
            return root;
        }
        TreeNode left = lowestCommonAncestor(root.left,p,q);
        TreeNode right = lowestCommonAncestor(root.right,p,q);
        if(right == null)return left;
        if(left == null)return right;
        return root;
        
    }
}

剑指 Offer 27. 二叉树的镜像

通过递归来交换每个节点的左右子树。

class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        if(root == null)return null;
        mirrorTree(root.left);
        mirrorTree(root.right);
        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;
        return root;
    }
}

剑指 Offer 28. 对称的二叉树

对称的二叉树的左子树,二叉树左边的左边要和二叉树右边的右边相同,一左边的右边要和右边的左边相同。(同时满足)
因此递归传递左左和右右左右和右左
递归停止条件:左右节点都为空(倒底了都长得一样 ->true)或者只有一个为空(说明此时不相等 ->false)

class Solution {
    public boolean isSymmetric(TreeNode root) {
       if(root == null)return true;
       return test(root.left,root.right);
    }
    public boolean test(TreeNode a,TreeNode b){
        if(a == null && b == null)return true;
        if(a == null || b == null)return false;
        if(a.val != b.val)return false;
        return test(a.left,b.right)&&test(a.right,b.left);  
    }  
}

剑指 Offer 29. 顺时针打印矩阵

只会弱智模拟(…)

class Solution {
    public int[] spiralOrder(int[][] matrix) {
        int n = matrix.length;
        if(n == 0)return new int[0];
        int m = matrix[0].length;
        int[] result = new int[n*m];
        int index = 0;
        int left = 0,top = 0,right = m-1,botton = n-1;
        while(true){
            for(int i=left;i<=right;i++){
                result[index++] = matrix[top][i];
            }
            if(++top > botton){
                break;
            }
            for(int i=top;i<=botton;i++){
                result[index++]=matrix[i][right];
            }
            if(--right < left){
                break;
            }
            for(int i=right;i>=left;i--){
                result[index++]=matrix[botton][i];
            }
            if(--botton<top){
                break;
            }
            for(int i=botton;i>=top;i--){
                result[index++]=matrix[i][left];
            }
            if(++left>right){
                break;
            }
        }
        return result;
    }
}

剑指 Offer 30. 包含min函数的栈

因为要求调用 min、push 及 pop 的时间复杂度都是 O(1),因此需要辅助栈来保存所有 非严格降序 的元素,则栈 A 中的最小元素始终对应栈 B 的栈顶元素,即 min() 函数只需返回栈 B 的栈顶元素即可。

class MinStack {
    Stack<Integer> a,b;

    /** initialize your data structure here. */
    public MinStack() {
        a = new Stack<>();
        b = new Stack<>();
    }
    
    public void push(int x) {
        a.push(x);
        if(b.empty()||b.peek()>=x)b.push(x);
    }
   
    public void pop() {
        if(a.pop().equals(b.peek()))
            b.pop();
    }
    public int top() {
        return a.peek();
    }
    public int min() {
        return b.peek();
    }
}

剑指 Offer 32 - II. 从上到下打印二叉树 II

方法一:采用递归层序遍历

class Solution {
    List<List<Integer>> li = new ArrayList();
    public List<List<Integer>> levelOrder(TreeNode root) {
        levelOrder(root,0);
        return li;
    }
    public void levelOrder(TreeNode r,int k){
        if(r != null){
            if(li.size() <= k)li.add(new ArrayList());
            li.get(k).add(r.val);
            levelOrder(r.left,k+1);
            levelOrder(r.right,k+1);
        }
    }
}

方法二:BFS

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> li = new ArrayList();
        Queue<TreeNode> queue = new LinkedList<>();
        if(root != null)queue.add(root);
        while(!queue.isEmpty()){
             List<Integer> tmp= new ArrayList();
             for(int i=queue.size();i>0;i--){
                TreeNode node = queue.poll();
                tmp.add(node.val);
                if(node.left != null)queue.add(node.left);
                if(node.right != null)queue.add(node.right);
             }
             li.add(tmp);
        }
        return li;
    }
}

剑指 Offer 39. 数组中出现次数超过一半的数字

啊这,因为出现次数超过一半,所以排序后中间那个数字一定就是答案

class Solution {
    public int majorityElement(int[] nums) {
        Arrays.sort(nums);
        return nums[nums.length/2];
    }
}

看见另一个方法,摩尔投票法,我愿称之为同归于尽法,两个数比较,如果不相同就同归于尽,最后留下了的那个数就是答案。

class Solution {
    public int majorityElement(int[] nums) {
        int res = 0,count = 0;
        for(int i=0;i<nums.length;i++){
            if(count == 0){
                res = nums[i];
                count++;
            }
            else{
                if(res == nums[i])count++;
                else count--;
            }
        }
        return res;
    }
}

剑指 Offer 40. 最小的k个数

啊这,用sort倒是很弱智简单,但复习学习一下堆.
用一个大根堆实时维护数组的前 k小值。首先将前 k 个数插入大根堆中,随后从第 k+1 个数开始遍历,如果当前遍历到的数比大根堆的堆顶的数要小,就把堆顶的数弹出,再插入当前遍历到的数。最后将大根堆里的数存入数组返回.

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        int[] res = new int[k];
        if (k == 0) { 
            return res;
        }
        PriorityQueue<Integer> li = new PriorityQueue<Integer>(new Comparator<Integer>(){
            public int compare(Integer a,Integer b){
                return b-a;
            }
        });
        for(int i=0;i<k;i++){
            li.add(arr[i]);
        }
        for(int i=k;i<arr.length;i++){
            if(arr[i] < li.peek()){
                li.poll();
                li.add(arr[i]);
            }
        }
        for(int i=0;i<k;i++){
            res[i] = li.poll();
        }
        return res;
    }
}

剑指 Offer 42. 连续子数组的最大和

动态规划
dp 的最优子结构是什么,进而推出状态定义和转移方程。数组的子结构通常就是子数组,而本题的最优子结构容易想到是 子数组的连续最大和 ,然后为了保证转移方程的有效性,因此肯定是要以 nums[i] 为结尾的 。

class Solution {
    public int maxSubArray(int[] nums) {
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        int maxx = dp[0];
        for(int i=1;i<nums.length;i++){
            dp[i] = Math.max(dp[i-1]+nums[i],nums[i]);
            maxx = Math.max(maxx,dp[i]);
        }
        return maxx;
    }
}

剑指 Offer 50. 第一个只出现一次的字符

class Solution {
    public char firstUniqChar(String s) {
        char ans =' ';
        int[] vis = new int[27];
        for(char i : s.toCharArray()){
            vis[i-'a']++;
        }
         for(char i : s.toCharArray()){
            if(vis[i-'a'] == 1){
                ans = i;
                break;
            }
        }
        return ans;
    }
}
class Solution {
    public char firstUniqChar(String s) {
        for(int i=0;i<s.length();i++){
            char ch = s.charAt(i);
            if(s.indexOf(ch)==i && s.indexOf(ch,i+1)==-1){
                return s.charAt(i);
            }
        }
        return ' ';
    }
}

剑指 Offer 52. 两个链表的第一个公共节点

神仙题解:使用双指针,使用两个指针 node1,node2 分别指向两个链表 headA,headB 的头结点,然后同时分别逐结点遍历,当 node1 到达链表 headA 的末尾时,重新定位到链表 headB 的头结点;当 node2 到达链表 headB 的末尾时,重新定位到链表 headA 的头结点。

不是c.next而是c,巧妙解决不相交的情况。

if(c != null)c=c.next;
else c=b;
if(d != null)d=d.next;
 else d=a;
public class Solution {
    public ListNode getIntersectionNode(ListNode a, ListNode b) {
        ListNode c = a;
        ListNode d = b;
        while(c != d){
            if(c != null)c=c.next;
            else c=b;
            if(d != null)d=d.next;
            else d=a;
        }
        return c;
    }
}

还可以通过集合把第一个链表的节点全部存放到集合set中,然后遍历第二个链表的每一个节点,判断在集合set中是否存在,如果存在就直接返回这个存在的结点。如果遍历完了,在集合set中还没找到,说明他们没有相交,直接返回null即可。


剑指 Offer 53 - I. 在排序数组中查找数字 I

排序数组中的搜索问题,首先想到 二分法 解决。

class Solution {
    public int search(int[] nums, int target) {
        int left = 0,right = nums.length-1;
        int ans = 0;
        while(left<right){
            int mid = (left+right)/2;
            if(nums[mid]>=target){
                right = mid;
            }
            if(nums[mid] < target){
                left = mid+1;
            }
        }
        while(left < nums.length && nums[left++]==target){
            ans++;
        }
        return ans;
    }
}

剑指 Offer 53 - II. 0~n-1中缺失的数字

弱智行为大赏:(用这种思想不香吗,非要二分(

class Solution {
    public int missingNumber(int[] nums) {
        int sum = nums.length*(nums.length+1)/2;
        for(int i=0;i<nums.length;i++){
            sum-=nums[i];
        }
        return sum;
    }
}
class Solution {
    public int missingNumber(int[] nums) {
        int left = 0,right = nums.length-1;
        while(left <= right){
            int mid = (left+right)/2;
            if(nums[mid] == mid)left = mid+1;
            else right = mid-1;
        }
        return left;
    }
}


10.18

剑指 Offer 24. 反转链表

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode result = null;
        while(head != null){
            ListNode tmp = new ListNode(head.val);
            tmp.next = result;
            result = tmp;
            head = head.next;
        }
        return result;
    }
}

剑指 Offer 25. 合并两个排序的链表

递归:

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if(l1 == null)return l2;
        if(l2 == null)return l1;
        if(l1.val <= l2.val){
            l1.next = mergeTwoLists(l1.next,l2);
            return l1;
        }else{
            l2.next = mergeTwoLists(l1,l2.next);
            return l2;
        }
    }
}

迭代

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode res = new ListNode(-1);
        ListNode result = res;
        while(l1 != null && l2 != null){
            if(l1.val <= l2.val){
                res.next = l1;
                res = res.next;
                l1 = l1.next;
            }else{
                res.next = l2;
                res = res.next;
                l2 = l2.next;
            }
        }
        if(l1 == null){
            res.next = l2;
        }else{
           res.next = l1;
        }
        return result.next;
    }
}

剑指 Offer 65. 不用加减乘除做加法

位运算

如果是十进制的话,我们是如何完成加法计算的?
15 + 12 = ?
个位数和十位数的数字分别相加先不管进位的问题,
2 + 5 = 7;
1 + 1 = 2;
所以得到结果 27。

计算产生进位的数字 这里有进位吗?没有,那么就是0
把上面两步的结果进行相加:27 + 0 = 27;

99 + 111 = ?
个、十、百位 的数字分别相加先不管进位的问题:
个位:9 + 1 = 0
十位:9 + 1 = 0
百位:0 + 1 = 1
得到临时结果:100
计算进位的数字:
1 + 9 = 10;
10 + 90 = 100;
得到进位结果:110
相加得到结果
100 + 110 = 210

如何用二进制完成以上的步骤?

问题1: 二进制的加法利用以上的步骤可以得到正确的结果吗?
12 二进制:1100
15 二进制:1111
各位置上的数字分别相加先不管进位的问题:
1100 + 1111 = 0011
得到临时二进制结果:0011
计算进位的数字:
0100 + 0100 = 1000
1000 + 1000= 10000
得到进位结果:11000
相加得到结果
0011 + 11000 = 11011(十进制:27)

问题2:第一步骤不用加法如何得到相同结果?异或
异或:相同为0,相异为1
1100 ^ 1111 = 0011
问题3:第二步骤不用加法如何得到相同结果?相与,左移一位
如果一个位置上的数字相遇能得到1 ,那么表示,位置上的数字都是1,然后在往左移动一位,就是步骤二 进位得到的结果
(1100 & 1111) << 1 = 11000

class Solution {
    public int add(int a, int b) {
        while(b != 0){
            int c = (a&b)<<1;
            a = a ^ b;
            b = c;
        }
        return a;
    }
}

剑指 Offer 59 - I. 滑动窗口的最大值

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums == null || nums.length == 0)return new int[0];
        int[] ans = new int[nums.length-k+1];
        LinkedList<Integer> list = new LinkedList<>();
        int index = 0;
        for(int i = 0;i<nums.length;i++){
            while(!list.isEmpty() && nums[list.getLast()] < nums[i]){
                list.removeLast();
            }
            list.offerLast(i);
            if(i >= k-1){
                if(list.getFirst() <= i-k){
                    list.removeFirst();
                }
                ans[index++] = nums[list.getFirst()];
            }
        }
        return ans;
    }
}


10.19

剑指 Offer 05. 替换空格

自己的弱智写法

class Solution {
    public String replaceSpace(String s) {
        String ans = "";
        for(char i : s.toCharArray()){
            if(i != ' '){
                ans += i;
            }
            else{
                ans += "%20";
            }
        }
        return ans;
    }
}

其他题解法:

class Solution {
    public String replaceSpace(String s) {
        char[] ch = new char[s.length()*3];
        int index = 0;
        for(int i=0;i<s.length();i++){
            char j = s.charAt(i);
            if(j == ' '){
                ch[index++] = '%';
                ch[index++] = '2';
                ch[index++] = '0';
            }else{
                ch[index++] = j;
            }
        }
        String ans = new String(ch,0,index);
        // String ans = String.valueOf(ch,0,index);
        //换成这个也行
        return ans;
    }
}

剑指 Offer 10- I. 斐波那契数列

递归超时了((

class Solution {
    public int fib(int n) {
        if(n == 0 || n == 1)return n;
        int a = 0,b = 1;
        for(int i = 2;i<= n;i++){
            b = a + b;
            a = b - a;
            b %= 1000000007;
        }
        return b;
    }
}

题解还有使用动态规划,dp[i]表示第i项的斐波那契数列的值

class Solution {
    public int fib(int n) {
        if(n == 0 || n == 1)return n;
        int[] dp = new int[n+1];
        dp[0] = 0;
        dp[1] = 1;
        for(int i = 2;i<=n;i++){
            dp[i] = (dp[i-1] + dp[i-2])%1000000007;
        }
        return dp[n];
    }
}

剑指 Offer 10- II. 青蛙跳台阶问题

有一说一,我恨dp
多少种可能性 的题目一般都有 递推性质

class Solution {
    public int numWays(int n) {
        if(n <= 1)return 1;
        int[] dp = new int[n+1];
        dp[0] = 1;
        dp[1] = 1;
        for(int i = 2;i<=n;i++){
            dp[i] = (dp[i-1]+dp[i-2])%1000000007;
        }
        return dp[n];
    }
}

剑指 Offer 11. 旋转数组的最小数字

class Solution {
    public int minArray(int[] nums) {
        int ans = nums[0];
        for(int i = nums.length-1;i>=0;i--){
            if(nums[i] <= ans){
                ans = nums[i];
            }
            else{
                break;
            }
        }
        return ans;
    }
}

看题解才发现又忘了可以用二分…

class Solution {
    public int minArray(int[] nums) {
        int l = 0,r = nums.length-1;
        while(l < r){
            int mid = ((r-l)>>1) + l;
            if(nums[mid] < nums[r]){
                r = mid;
            }
            else if(nums[mid] > nums[r]){
                l = mid+1;
            }
            else{
                r--;
            }
        }
        return nums[l];
    }
}

剑指 Offer 54. 二叉搜索树的第k大节点

中序遍历二叉搜索树是有有序的

class Solution {
    ArrayList<Integer> li = new ArrayList<Integer>();
    public int kthLargest(TreeNode root, int k) {
        insertNode(root);
        return li.get(li.size()-k);
    }
    public void insertNode(TreeNode root){
        if(root == null)return;
        if(root.left != null)insertNode(root.left);
        li.add(root.val);
        if(root.right != null)insertNode(root.right);
    }
}



10.20

剑指 Offer 32 - I. 从上到下打印二叉树

List list = new ArrayList();
list不能直接转换为int[],因为list.toArray(指定数组)应该传入泛型参数,但泛型必须是引用类型,不能是基本类型。
List转换成int[]数组

class Solution {
    public int[] levelOrder(TreeNode root) {
        List<Integer> list = new ArrayList<Integer>();
        Queue<TreeNode> queue = new LinkedList<>();
        if(root != null)queue.add(root);
        while(!queue.isEmpty()){
            for(int i = queue.size()-1;i>=0;i--){
                TreeNode node = queue.poll();
                list.add(node.val);
                if(node.left != null)queue.add(node.left);
                if(node.right != null)queue.add(node.right);
            }
        }
        int[] ans = new int[list.size()];
        for(int i = 0;i<list.size();i++){
            ans[i] = list.get(i);
        }
        return ans;  
    }
}

剑指 Offer 58 - II. 左旋转字符串

遍历拼接

class Solution {
    public String reverseLeftWords(String s, int n) {
        char[] r = new char[s.length()];
        int index = 0;
        for(int i = n;i < s.length();i++){
            r[index++] = s.charAt(i);
        }
        for(int i = 0;i<n;i++){
            r[index++] = s.charAt(i);
        }
        return String.valueOf(r);
    }
}

求余运算

class Solution {
    public String reverseLeftWords(String s, int n) {
        StringBuilder ans = new StringBuilder();
        for(int i = n;i<s.length()+n;i++){
            ans.append(s.charAt(i%s.length()));
        }
        return ans.toString();
    }
}

剑指 Offer 03. 数组中重复的数字

利用set的key唯一

class Solution {
    public int findRepeatNumber(int[] nums) {
        HashSet<Integer> set = new HashSet<>();
        for(int i : nums){
            if(set.contains(i))return i;
            set.add(i);
        }
        return -1;
    }
}


10.21

剑指 Offer 55 - I. 二叉树的深度

递归

class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null)return 0;
        int ans = serach(root,0);
        return ans;
    }
    public int serach(TreeNode root,int k){
        if(root == null)return 0;
        int l =serach(root.right,k)+1;
        int r = serach(root.left,k)+1;
        if(l > r )return l;
        else return r;

    }
}

BFS,注意int n = queue.size();, 不要放在循环条件里面,因为会改变!!

class Solution {
    public int maxDepth(TreeNode root) {
        int ans = 0;
        Queue<TreeNode> queue = new LinkedList<>();
        if(root != null)queue.add(root);
        while(!queue.isEmpty()){
            ans++;
            int n = queue.size();
            for(int i = 0;i<n;i++){
                TreeNode node = queue.poll();
                if(node.left != null)queue.add(node.left);
                if(node.right != null)queue.add(node.right);
            }
        }
        return ans;
    }
}

剑指 Offer 55 - II. 平衡二叉树

判断该树是不是平衡二叉树:某二叉树中任意节点的左右子树的深度相差不超过1。
后序遍历 + 剪枝
在这里插入图片描述

class Solution {
    public boolean isBalanced(TreeNode root) {
        if(search(root) == -1)return false;
        return true;
    }
    public int search(TreeNode root){
        if(root == null)return 0;
        int l = search(root.left);
        if(l == -1)return-1;
        int r = search(root.right);
        if(r == -1)return -1;
        if(Math.abs(l-r) <= 1)return Math.max(l,r)+1;
        else return -1;
    }
}

剑指 Offer 56 - I. 数组中数字出现的次数

class Solution {
    public int[] singleNumbers(int[] nums) {
        HashSet<Integer> set = new HashSet<Integer>();
        for(int i : nums){
            if(!set.contains(i)){
                set.add(i);
            }else{
                set.remove(i);
            }
        }
        Iterator<Integer> it = set.iterator();
        int[] ans = new int[2];
        int index = 0;
        while(it.hasNext()){
            ans[index++] = it.next();
        }
        return ans;
    }
}

如果除了一个数字以外,其他数字都出现了两次,那么如何找到出现一次的数字?
全员进行异或操作即可。

那么这一方法如何扩展到找出两个出现一次的数字呢?
如果我们可以把所有数字分成两组,使得:

  • 两个只出现一次的数字在不同的组中;
  • 相同的数字会被分到相同的组中。
    那么对两个组分别进行异或操作,即可得到答案的两个数字。
    在这里插入图片描述
class Solution {
    public int[] singleNumbers(int[] nums) {
        int ret = 0;
        for (int n : nums) {
            ret ^= n;
        }
        int div = 1;
        while ((div & ret) == 0) {
            div <<= 1;
        }
        int a = 0, b = 0;
        for (int n : nums) {
            if ((div & n) != 0) {
                a ^= n;
            } else {
                b ^= n;
            }
        }
        return new int[]{a, b};
    }
}



10.22

剑指 Offer 22. 链表中倒数第k个节点

class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode frontNode = head,behindNode = head;
        while(frontNode != null && k > 0){
            frontNode = frontNode.next;
            k--;
        }
        while(frontNode != null){
            frontNode = frontNode.next;
            behindNode = behindNode.next;
        }
        return behindNode;
    }
}

剑指 Offer 26. 树的子结构

如果B为A的子结构,则B可能为A的任意一个节点
1.遍历A的每个节点
2.判断A中 以 NA为根节点的子树是否包含树 B

class Solution {
//遍历
    public boolean isSubStructure(TreeNode A, TreeNode B) {
        if(A == null || B == null)return false;
        return dfs(A,B) || isSubStructure(A.left,B) || isSubStructure(A.right,B);
    }
    //判断是否为子结构
    public boolean dfs(TreeNode A,TreeNode B){
        if(B == null)return true;
        if(A == null)return false;
        if(A.val != B.val)return false;
        boolean flag1 = dfs(A.left,B.left);
        if(flag1 == false)return false;
        boolean flag2 = dfs(A.right,B.right);
        if(flag2 == false)return false;
        return true;
    }
}



10.23

剑指 Offer 17. 打印从1到最大的n位数

class Solution {
    public int[] printNumbers(int n) {
        int end = (int)Math.pow(10,n)-1;
        int[] ans = new int[end];
        int index = 0;
       for(int i = 0;i<end;i++){
           ans[i] = i+1;
       }
        return ans;
    }
}

字符串模拟加法(大数)

    public void printNumbers(int n) {
        StringBuilder ans = new StringBuilder("");
        
        /给每个位数赋值0
        for(int i = 0;i<n;i++)ans.append("0");
        /只要位数不超过N,就一直使用相加方法
        while(addString(ans)){
            int start = 0;/表示不为0的最高位
            for(int i = 0;i< ans.length();i++){
                if(ans.charAt(i) != '0'){
                    start = i;/找到就break
                    break;
                }
            }
            /打印,从start到n
            System.out.print(ans.substring(start,ans.length())+ " ");
        }
    }
    public boolean addString(StringBuilder s){
        int n = s.length();/n的长度
        int stackover = 0;/某位上是否有来自上一位的进位
        boolean flag = true;/表示位数还在N以内,没有超过范围
        for(int i = n-1;i >= 0;i--){
            /diff表示 此时位数上的那个数字 + 上一位的进位
            int diff = s.charAt(i) - '0' + stackover;
            if(i == n-1){
                diff++;/如果此时的位数为个位,则diff++
            }
            /如果位数上的数字此时大于9,就需要进位
            if(diff >= 10){
                if(i == 0){/如果已经为最高位,说明已经到最后一个数字了
                    flag = false;
                }
                else{
                    stackover = 1;
                    s.setCharAt(i, (char) (diff - 10 +'0'));
                }
            }
            /如果位数上的数字此时小于等于9,不需要进位,直接将此时数字替换成diff
            else{
                s.setCharAt(i, (char) (diff + '0'));
                break;
            }
        }
        return flag;
    }

剑指 Offer 18. 删除链表的节点

class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        ListNode h = head,pre = null;
        if(head == null)return null;
        if(head.val == val)return head.next;
        while(h.val != val){
            pre = h;
            h = h.next;
        }
        pre.next = pre.next.next;
        return head;
    }
}

剑指 Offer 04. 二维数组中的查找

每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。
可以从右上角出发,此时往左(j–)走的数字都比此时的数小,往下(i++)走的数字都比此时的数大。【平衡二叉树】

class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        if(matrix == null || matrix.length == 0)return false;
        int i = 0,j = matrix[0].length -1,m =matrix.length,n = matrix[0].length;
        while(i < m && j >= 0){
            if(matrix[i][j] == target)return true;
            else if(matrix[i][j] > target)j--;
            else i++;
        }
        return false;
    }
}

剑指 Offer 06. 从尾到头打印链表

链表遍历存值在list,然后反转list,利用流操作将list转换为int数组
//还可以利用栈

class Solution {
    public int[] reversePrint(ListNode head) {
        if(head == null)return new int[0];
        ArrayList<Integer> list = new ArrayList<Integer>();
        while(head != null){
            list.add(head.val);
            head = head.next;
        }
        Collections.reverse(list);
        int[] ans = list.stream().mapToInt(Integer::valueOf).toArray();
        return ans;
    }
}



10.24

剑指 Offer 15. 二进制中1的个数

当n&1 = 1时,此时二进制形式中的n的最右位为1
利用这点累加答案,并循环将n右移一位(本题要求把数字 nn 看作无符号数,因此使用 无符号右移 操作, Java 中无符号右移为 “>>>” )

public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        int ans = 0;
        while(n != 0){
            ans += n&1;
            n = n>>> 1;
        }
        return ans;
    }
}

剑指 Offer 16. 数值的整数次方

递归

class Solution {
    public double myPow(double x, int n) {
        if(n == 0)return 1;
        if(n == 1)return x;
        if(n == -1)return 1/x;
        double half = myPow(x,n/2);
        double mod = myPow(x,n%2);
        return half * half *mod;
    }
}

快速幂

快速幂算法

class Solution {
    public double myPow(double x, int n) {
        if(n == 0)return 1;
        if(n == 1)return x;
        if(n == -1)return 1/x;
        
        /一定要是long
        long count = n;
        if(count < 0){
            x = 1/x;
            count = -1 *count;
        }
        double ans = 1;
        while(count > 0){
            /指数为奇数时,将其变成偶数,就是把底数*x
            /(3^5) = (3^4) * (3^1) 
            if(count%2 == 1){/相当于count & 1
                ans = ans *x;
            }
            /相当于count >>= 1
            count/= 2;/每一步都把指数分成两半,而相应的底数做平方运算
            x = x*x;
        }
        return ans;
    }
}

剑指 Offer 38. 字符串的排列

class Solution {
    List<String> res = new ArrayList<>();
    Deque<Character> p = new ArrayDeque<>();
    public String[] permutation(String s) {
        if(s == null || s.length() == 0)return new String[]{};
        char[] array = s.toCharArray();
        Arrays.sort(array);
        boolean[] vis = new boolean[array.length];
        dfs(array,0,vis);
        String[] ans = new String[res.size()];
        int k = 0;
        for(String str : res){
            ans[k++] = str;
        }
        return ans;
    }
    public void dfs(char[] ch,int deep,boolean[] vis){
        if(deep == ch.length){
            StringBuilder s = new StringBuilder();
            for(Character c : p){
                s.append(c);
            }
            res.add(s.toString());
            return;
        }
        for(int i = 0;i<ch.length;i++){
            if(!vis[i]){
                if(i>0&&ch[i] == ch[i-1]&& !vis[i-1]){
                    continue;
                }
                p.addLast(ch[i]);
                vis[i] = true;
                dfs(ch,deep+1,vis);
                p.removeLast();
                vis[i] = false;
            }
        }
    }
}


10.26

剑指 Offer 57 - II. 和为s的连续正数序列

只想到暴力枚举

class Solution {
    public int[][] findContinuousSequence(int target) {
        ArrayList<int[]> res = new ArrayList<>();
        int i = 1,j = 1;
        int k = target/2 + 1;
        int sum = 1;
        while(i <= k){
            if(sum < target){
                j++;
                sum+=j;
            }else if(sum > target){
                sum-=i;
                i++;
            }else{
                int[] nums = new int[j-i+1];
                for(int x = i,y = 0;x <= j ;x++){
                    nums[y++] = x;
                }
                res.add(nums);
                sum-=i;
                i++;
            }
        }
        return res.toArray(new int[0][]);
    }
}

可以用双指针法在这里插入图片描述

class Solution {
    public int[][] findContinuousSequence(int target) {
        ArrayList<int[]> res = new ArrayList<>();
        int sum = 0;
        for(int i = 1,j = 2;i < j ;){
            sum = (i + j) * (j - i+1) / 2;
            if(sum == target){
                int[] nums = new int[j-i+1];
                for(int x = i,y = 0 ;x <= j;x++){
                    nums[y++] = x;
                }
                res.add(nums);
                i++;
            }else if(sum > target){
                i++;
            }else{
                j++;
            }
        }
        return res.toArray(new int[0][]);
    }
}

剑指 Offer 64. 求1+2+…+n

利用and的短路特性(代替if)和递归(代替for)

class Solution {
    public int sumNums(int n) {
        boolean flag = n >0 && (n += sumNums(n-1)) > 0;
        return n;
    }
}

剑指 Offer 35. 复杂链表的复制

class Solution {
    public Node copyRandomList(Node head) {
        if(head == null)return null;
        Node cur = head;   
  /将拷贝节点放到原节点后面,例如1->2->3这样的链表就变成了这样1->1'->2->2'->3->3'
        while(cur != null){
            Node copyNode = new Node(cur.val);
            copyNode.next = cur.next;
            cur.next = copyNode;
            cur = cur.next.next;
        }
        cur = head;
        
        /把拷贝节点的random指针安排上
        while(cur != null){
            if(cur.random != null){
                cur.next.random = cur.random.next;
            }
            cur = cur.next.next;
        }
        Node copyHead = head.next;
        cur = head;
        Node curCopy = head.next;
        
        /分离拷贝节点和原节点,变成1->2->31'->2'->3'两个链表,后者就是答案
        while(cur != null){
            cur.next = cur.next.next;
            cur = cur.next;
            if(curCopy.next != null){
                curCopy.next = curCopy.next.next;
                curCopy = curCopy.next;
            }
        }
        return copyHead;
    }
}

剑指 Offer 56 - II. 数组中数字出现的次数 II

如果一个数字出现三次,那么它的二进制表示的每一位(0或者1)也出现三次。如果把所有出现三次的数字的二进制表示的每一位都分别加起来,那么每一位的和都能被3整除。如果某一位的和能被3整除,那么那个只出现一次的数字二进制表示中对应的那一位是0;否则就是1;

class Solution {
    public int singleNumber(int[] nums) {
        if(nums.length == 0)return -1;
        int[] bitSum = new int[32];/java int类型有32位,其中首位为符号位
        int res = 0;
        for(int num : nums){
            int mask = 1;
            for(int i = 31 ; i >= 0 ;i--){
            /这里同样可以通过num的无符号右移>>>来实现
            /否则带符号右移(>>)左侧会补符号位,对于负数会出错。
                if((mask&num) != 0)bitSum[i]++;
                mask <<= 1;/左移没有无符号、带符号的区别,都是在右侧补0
            }
        }
        for(int i = 0 ;i<32; i++){
            res <<= 1;
            res+=bitSum[i]%3;/这两步顺序不能变,否则最后一步会多左移一次
        }
        return res;
    }
}

剑指 Offer 57. 和为s的两个数字

看见有序第一反应用二分,结果写成双指针…

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int i = 0,j = nums.length-1;
        while(i < j){
            int sum = nums[i]+nums[j];
            if(sum == target)return new int[]{nums[i],nums[j]};
            else if(sum > target)j--;
            else i++;
        }
        return new int[]{};
    }
}

二分法:遍历我们每一个数,然后在后面的数中二分查找对应的差

for(int i = 0;i<nums.length;++i){
            int left = i + 1,right = nums.length-1,e =target - nums[i];
            while(left <= right){
                int mid = left + (right - left)/2;
                if(nums[mid] == e){
                    return new int[]{nums[i],nums[mid]};
                }else if(nums[mid] > e){
                    right = mid -1;
                }else if(nums[mid] < e){
                    left = mid + 1;
                }
            }
        }
        return new int[]{};

10.27

剑指 Offer 07. 重建二叉树[已知前序和中序建树

建树无论是前后建树,或者中后建树,要点都是传递中的序列下标,因为根结点下标都可以根据中进行推算。

class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        int n = preorder.length;
        if(n == 0)return null;
        int rootVal = preorder[0],rootIndex = 0;
        for(int i = 0;i<n;i++){
            if(inorder[i] == rootVal){
                rootIndex = i;
                break;
            }
        }
        TreeNode node = new TreeNode(rootVal);
        node.left = buildTree(Arrays.copyOfRange(preorder,1,1+rootIndex),Arrays.copyOfRange(inorder,0,rootIndex));
        node.right = buildTree(Arrays.copyOfRange(preorder,1+rootIndex,n),Arrays.copyOfRange(inorder,rootIndex+1,n));
        return node;
    }
}

106. 从中序与后序遍历序列构造二叉树^1.28

106. 从中序与后序遍历序列构造二叉树

和上面的差不多,只不过换成根结点一定是后续遍历的最后一个节点

class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        int n = inorder.length;
        if(n == 0)return null;
        int rootVal = postorder[n-1];
        int rootIndex = n-1;
        for(int i = 0 ; i < n ; i++){
            if(inorder[i] == rootVal){
                rootIndex = i;
                break;
            }
        }
        TreeNode root = new TreeNode(rootVal);
        root.left = buildTree(Arrays.copyOfRange(inorder,0,rootIndex),Arrays.copyOfRange(postorder,0,rootIndex));
        root.right = buildTree(Arrays.copyOfRange(inorder,rootIndex+1,n),Arrays.copyOfRange(postorder,rootIndex,n-1));
        return root;
    }
}

剑指 Offer 12. 矩阵中的路径

class Solution {
    int[][] dir = new int[][]{{0,1},{0,-1},{1,0},{-1,0}};
    public boolean exist(char[][] board, String word) {
        char[] sChar = word.toCharArray();
        boolean[][] vis = new boolean[board.length][board[0].length];
        for(int i = 0;i < board.length ;i++){
            for(int j = 0;j<board[0].length;j++){
                if(board[i][j] == sChar[0]){   
                    if(bfs(vis,board,sChar,i,j,0)){
                        return true;
                    }
                   }
                }
            }
        return false;
    }
    public boolean bfs(boolean[][] vis,char[][] board,char[] word,int x,int y,int k){
        if(k == word.length){
            return true;
        }
        if(x < 0 || x >= board.length || y < 0 || y >= board[0].length || vis[x][y] || board[x][y] != word[k]){
            return false;
        }
        vis[x][y] = true;
        boolean flag = false;
        for(int i=0;i<4;i++){
            int tx = x + dir[i][0];
            int ty = y + dir[i][1];
           flag = flag || bfs(vis,board,word,tx,ty,k+1);
        }
        vis[x][y] = false;
        return flag;
    }
}

剑指 Offer 13. 机器人的运动范围

class Solution {
    int[][] dir = new int[][]{{0,1},{0,-1},{1,0},{-1,0}};
    public int movingCount(int n, int m, int k) {
        boolean[][] vis = new boolean[n][m];
        int sum = bfs(n,m,k,0,0,vis);
        return sum;
    }
    public int bfs(int n,int m,int k,int x,int y,boolean[][] vis){
        if(x < 0 ||y < 0 || x >= n || y >= m || vis[x][y] || !add(x,y,k)){
            return 0;
        }
        int flag = 1;
        vis[x][y] = true;
        for(int i = 0;i<4;i++){
            int tx = x + dir[i][0];
            int ty = y + dir[i][1];
            flag += bfs(n,m,k,tx,ty,vis);
        }
        return flag;
    }
    public boolean add(int x,int y,int k){
        int sum = 0;
        while(x != 0){
            sum += x%10;
            x /=10;
        }
        if(sum > k)return false;
        while(y != 0){
            sum += y%10;
            y /=10;
        }
        if(sum > k)return false;
        return true;
    }
}



10.28

剑指 Offer 47. 礼物的最大价值

每次只有两种选择:向右或者向下
动态规划:dp[i][j] = max(dp[i-1][j],dp[i][j-1])+value[i][j];

f(i,j)等于 f(i,j-1) f(i-1,j)中的较大值加上当前单元格礼物价值grid(i,j) 。

可以化简为dp[j] = Math.max(dp[j], dp[j - 1]) + grid[i - 1][j - 1];右边的dp[j]是上一行遍历时候记录下的dp[j]也就是目前遍历到的这个位置的上面的记录,dp[j-1]就是左边的已经更新的记录,其实就是取左边和上面中的最大值,这样一行行遍历下去,对应最后的dp[n]就是遍历到最后一个位置。

class Solution {
    public int maxValue(int[][] g) {
        int n = g.length,m = g[0].length;
        int[] dp = new int[m+1];
        for(int i = 1 ;i <= n;i++){
            for(int j = 1;j <= m;j++){
                dp[j] = Math.max(dp[j],dp[j-1])+g[i-1][j-1];
            }
        }
        return dp[m];
    }
}

剑指 Offer 48. 最长不含重复字符的子字符串

维护一个滑动窗口,并利用hashset的特性

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int res = 0;
        Set<Character> set = new HashSet<>();
        for(int l = 0,r = 0;r < s.length();r++){
            char c = s.charAt(r);
            while(set.contains(c)){
                set.remove(s.charAt(l));
                l++;
            }
            set.add(c);
            res = Math.max(res,r-l+1);
        }
        return res;
    }
}

剑指 Offer 49. 丑数

第一个丑数是1,以后的丑数都是基于前面的小丑数分别乘2,3,5构成的。
我们每次添加进去一个当前计算出来个三个丑数的最小的一个,并且是谁计算的,谁指针就后移一位.
//虽然代码用了dp数组,但是和动规没关系

class Solution {
    public int nthUglyNumber(int n) {
        if(n <= 0)return -1;
        int[] dp = new int[n];
        dp[0] = 1;
        int k2 = 0,k3 = 0,k5 = 0;
        for(int i = 1 ; i < n ;i++){
            dp[i] = Math.min(dp[k2]*2,Math.min(dp[k3]*3,dp[k5]*5));
            if(dp[k2]*2 == dp[i]){
                k2++;
            }
            if(dp[k3]*3 == dp[i]){
                k3++;
            }
            if(dp[k5]*5 == dp[i]){
                k5++;
            }
        }
        return dp[n-1];
    }
}

剑指 Offer 63. 股票的最大利润

弱智逻辑写法

class Solution {
    public int maxProfit(int[] prices) {
        int n = prices.length;
        if(prices == null || n == 0)return 0;
        int res = 0;
        int min = prices[0];
        for(int i = 0 ;i <n;i++){
            if(prices[i] < min){
                min = prices[i];
            }else{
                res = Math.max(res,prices[i]-min);
            }
        }
        return res;
    }
}

动态规划
前i日最大利润=max(前(i−1)日最大利润,第i日价格−前i日最低价格).

class Solution {
    public int maxProfit(int[] prices) {
        int n = prices.length;
        if(n == 0)return 0;
        int[] dp = new int[n+1];
        int minn = prices[0];
        for(int i = 1;i < n ;i++){
            minn = Math.min(minn,prices[i]);
            dp[i] = Math.max(dp[i-1],prices[i]-minn);
        }
        return dp[n-1];
    }
}

10.30

剑指 Offer 45. 把数组排成最小的数

class Solution {
    public String minNumber(int[] nums) {
        if(nums.length == 0)return null;
        List<String> list = new ArrayList<>();
        for(int i : nums){
            list.add(String.valueOf(i));
        }
        Collections.sort(list,new Comparator<String>(){
            public int compare(String a,String b){
                String ab = a+b;
                String ba = b+a;
                return ab.compareTo(ba);
            }
        });
        StringBuilder sb = new StringBuilder();
        for(String s : list){
            sb.append(s);
        }
        return sb.toString();
    }
}

剑指 Offer 21. 调整数组顺序使奇数位于偶数前面

快排

class Solution {
    public int[] exchange(int[] nums) {
        int l = 0;
        int r = nums.length-1;
        while(l < r){
            while(l < r && nums[l]%2 != 0){
                l++;
            }
            while(l < r && nums[r]%2 == 0){
                r--;
            }
            if(l < r){
                int t = nums[l];
                nums[l] = nums[r];
                nums[r] = t;
            }
        }
        return nums;
    }
}

剑指 Offer 58 - I. 翻转单词顺序

class Solution {
    public String reverseWords(String s) {
        String[] str = s.trim().split(" ");
        StringBuilder sb = new StringBuilder();
        for(int i = str.length-1;i>=0;i--){
            if(str[i].equals(" ")||str[i].equals(""))continue;
            sb.append(str[i]);
            sb.append(" ");
        }
        return sb.toString().trim();
    }
}
class Solution {
    public String reverseWords(String s) {
        s = s.trim();
        StringBuilder sb = new StringBuilder();
        int i = s.length()-1;
        int j = i;
        while(i >= 0){
            while(i >= 0 && s.charAt(i) != ' ')i--;
            sb.append(s.substring(i+1,j+1)+" ");
            while(i >= 0 && s.charAt(i) == ' ')i--;
            j = i;
        }
        return sb.toString().trim();
    }
}

剑指 Offer 61. 扑克牌中的顺子

class Solution {
    public boolean isStraight(int[] nums) {
        Arrays.sort(nums);
        int k = 0;
        for(int i=0;i<4;i++){
            if(nums[i]==0){
                k ++;
                continue;
            }
            if(nums[i] == nums[i+1])return false;
        } 
        if(nums[4] == 0)k++;
        return nums[4]-nums[k] <= 4;
    }
}

剑指 Offer 62. 圆圈中最后剩下的数字

链表模拟

class Solution {
    public int lastRemaining(int n, int m) {
        if (n == 0 || m == 0)
            return -1;
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < n; i++)
            list.add(i);
        int c = (m - 1) % n;
        while (list.size() != 1) {
            list.remove(c);
            c = (c + m - 1) % list.size();
        }
        return list.get(0);
    }
}

或者:
在这里插入图片描述

class Solution {
    public int lastRemaining(int n, int m) {
        int flag = 0;   //迭代
        for (int i = 2; i <= n; i++) {
            flag = (flag + m) % i;
        //动态规划的思想,将f(n,m)替换成flag存储
        }
    return flag;
    }
}

10.31

剑指 Offer 60. n个骰子的点数

竟然用动规…这谁顶得住

class Solution {
    public double[] twoSum(int n) {
    /定义一个数组保存所有状态,一维是第n次时,二维为第n次时所有可能情况
        int[][] dp = new int[n+1][6*n+1];
        
        for(int i = 1 ; i <= 6; i++){
            dp[1][i] = 1;/我们只能知道第一次的情况,也是边界条件
        }
        
       /一层循环,每多一个色子的情况
        for(int i = 2 ; i <= n ;i++){
           /二层循环是这么多色子情况下的所有可能和
            for(int j = i ;j <= 6*i;j++){
                for(int k = 1 ; k<=6 &&k<=j;k++){
                    dp[i][j] += dp[i-1][j-k];
                }
            }
        }
        double[] ans = new double[6*n - n +1];
        double sum = Math.pow(6,n);
        for(int i = n;i<=6*n;i++){
            ans[i-n] = dp[n][i]*1.0/sum;
        }
        return ans;
    }
}

剑指 Offer 36. 二叉搜索树与双向链表

中序遍历

class Solution {
    Node pre,head;
    public Node treeToDoublyList(Node root) {
        if(root == null)return null;
        dfs(root);
        head.left = pre;
        pre.right = head;
        return head;
    }
    public void dfs(Node cur){
        if(cur == null)return;
        dfs(cur.left);
        if(pre == null)head = cur;
        else pre.right = cur;
        cur.left = pre;
        pre = cur;
        dfs(cur.right);
    }
}

剑指 Offer 44. 数字序列中某一位的数字

我讨厌数学问题
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Solution {
    public int findNthDigit(int n) {
        int digit = 1;
        long start = 1;
        long count = 9;
        while(n > count){
            n -= count;
            digit++;
            start*=10;
            count = digit*start*9;
        }
        long num = start + (n-1)/digit;
        return Long.toString(num).charAt((n-1)%digit)-'0';
    }
}


11.1

剑指 Offer 09. 用两个栈实现队列

class CQueue {
    LinkedList<Integer> stack1;
    LinkedList<Integer> stack2; 
    public CQueue() {
        stack1 = new LinkedList<>();
        stack2 = new LinkedList<>();
    }
    
    public void appendTail(int value) {
        stack1.add(value);
    }
    
    public int deleteHead() {
        if (stack2.isEmpty()) {
			if (stack1.isEmpty()) return -1;
			while (!stack1.isEmpty()) {
				stack2.add(stack1.pop());
			}
			return stack2.pop();
		} else return stack2.pop();
    }
}

剑指 Offer 31. 栈的压入、弹出序列

class Solution {
    public boolean validateStackSequences(int[] pushed, int[] popped) {
        Deque<Integer> stack = new ArrayDeque<>();
        int i = 0;
        for(int j : pushed){
            stack.push(j);
            while(i < popped.length && !stack.isEmpty() && stack.peek() == popped[i]){
                stack.pop();
                i++;
            }
        }
        return i == popped.length;
    }
}

剑指 Offer 32 - III. 从上到下打印二叉树 III

class Solution {
    List<List<Integer>> list = new ArrayList<>();
    public List<List<Integer>> levelOrder(TreeNode root) {
        level(root,1);
        return list;
    }
    public void level(TreeNode root,int k){
        if(root != null){
            if(list.size() < k)list.add(new ArrayList());
            if(k%2 == 0){
                list.get(k-1).add(0,root.val);
            }else{
                list.get(k-1).add(root.val);
            }
            level(root.left,k+1);
            level(root.right,k+1);
        }
    }
}

剑指 Offer 34. 二叉树中和为某一值的路径

class Solution {
    List<List<Integer>> list = new ArrayList<>();
    public List<List<Integer>> pathSum(TreeNode root, int sum) {
        dfs(root,sum,new ArrayList<>());
        return list;
    }
    public void dfs(TreeNode root,int sum,List<Integer> li){
        if(root == null)return;
        li.add(root.val);
        sum -= root.val;
        if(sum == 0 && root.left == null && root.right == null){
            list.add(new ArrayList<>(li));
        }else{
            dfs(root.left,sum,li);
            dfs(root.right,sum,li);
        }
        li.remove(li.size()-1);
    }
}

11.2

剑指 Offer 66. 构建乘积数组

class Solution {
    public int[] constructArr(int[] a) {
        int len = a.length;
        int[] b = new int[len];
        for(int i = 0,x = 1;i < len ;i++){
            b[i] = x; 
            x *= a[i];
        }
        for(int i = len-1,x=1;i>=0;i--){
            b[i] *= x;
            x *= a[i];
        }
        return b;
    }
}

剑指 Offer 67. 把字符串转换成整数

class Solution {
    public int strToInt(String s) {
        s = s.trim();
        if(s.length() == 0)return 0;
        char[] str = s.toCharArray();
        if(!Character.isDigit(str[0]) && str[0]!='+' && str[0]!='-'){
            return 0;
        }
        int i = 0;
        boolean negative = false;
        if(str[i]=='-'){
            negative = true;
            i++;
        }
        else if(str[i] == '+'){
            i++;
        }
        long res = 0;
        while(i < str.length && Character.isDigit(str[i])){
            res = res*10 + str[i]-'0';
            if(negative == false && res > Integer.MAX_VALUE)return Integer.MAX_VALUE;
            else if(negative == true && -res < Integer.MIN_VALUE)return Integer.MIN_VALUE;
            i++;
        }
        return negative?-(int)res:(int)res;
    }
}

剑指 Offer 14- I. 剪绳子

class Solution {
    public int cuttingRope(int n) {
        if(n <= 3)return n-1;
        int[] dp = new int[n+1];
        dp[1] = 1;
        dp[2] = 2;
        dp[3] = 3;
        for(int i = 4 ;i <= n;i++){
            for(int j = 1 ;j <= i/2;j++){
                dp[i] = Math.max(dp[i],dp[i-j]*dp[j]);
            }
        }
        return dp[n];
    }
}

剑指 Offer 33. 二叉搜索树的后序遍历序列

递归

class Solution {
    public boolean verifyPostorder(int[] postorder) {
        if(postorder.length < 2)return true;
        return order(postorder,0,postorder.length-1);
    }
    public boolean order(int[] postorder,int left,int right){
        if(left >= right)return true;
        int root = postorder[right];
        int k = left;
        while(k < right && postorder[k] < root){
            k++;
        } 
        for(int i = k;i < right ;i++){
            if(postorder[i] < root)return false;
        }
        if(!order(postorder,left,k-1))return false;
        if(!order(postorder,k,right-1))return false;
        return true;
    }
}

倒着遍历后续遍历得到的数组

class Solution {
    public boolean verifyPostorder(int[] postorder) {
        if(postorder.length < 2)return true;
        Deque<Integer> stack = new LinkedList<>();
        int root = Integer.MAX_VALUE;
        for(int i = postorder.length-1 ; i>= 0;i--){
            if(postorder[i] > root)return false;
            while(!stack.isEmpty() && postorder[i] < stack.peek()){
                root = stack.pop();
            }
            stack.push(postorder[i]);
        }
        return true;
    }
}

11.3

剑指 Offer 59 - II. 队列的最大值

class MaxQueue {
    private Deque<Integer> queue;
    private Deque<Integer> help;
    public MaxQueue() {
        queue = new ArrayDeque<>();
        help = new ArrayDeque<>();
    }
    
    public int max_value() {
        return queue.isEmpty() ? -1 : help.peek();
    }
    
    public void push_back(int value) {
        queue.offer(value);
        while(!help.isEmpty() && value > help.peekLast()){
            help.pollLast();
        }
        help.offer(value);
    }
    
    public int pop_front() {
        if(queue.isEmpty())return -1;
        int val = queue.pop();
        if(help.peek() == val){
            help.pop();
        }
        return val;
    }
}

剑指 Offer 41. 数据流中的中位数

11.16

剑指 Offer 37. 序列化二叉树

题目意思是随便你用什么方式,总之把它变成字符串,递归前序遍历也好,层序遍历也随便

public class Codec {
    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        if(root == null){
            return "null,";
        }
        String res = root.val + ",";
        res += serialize(root.left);
        res += serialize(root.right);
        return res;
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        String[] arr = data.split(",");
        Queue<String> queue = new LinkedList<String>();
        for(int i = 0 ;i <arr.length ; i++){
            queue.offer(arr[i]);
        }
        return dfs(queue);
    }
    public TreeNode dfs(Queue<String> q){
        String val = q.poll();
        if(val.equals("null")){
            return null;
        }
        TreeNode r = new TreeNode(Integer.valueOf(val));
        r.left = dfs(q);
        r.right = dfs(q);
        return r;
    }
}

剑指 Offer 41. 数据流中的中位数

维持一个大顶堆和小顶堆,确保:

1、大小顶堆元素数量差小于等于1

2、大顶堆中所有元素均小于小顶堆中元素
在这里插入图片描述

class MedianFinder {
    Queue<Integer> a,b;
    /** initialize your data structure here. */
    public MedianFinder() {
        a = new PriorityQueue<Integer>();// 小顶堆,保存较大的一半
        b = new PriorityQueue<Integer>((x,y)->(y-x));
    }
    
    public void addNum(int num) {
        if(a.size() != b.size()){
            a.add(num);
            b.add(a.poll());
        }
        else{
            b.add(num);
            a.add(b.poll());
        }
    }
    
    public double findMedian() {
        if(a.size() == b.size()){
            return (a.peek()+b.peek())*1.0/2.0;
        }
        else{
            return a.peek();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值