剑指offer刷题笔记-不晓得第几天了

矩阵中的路径(medium)

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。

[["a","b","c","e"],
["s","f","c","s"],
["a","d","e","e"]]

但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。
示例 1:

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true

示例 2:

输入:board = [["a","b"],["c","d"]], word = "abcd"
输出:false

提示:

1 <= board.length <= 200
1 <= board[i].length <= 200

DFS+回溯+剪枝


public boolean exist(char[][] board, String word) {
        boolean[][] flag=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 (solve(board,word,i,j,flag,0))
                    return true;
            }
        }
        return false;
    }

    private boolean solve(char[][] board, String word, int i, int j, boolean[][] flag, int index) {
        if (i<0||i>=board.length||j<0||j>=board[0].length||flag[i][j]||board[i][j]!=word.charAt(index))
            return false;
        if (index==word.length()-1)
            return true;//匹配到word的最后一个字符
        flag[i][j]=true;
        //到了这一步说明上一个字符匹配成功下面开始匹配下一个字符
        boolean f=solve(board,word,i+1,j,flag,index+1)||
                solve(board,word,i-1,j,flag,index+1)||
                solve(board,word,i,j+1,flag,index+1)||
                solve(board,word,i,j-1,flag,index+1);
        flag[i][j]=false;//一次结束
        return f;
    }

二进制中1的个数

请实现一个函数,输入一个整数,输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。

示例 1:

输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 ‘1’。

示例 2:

输入:00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 ‘1’。

示例 3:

输入:11111111111111111111111111111101
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 ‘1’。

今个才晓得原来java的Integer.bitcount(n)可直接得出1的个数(也叫汉明重量)有关源码的分析贴个大佬的解释
https://segmentfault.com/a/1190000015763941

public static int count(int n){
        return Integer.bitCount(n);//原来真的有这个方法,涨见识了....

    }

还有一个很巧妙的方法(总之都是我想不到的方法)
搬运牛客网上评论区的解释
(搬运评论区大佬的解释)
如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减1,那么原来处在整数最右边的1就会变为0,原来在1后面的所有的0都会变成1(如果最右边的1后面还有0的话)。其余所有位将不会受到影响。
举个例子:一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成了1,而前面的1保持不变,因此得到的结果是1011.我们发现减1的结果是把最右边的一个1开始的所有位都取反了。这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000.也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。

public static int count1(int n){
        int num=0;
        while (n!=0){
            num++;
            n=n&(n-1);
        }
        return num;
    }

秒啊~

打印从1到最大的n位数

输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。
不考虑大数的问题:

public int[] printNumbers(int n) {
        int[] result=new int[(int)Math.pow(10,n)-1];
        int i=0,number=1;
        while (i<n){
            i++;
            number*=10;//最大不超过number
        }
        int k=0;
        for (int j = 1; j <number ; j++) {
            result[k]=j;
            k++;
        }
        return result;
    }

这题力扣应该是改简单了,应该考虑大数问题
小tip:(我经常忽略,之前联系蓝桥杯的时候倒是有这个思考) 当遇到有关数的时候,我们要考虑一下是否会出现数据特别大的情况,假如出现,可以通过数组或字符串进行替换处理。
解题思路
由于 n 可能会非常大,因此不能直接用 int 表示数字,而是用 char 数组进行存储。
等哈再写,先把下道题做了
//////////////////////////////分割线
贴上C君写的
使用回溯法得到所有的数

public void print1ToMaxOfNDigits(int n) {
    if (n <= 0)
        return;
    char[] number = new char[n];
    print1ToMaxOfNDigits(number, 0);
}

private void print1ToMaxOfNDigits(char[] number, int digit) {
    if (digit == number.length) {
        printNumber(number);
        return;
    }
    for (int i = 0; i < 10; i++) {
        number[digit] = (char) (i + '0');
        print1ToMaxOfNDigits(number, digit + 1);
    }
}

private void printNumber(char[] number) {
    int index = 0;
    while (index < number.length && number[index] == '0')
        index++;
    while (index < number.length)
        System.out.print(number[index++]);
    System.out.println();
}

删除链表节点

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。

返回删除后的链表的头节点。

注意:此题对比原题有改动

示例 1:

输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.

示例 2:

输入: head = [4,5,1,9], val = 1
输出: [4,5,9]
解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.

说明:

题目保证链表中节点的值互不相同
若使用 C 或 C++ 语言,你不需要 free 或 delete 被删除的节点

题目不难!

public ListNode deleteNode(ListNode head, int val) {
         ListNode listNode=head;
       if (head.val==val){
            listNode=listNode.next;//如果头结点是要删除的结点
            return listNode;
        }
        while (listNode.next.val!=val)
                listNode=listNode.next;
        if (listNode.next.next==null){
            //如果删除的是尾结点
            listNode.next=null;
        }else 
            listNode.next=listNode.next.next;

        return head;
    }

突然真正意识到当时复习考研题目经常会问为什么要设置一个没有实际值的头结点,真的是方便操作啊,这个表就得分好几种情况讨论…

调整数组顺序使奇数位于偶数前面

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。

示例:

输入:nums = [1,2,3,4]
输出:[1,3,2,4] 
注:[3,1,2,4] 也是正确的答案之一。

提示:

1 <= nums.length <= 50000
1 <= nums[i] <= 10000

用快排的思想

public int[] exchange(int[] nums) {
        int i=0,j=nums.length-1;
        while (i<j) {
            while (nums[i] % 2 == 1&&i<j) {
                i++;
            }
            while (nums[j] % 2 == 0&&i<j) {
                j--;
            }
           swap(nums, i, j);
        }
        return nums;
    }
    public static void swap(int[] nums,int i,int j){
        int tmp=nums[i];
        nums[i]=nums[j];
        nums[j]=tmp;
    }

二叉树的深度

输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。

例如:

给定二叉树 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

返回它的最大深度 3 。

提示:

节点总数 <= 10000

一行搞定

public int maxDepth(TreeNode root) {
     return root==null?0:1+Math.max(maxDepth(root.left),maxDepth(root.right));
    }

平衡二叉树

输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。

示例 1:

给定二叉树 [3,9,20,null,null,15,7]

    3
   / \
  9  20
    /  \
   15   7

返回 true 。

示例 2:

给定二叉树 [1,2,2,3,3,null,null,4,4]

       1
      / \
     2   2
    / \
   3   3
  / \
 4   4

返回 false 。

也很简单,用了上面求高度的方法

public int maxDepth(TreeNode root) {
        return root==null?0:1+Math.max(maxDepth(root.left),maxDepth(root.right));
    }
    public boolean isBalanced(TreeNode root) {
        if (root==null)
        return true;
        if (Math.abs(maxDepth(root.left)-maxDepth(root.right))<=1)
            return isBalanced(root.left)&&isBalanced(root.right);
        return false;
        
    }

字符串的排列(medium)

输入一个字符串,打印出该字符串中字符的所有排列。

你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。

示例:

输入:s = “abc”
输出:[“abc”,“acb”,“bac”,“bca”,“cab”,“cba”]

限制:

1 <= s 的长度 <= 8

记得算法的时候学全排列费了好大的劲…
找到了之前复习算法的代码,想到了那一个个泪流满面(并没有)的日子
直接用递归52个用例只过了41个,主要原因是超时了…(牛客上输出竟然让我排序!!)
终于用比较笨的方法做出来了,用的set去重

public static String[] permutation(String s){
        Set<String> set=new TreeSet<>();
        if (s==null||s.length()==0) return null;
        char[] stochar=s.toCharArray();

        pailie(stochar,0,set);

        String[] ans=new String[set.size()];
        int n=0;
        for (String s1:set)
            ans[n++]=s1;
        return ans;
//        return set.toArray(new String[0]);
    }

    private static void pailie(char[] stochar, int i, Set<String> set) {
        if (i==stochar.length-1) {
            set.add(String.valueOf(stochar));
        }else {
            for (int j = i; j <stochar.length ; j++) {
                swap(stochar,i,j);
                pailie(stochar,i+1, set);
                swap(stochar,i,j);
            }
        }

    }

    private static void swap(char[] stochar, int i, int j) {
        char tmp=stochar[i];
        stochar[i]=stochar[j];
        stochar[j]=tmp;
    }

再用一下C的方法 (也是比较普遍的方法,用了标记数组来防止重复计算)

private ArrayList<String> ret = new ArrayList<>();

public ArrayList<String> Permutation(String str) {
    if (str.length() == 0)
        return ret;
    char[] chars = str.toCharArray();
    Arrays.sort(chars);
    backtracking(chars, new boolean[chars.length], new StringBuilder());
    return ret;
}

private void backtracking(char[] chars, boolean[] hasUsed, StringBuilder s) {
    if (s.length() == chars.length) {
        ret.add(s.toString());
        return;
    }
    for (int i = 0; i < chars.length; i++) {
        if (hasUsed[i])
            continue;
        if (i != 0 && chars[i] == chars[i - 1] && !hasUsed[i - 1]) /* 保证不重复 
        这里我不是很懂为什么最后一个条件前面要加非,我自己测试和理解的都是不加非的,如果当前字符和上一个字符相等而且上一个字符被访问过了就不再访问了,所以为啥加非呢?*/
            continue;
        hasUsed[i] = true;
        s.append(chars[i]);
        backtracking(chars, hasUsed, s);
        s.deleteCharAt(s.length() - 1);
        hasUsed[i] = false;
    }
}

关于我的疑问
剑指offer刷题笔记-不晓得第几天了_第1张图片
剑指offer刷题笔记-不晓得第几天了_第2张图片

你可能感兴趣的:(leetcode)