《我陪你刷剑指Offer》

24. 反转链表

遍历的思路就是头插法,递归的思路就是先到尾部,然后再指向前方即可!

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        while(cur!=null){
            ListNode next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }

        return pre;
    }
}



class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null || head.next == null) return head;

        ListNode newhead = reverseList(head.next);
        head.next.next = head;
        head.next = null;

        return newhead;
    }
}

03. 数组中重复的数字

思路就是先用一个map进行存储,然后如果遇到一样的就直接取出来就可以了

class Solution {
    public int findRepeatNumber(int[] nums) {
        Map<Integer,Integer> table = new HashMap<>();
        for(int i =0;i<nums.length;i++){
            if(table.containsKey(nums[i])) return nums[i];
            table.put(nums[i],i);
        }
        return -1;
    }
}

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

思路就是让一个指针先走到3,然后再让后面的指针走到3的时候停下来就可以了

class Solution {

    public ListNode getKthFromEnd(ListNode head, int k) {
       ListNode quick = head,show = head;
       for(int i = 0; i < k ;i++ ){
           quick = quick.next;
       }
       while(quick!=null){
           quick = quick.next;
           show = show.next;
       }
       return show;
    }

}

09. 用两个栈实现队列

加的时候直接加到第一个栈里面去,取出来的时候判断一下第二个栈是否为空,空的话就把数据注入进来然后再弹出就可以了

class MyQueue {
    Stack<Integer> stack1;
    Stack<Integer> stack2;
  
    public MyQueue() {
        stack1 = new Stack<>();
        stack2 = new Stack<>();
    }
    
    public void push(int x) {
        stack1.push(x);
    }
    
    //加入的时候判读一下就好了!
    public int pop() {
        if(stack2.empty()){
            while(!stack1.empty()){
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }
    
    public int peek() {
            if(stack2.empty()){
            while(!stack1.empty()){
                stack2.push(stack1.pop());
            }
        }
        return stack2.peek();
    }
    
    public boolean empty() {
        return stack1.empty()&&stack2.empty();
    }
}

42. 连续子数组的最大和

看到这个题只要想到累计想加就可以搞定了

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

       return res;
    }
}

38. 字符串的排列

跟数字全排列一模一样的格式,唯一的区别就是不用返回已经添加了的字符

class Solution {

    public String[] permutation(String s) {
        Set<String> res = new HashSet<>();
        qpl(res,"",s.toCharArray(),new boolean[s.length()]);
        return res.toArray(new String[res.size()]);
    }

    void qpl(Set<String> res,String temp,char[] chars,boolean[] visted){
        if(temp.length() == chars.length) res.add(temp);

        for(int i =0;i< chars.length;i++){
            if(visted[i]) continue;
            visted[i] = true;
            qpl(res, temp+chars[i], chars, visted);
            visted[i] = false;
        }
    }
}

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;
        }
    }
}

29. 顺时针打印矩阵

没啥好说的,还是左上右下,记得数组的要构造数组,list的构造list然后初始化一下,左上右下就完了

class Solution {
    public int[] spiralOrder(int[][] matrix) {
        if(matrix == null || matrix.length == 0) return new int[0];
        int left = 0,up = 0,x =0 ;
        int right = matrix[0].length-1,down = matrix.length-1;

        int[] res = new int[(right+1 )* (down +1)];

        while(left<=right && up <= down){
            for(int i = left;i <= right && up<=down ; i++){
                res[x++]  = matrix[up][i];
            }
            up++;
            for(int i = up; i<=down && left<=right ; i++){
                res[x++]  = matrix[i][right];
            }
            right--;
            for(int i = right;i>=left && up<=down ; i--){
                res[x++]  = matrix[down][i];
            }
            down--;
            for(int i = down;i>=up && left<=right ; i--){
                res[x++]  = matrix[i][left];
            }
            left++;
        }
      return res;  
    }
}
   

04. 二维数组中的查找

这一题就是直接比下面第一个数字小的就直接往上面跑,注意边界问题

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

06. 从尾到头打印链表

先计数,再反转,最后再加入即可

class Solution {
    public int[] reversePrint(ListNode head) {
        int count = 0;
        ListNode cur = head;
        while (cur != null){
            count++;
            cur = cur.next;
        }
        int[] res = new int[count];
        count--;
        while (head != null && count >= 0){
            res[count--] = head.val;
            head = head.next;
        }
        return res;
    }
}

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

完美的邂逅,这里就判断一下是否到达了终点即可,如果到达了就重新返回到另一处的起点就可以了

public class Solution {
    //如果到了尾部  就从头开始算
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode A = headA;
        ListNode B = headB;
        
        while(A != B){
            A =  A  == null ? headB : A.next;
            B =  B  == null ? headA : B.next;
        }
        return A;
    } 
}

10- II. 青蛙跳台阶问题

注意用sum来统计前面两次跳台阶的次数,是a和b一直往上跳,因为b在跳的幅度更大,所以b=num

class Solution {
    public int numWays(int n) {
       int a = 1 , b = 1 , num;
       for( int i = 0; i < n ;i ++){
           num = (a + b) % 1000000007;
           //下面两步就是计数的过程
           a = b;
           b = num;
       }  
       return a ;
    }
}

10- I. 斐波那契数列

跟青蛙跳台阶一样的动态规划

class Solution {
    public int fib(int n) {
        //因为数字可以为0,而青蛙必须从1开始跳
        int a = 0, b = 1, sum;
        for(int i = 0; i < n; i++){
            sum = (a + b) % 1000000007;
            a = b;
            b = sum;
        }
        return a;
    }
}

40. 最小的k个数

直接用小顶堆,然后直接排序就可以了

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        PriorityQueue<Integer> zxd = new PriorityQueue<>();

        for(int data : arr){
            zxd.offer(data);
        } 

        int[] res = new int[k];
        for(int i =0; i < k;i++ ){
            res[i] = zxd.poll();
        }

        return res;
    }
}

11. 旋转数组的最小数字

用二分法就可以了

class Solution {
    public int minArray(int[] numbers) {
        int l = 0, r = numbers.length -1;
        while(l < r){
            int mid = (l+r)/2;
            //此时就要去右边找
            if(numbers[mid] > numbers[r]) l = mid+1;
            //此时去左边找
            else if(numbers[mid] < numbers[r]) r = mid;
            else r--;
        }
        return numbers[l];
    }
}

40. 最小的k个数

思路就是直接先存到小顶堆里面,然后一个一个输出就可以了

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        PriorityQueue<Integer> zxd = new PriorityQueue<>();

        for(int data : arr){
            zxd.offer(data);
        } 

        int[] res = new int[k];
        for(int i =0; i < k;i++ ){
            res[i] = zxd.poll();
        }

        return res;
    }
}

13. 机器人的运动范围

技巧性挺强的,但是套路差不多,就是搞一个已经访问了的visited,跟全排列差不多,核心的点就是返回的时候,只用i+1和j+1,然后位数就用取模来判断,如果是大于10就位数-8,相当于+1

class Solution {

    int m , n , k ;

    boolean[][] visited ; 


    public int movingCount(int m, int n, int k) {
        this.m = m;
        this.n = n;
        this.k = k;

        this.visited = new boolean[m][n];

        //假设是从(0,0)开始跑的
        return dfs(0,0,0,0);

    }

    int dfs(int x,int y,int xx,int yy){
        if( x >=m || y >= n || k < xx+yy || visited[x][y]) return 0;
        visited[x][y] = true;
        return 1+dfs(x+1,y,(x+1)%10 != 0? xx+1 : xx-8,yy) + dfs(x,y+1,xx,(y+1)%10 != 0? yy+1 : yy-8);
    }
   
}

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

思路就是滑动窗口,注意一下细节就可以了,右指针一直跑,左指针看情况动

class Solution {
    public int lengthOfLongestSubstring(String s) {
          int res = 0, left = 0, right = 0;
       char[] chars = s.toCharArray();
        Map<Character,Integer> map = new HashMap<>();   

       while(right<chars.length){
          if(map.containsKey(chars[right]))
                left = Math.max(left,map.get(chars[right])+1);
           
           map.put(chars[right], right);
           res = Math.max(res,right - left +1 );
           right++;
       }
       return res;
    }
}

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

约瑟夫环问题,取模来解决即可

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

26. 树的子结构

== 后面的三重判断的意思就是说节点可以为头结点或者他的左右子节点,先找到那个节点,然后进行判断是否是属于dfs的条件==

class Solution {
     public boolean isSubStructure(TreeNode A, TreeNode B) {
       return (A != null && B !=null) && (dfs(A , B )||
       isSubStructure(A.left, B) || isSubStructure(A.right, B)); 
    }
    
    boolean dfs(TreeNode A,TreeNode B){
        if(B == null ) return true;
        if(A == null || A.val != B.val) return false;

        return dfs(A.left,B.left)  &&  dfs(A.right, B.right);
    }
}

63. 股票的最大利润

就是简单的动态规划,用一下max和min就可以做出来了

class Solution {
    public int maxProfit(int[] prices) {
      int min = Integer.MAX_VALUE;
      int res = 0;

        for(int i  = 0 ; i < prices.length ; i++ ){
             min = Math.min(min,prices[i]);
             res = Math.max(res,prices[i] - min);

        }


        return res;
    }
}

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

直接可以用二分法,找到最后的那个下标就可以了

class Solution {
    public int missingNumber(int[] nums) {
         int i = 0 , j = nums.length -1 ;
        
        
        while( i  <=   j){
            int m = (i + j) / 2;

            if(nums[m] == m) i = m + 1;
            else j = m - 1;

        }


        return i ;
    }   
}


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

用一个队列简单存储一下就可以了

class Solution {
    public int[] levelOrder(TreeNode root) {

    List<Integer> list = new ArrayList<>();
    Queue<TreeNode> queue = new LinkedList<>();
        if(root == null )  return new int[]{};
        queue.offer(root);

        while(!queue.isEmpty()){
           TreeNode temp =  queue.poll();

           list.add(temp.val);

           if(temp.left!=null) queue.offer(temp.left);
           if(temp.right!=null) queue.offer(temp.right);
        }

        int[] res =  new int[list.size()];
        int i = 0;
        for(int data : list){
            res[i++] = data;
        }
        return res;
    }
}

55 - I. 二叉树的深度

就是简单的遍历一下就可以了

class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null )  return   0;

        int left =  maxDepth(root.left);
        int right = maxDepth(root.right);


        return Math.max(left,right) + 1;
    }
}

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

Hash表的key都是唯一的,后面的会刷新掉前面key的value,只要包含,标记为false就可以了

class Solution {
    public char firstUniqChar(String s) {
        Map<Character,Boolean> map =  new HashMap<>();
        char[] chars = s.toCharArray();
        for(int i = 0 ;i < s.length() ;i++){
            map.put(chars[i],!map.containsKey(chars[i]));
        }
        for(int i = 0 ;i < s.length() ;i++){
            if(map.get(chars[i])) return chars[i];
        }
        return ' ';
    }   
}

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

这一题主要要用到Linkedlist里面的一个removeLast的方法,再一个就是dfs的时候跟标记法一样的,回溯的时候记得取消标记,就可以了,细心一点

class Solution {
    List<List<Integer>>  res = new LinkedList<>();
    LinkedList<Integer> temp = new LinkedList<>();
   
    public List<List<Integer>> pathSum(TreeNode root, int sum) {
        dfs(root,sum);
        return res;
    }

    void dfs(TreeNode root,int sum){
        if(root == null) return;
        temp.add(root.val);
        sum -=root.val;
        if(root.left == null && root.right == null && sum == 0 )
            res.add(new LinkedList(temp));
        dfs(root.left,sum);
        dfs(root.right,sum);
        temp.removeLast();
    }
}

45. 把数组排成最小的数

思想就是先把数组转换成字符串组,然后进行比大小,交换,最后用一个builder加上去就可以了

class Solution {
    public String minNumber(int[] nums) {
       String[] res = new String[nums.length];
       for(int i  = 0 ; i  < nums.length ;i++){
           res[i] = String.valueOf(nums[i]);
       }
       Arrays.sort(res,(x,y)->(x+y).compareTo(y+x));
       StringBuilder  arr = new StringBuilder ();
       for(String data :res){
           arr.append(data);
       }
       return arr.toString();
    }
}

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

这个过程就是中序遍历的过程,首先递归到最左下角,然后判断如果next不存在的话,就令一下当前节点为头结点,然后如果存在就把指针进行指向一下就可以了,最后再考虑右边

class Solution {

   Node next,head;
    
    public Node treeToDoublyList(Node root) {
       if(root == null) return null;

       dfs(root); 

       head.left = next;
       next.right = head;

       return head;
    }
    
    void dfs(Node node){
        if(node == null) return;
        //先到达最左边
        dfs(node.left);
        if(next == null){
            head = node;
            next = head;
        }else{
            node.left = next;
            next.right = node;
            next = node;
        }
        dfs(node.right);
    }

}

05. 替换空格

比较简单,就是直接用个Stirngbuilder就可以了

class Solution {
    public String replaceSpace(String s) {
        char[] chars = s.toCharArray();
        StringBuilder res = new StringBuilder();

        for(char data : chars){
            if(data == ' '){
                res.append("%20");
            }else{
                res.append(data);
            }
        }

        return res.toString();
    }
}

58 - II. 左旋转字符串

比较简单,还是字符串拼接

class Solution {
    public String reverseLeftWords(String s, int n) {
        StringBuilder res = new StringBuilder();

        for(int i = n ;i < s.length(); i++){
            res.append(s.charAt(i));
        }

        for(int i  = 0;i < n ;i++ ){
            res.append(s.charAt(i));
        }

        return res.toString();
    }
}

46. 全排列

思路就是装一装,把走过的路给标记下来,然后装在一个临时的temp里面,走的时候记得拿走就可以了

class Solution {

    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        ArrayList<Integer> temp = new ArrayList<>();
        boolean[] visited = new boolean[nums.length];
        if(nums.length == 0) return res;

        dfs(res,temp,visited,nums);
        

        return res;
    }

    void dfs(List<List<Integer>> res,ArrayList<Integer> temp,boolean[] visited,int[] nums){
        if(temp.size() == nums.length) res.add(new ArrayList(temp));

        for(int i = 0; i < nums.length ; i++){
            if(visited[i]) continue;
            visited[i] = true;
            temp.add(nums[i]);
            dfs(res,temp,visited,nums);
            visited[i] = false;
            temp.remove(temp.size() - 1);
        }
    }

}

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

思路就是一直往下跑,然后左边没跑到就返回右边,右边没跑到就返回左边,然后跑到了就返回root,大致就是这样的一个思路

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
       if(root == null || p == root || q == root) return root;

        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);

        if(left == null) return right;
        if(right == null) return left;

       return root;
    }
}

27. 二叉树的镜像

思想就是先递归到底层,然后交换一下就可以了

class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        if(root == null ) return root;

        TreeNode temp = mirrorTree(root.right);
        root.right = mirrorTree(root.left);
        root.left = temp;

        return root;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值