算法题目总结

这篇博客详细总结了各种算法题目,包括动态规划中的斐波那契数列、跳台阶问题,链表的操作如合并排序链表、寻找链表环的入口,二叉树的重建与遍历,以及位运算在数组中找只出现一次的数字等。同时,涵盖了剑指offer和LeetCode的经典题目解析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

动态规划

剑指offer

7. 斐波那契数列
public class Solution {
    public int Fibonacci(int n) {
        if(n == 0){
            return 0;
        }
        if(n==1){
            return 1;
        }
        
        int[] dp = new int[n+1];
        dp[0] = 0;
        dp[1] = 1;
        
        for (int k=2; k<=n; k++){
            dp[k] = dp[k-1]+dp[k-2];
        }
        
        return dp[n];
    }
}
8. 跳台阶
public class Solution {
    public int JumpFloor(int target) {
        if(target == 0){
            return 0;
        }
        if(target == 1){
            return 1;
        }
        
        //定义状态:dp[i] 为跳上第i阶阶梯的调法
        int[] dp = new int[target+1];
        dp[1] = 1;
        dp[2] = 2;
        
        for(int k = 3; k<=target; k++){
            dp[k] = dp[k-1] + dp[k-2];
        }
        
        return dp[target];
    }
}
9.变态跳台阶
public class Solution {
    public int JumpFloorII(int target) {
        if(target == 0){
            return 0;
        }
        if(target == 1){
            return 1;
        }
        
        //确定状态,dp[i] 表示跳到第i阶台阶的跳发
        int[] dp = new int[target+1];
        dp[1] = 1;
        dp[2] = 2;
        
        for(int k=3; k<=target; k++){
            dp[k] = 1;  //直接跳到第k阶台阶,属于一种跳发
            for(int n=1; n<k; n++){
                dp[k] = dp[k]+dp[n];
            }
        }
        
        return dp[target];
    }
}
10.矩形覆盖
public class Solution {
    public int RectCover(int target) {
        if(target == 0){
            return 0;
        }
        if(target == 1){
            return 1;
        }
        
        int[] dp = new int[target+1];
        dp[1] = 1;
        dp[2] = 2;
        
        for(int k=3; k<=target; k++){
            dp[k] = dp[k-1] + dp[k-2];
        }
        
        return dp[target];
    }
}
30.连续子数组的最大和
public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        if(array.length == 0){
            return 0;
        }
        
        int[] dp = new int[array.length];
        dp[0] = array[0];
        
        int max = array[0];
        for(int k=1; k<array.length; k++){
            dp[k] = Math.max(array[k], dp[k-1]+array[k]);
            max = Math.max(max, dp[k]);
        }
        
        return max;        
    }
}
33.丑数

思路:
丑数 N = k2 * 2 + k3 * 3 + k5 * 5 (k2, k3 , k5 个数不定)

public class Solution {
    public int GetUglyNumber_Solution(int index) {
        if(index == 0){
            return 0;
        }
        
        int[] dp = new int[index+1];
        dp[1] = 1; // dp[k]表示从小到大的第k个丑数
        
        int k2 = 1, k3 = 1, k5 = 1; // 丑数 N = k2 *2 + k3 * 3 + k5 *5
        for(int k=2; k<=index; k++){
            dp[k] = Math.min(dp[k2]*2, Math.min(dp[k3]*3, dp[k5]*5));
            if(dp[k] == dp[k2]*2){
                k2++;
            }
            if(dp[k] == dp[k3]*3){
                k3++;
            }
            if(dp[k] == dp[k5]*5){
                k5++;
            }
        }
        return dp[index];
    }
}
67. 剪绳子
public class Solution {
    public int cutRope(int target) {
        int[] dp = new int[target+1];
        dp[1] = 1;
        
        // i*(k-i) 表示把k分成两个数, 
        // i*dp[k-i]把k至少分成三个数,因为dp[k-i]的结果是把k-i至少分成了两个数
        for(int k=2; k<=target; k++){
            for(int i=1; i<k; i++){
                dp[k] = Math.max(dp[k], Math.max(i*(k-i), i*dp[k-i]));
            }
        }
        return dp[target];
    }
}

leetcode

53.最大子序和
class Solution {
    //动态规划
    public int maxSubArray(int[] nums) {
        int n = nums.length;
        if(n==0){
            return 0;
        }
        
        //定义状态,dp[i]表示以第i个元素为结尾的连续子序列的最大和
        int[] dp = new int[n];
        //状态初始化
        dp[0] = nums[0];
        int max = nums[0];
        
        //以第k个元素结尾的连续子序列的最大和要么是第k个元素本身,要么是第k个元素与以第k-1个元素结尾的子序列和的和
        //状态转移方程:dp[k] = Math.max(nums[k], nums[k]+dp[k-1]);
        for(int k=1; k<n; k++){
            dp[k] = Math.max(nums[k], nums[k]+dp[k-1]);
            max = Math.max(dp[k], max);
        }
        
        return max;       
    }
}
121.买卖股票的最佳时机
class Solution {
    public int maxProfit(int[] prices) {
        int n = prices.length;
        if(n == 0){
            return 0;
        }
        
        //定义状态,dp[k]表示第k天可以获得的最大利润
        int[] dp = new int[n];
        dp[0] = 0;
        
        int min = prices[0];  //记录前k-1天中,股票的最低价  
        int max = dp[0];      //记录k天中可获得的最大利润
        //状态转移方程:第k天可以获得的最大利润 = max(第k-1天可以获得的最大利润, 第k天价格 -前k-1天中价格最小值)
        for(int k=1; k<n; k++){        
            dp[k] = Math.max(dp[k-1], prices[k]-min);
            min = Math.min(min, prices[k]);
            max = Math.max(max, dp[k]);
        }
        
        return max;
    }
}
343.整数拆分
class Solution {
    public int integerBreak(int n) {
        
        //确定状态:dp[k] 表示拆分正整数k后可获得的最大积 k>=2
        int[] dp = new int[n+1];
        dp[1] = 1;
        dp[2] = 1;  //2=1+1;  1*1=1
        
        //状态转移方程:dp[k] = max( i*(k-i), i*dp[k-i] ) , 其中 1<=i<k
        for(int k=3; k<=n; k++){
            dp[k] = 0;
            for(int i=1; i<k; i++){
                int temp = Math.max(i*(k-i), i*dp[k-i]);
                dp[k] = Math.max(temp, dp[k]);
            }          
        }       
        return dp[n];      
    }
}
198.打家劫舍
class Solution {
    public int rob(int[] nums) {
        int n = nums.length;
        if(n == 0){
            return 0;
        }
        
        //定义状态,dp[k] 为考虑盗取第[0,k]号房子所取得的最大收益
        int[] dp = new int[n];
        dp[0] = nums[0];
     
        //状态转移方程,求解dp[k], dp[k] = max( nums[k]+dp[k-2], nums[k-1]+dp[k-3]... )
        for(int k=1; k<n; k++){
            dp[k] = 0;
            for(int i = k; i>=0; i--){
                dp[k] = Math.max((nums[i]+(i>=2 ? dp[i-2]:0)), dp[k]);
            }
        }

        return dp[n-1];        
    }
}

链表

剑指offer

3.从尾到头打印链表
/**
*    public class ListNode {
*        int val;
*        ListNode next = null;
*
*        ListNode(int val) {
*            this.val = val;
*        }
*    }
*
*/
import java.util.*;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> res = new ArrayList<>();
        if (listNode == null){
            return res;
        }
        
        Stack<Integer> stack = new Stack<>();
        while(listNode != null){
            stack.push(listNode.val);
            listNode = listNode.next;
        }
        
        int n = stack.size();  //需单独保存一下
        for(int i = 0; i<n; i++){
            res.add(stack.pop());
        }
        
        return res;                             
    }
}
14.链表中倒数第k个结点
/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindKthToTail(ListNode head,int k) {
        if (head == null){
            return null;
        }
        
        if(k == 0){
            return null;
        }
        
        ListNode slow = head;
        ListNode fast = head;
        
        // fast 最终要只想链表的最后一个非空节点
        for(int i=0; i<k-1; i++){
            fast = fast.next;
            if(fast == null){
                return null;
            }
        }
        
        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next;
        }
        
        return slow;
    }
}
15.反转链表
/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode ReverseList(ListNode head) {
        if(head == null){
            return null;
        }
        
        ListNode pre = null;
        ListNode cur = head;
        
        while(cur != null){
            ListNode sub = cur.next;
            cur.next = pre;
            pre = cur;
            cur = sub;
        }
        
        return pre;
    }
}
16.合并两个排序的链表

非递归(归并法)解法

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1 == null){
            return list2;
        }
        
        if(list2 == null){
            return list1;
        }
        
        ListNode dummyHead = new ListNode(-1);
        ListNode cur = dummyHead;
        while(list1 != null || list2 != null){
            if (list1 == null){
                cur.next = list2;
                cur = cur.next;
                list2 = list2.next;
            } else if(list2 == null){
                cur.next = list1;
                cur = cur.next;
                list1 = list1.next;
            } else {
                if(list1.val < list2.val){
                    cur.next = list1;
                    cur = cur.next;
                    list1 = list1.next;
                }else {
                    cur.next = list2;
                    cur = cur.next;
                    list2 = list2.next;
                }                
            }            
        }
        
        return dummyHead.next;        
    }
}

递归解法

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1 == null){
            return list2;
        }
        
        if(list2 == null){
            return list1;
        }
        
        ListNode node = null;
        if(list1.val < list2.val){
            node = list1;
            node.next = Merge(list1.next, list2);
        }else {
            node = list2;
            node.next = Merge(list1, list2.next);
        }
        
        return node;        
    }
}
25.复杂链表的复制
/*
public class RandomListNode {
    int label;
    RandomListNode next = null;
    RandomListNode random = null;

    RandomListNode(int label) {
        this.label = label;
    }
}
*/
public class Solution {
    public RandomListNode Clone(RandomListNode pHead)
    {
        if(pHead == null){
          return null;
        }
        
        // 1.复制 A->B->C   =>   A->A'->B->B'->C->C'
        RandomListNode cur = pHead;
        while(cur != null){
            RandomListNode node = new RandomListNode(cur.label);
            node.next = cur.next;
            cur.next = node;
            cur = cur.next.next;
        }
        
        // 2.复制random
        cur = pHead;       
        while(cur != null){
            if(cur.random != null){
                cur.next.random = cur.random.next;
            }
            cur = cur.next.next;
        }
        
        // 3.拆分,需要不破坏原链表,拆分成两个独立的链表
        cur = pHead;
        RandomListNode newCur = pHead.next;
        RandomListNode newHead = pHead.next;
        while(cur != null){
            cur.next = cur.next.next;
            if(newCur.next != null){
                newCur.next = newCur.next.next;
            }
            cur = cur.next;           
            newCur = newCur.next;
        }
        
        return newHead;                                   
    }
}
36.两个链表的第一个公共结点

思路1: 找出2个链表的长度,然后让长的先走两个链表的长度差,然后再一起走

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        if (pHead1 == null || pHead2 == null){
            return null;
        }
        
        int len1 = 0;
        ListNode cur1 = pHead1;
        while(cur1 != null){
            len1++;
            cur1 = cur1.next;
        }
        
        int len2 = 0;
        ListNode cur2 = pHead2;
        while(cur2 != null){
            len2++;
            cur2 = cur2.next;
        }
        
        int n = Math.abs(len1-len2);
        if(len1>len2){
            cur1 = pHead1;
            for(int i=0; i<n; i++){
                cur1 = cur1.next;
            }
            cur2 = pHead2;
        }else{
            cur2 = pHead2;
            for(int i=0; i<n; i++){
                cur2 = cur2.next;
            }
            cur1 = pHead1;
        }
        
        while(cur1 != null){
            if(cur1 == cur2){
                return cur1;
            }
            cur1 = cur1.next;
            cur2 = cur2.next;
        }
        
        return null;
    }
}

思路2:
假定 List1长度: a+n , List2 长度:b+n,n为两个链表的公共部分
让cur1走路程a+n+b+n,cur2走路程b+n+a+n, 若有公共部分,则cur1与cur2在走开始最后一段路程n时会相遇
时间复杂度O(n+a+b)

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        ListNode cur1 = pHead1;
        ListNode cur2 = pHead2;
        
        while(cur1 != cur2){
            cur1 = (cur1 == null? pHead2 : cur1.next);
            cur2 = (cur2 == null? pHead1 : cur2.next);
        }
        
        return cur1;
    }
}

思路3:
如果存在共同节点的话,那么从该节点,两个链表之后的元素都是相同的。
可以用两个栈分别来装这两条链表,然后一个一个比较pop出来的值,找到第一个相同的节点。

55.链表中环的入口结点

思路:
设起点距环的入口的距离为n, 环的长度为r
fast走的距离为:2L=n+ar, slow走的距离为:L=n+br
可得,2L-L =L=(a-b)r
即:slow走了(a-b)个环的距离。
若slow从环入口出发,走过L距离后,恰好回到环入口;
所以当slow从起点出发,走过L距离后,在环内且距环入口的距离为n,恰好是起点距环入口的距离

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {

    public ListNode EntryNodeOfLoop(ListNode pHead)
    {    
        // 成环至少需要两个节点
        if(pHead == null || pHead.next == null){
            return null;
        }
        
        ListNode fast = pHead;
        ListNode slow = pHead;
        
        // 不成环时,会从此循环中退出
        while(fast != null && fast.next.next != null){
            fast = fast.next.next;
            slow = slow.next;      
            
            if(fast == slow){
                // 第一次相遇后
                fast = pHead;
                while(fast != slow){
                    fast = fast.next;
                    slow = slow.next;
                }
                return fast;               
            }
        }
       return null;        
    }
}
56.删除链表中重复的结点

思路:
创建虚拟头结点,以便对头结点作统一处理
设置两个指针,慢指针指向确定的不重复的节点,快指针为当前做判断的工作节点

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public ListNode deleteDuplication(ListNode pHead)
    {
        if(pHead == null){
            return null;
        }
        
        ListNode dummyHead = new ListNode(-1);
        dummyHead.next = pHead;
        
        ListNode pre = dummyHead;
        ListNode cur = pHead;
        
        while(cur != null && cur.next != null){
            if(cur.next.val == cur.val){
                int val = cur.val;
                while(cur != null && cur.val == val){
                    cur = cur.next;
                }
                pre.next = cur;
            }else {
                pre = cur;
                cur = cur.next;
            }
        }    
        
        return dummyHead.next;
    }
}

二叉树

剑指offer

4.重建二叉树

先找出根节点,然后使用递归重构

/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
import java.util.*;
public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        if(pre.length == 0 || in.length == 0 || pre.length != in.length){
            return null;
        }
        
        TreeNode root = new TreeNode(pre[0]);
        int index = 0;
        while(in[index] != pre[0]){
            index++;
        }
        int[] leftPre = new int[index];
        int[] leftIn = new int[index];
        leftPre = Arrays.copyOfRange(pre,1, index+1);
        leftIn = Arrays.copyOfRange(in, 0, index);
        
        int[] rightPre = new int[pre.length-index-1];
        int[] rightIn = new int[in.length-index-1];
        rightPre = Arrays.copyOfRange(pre, index+1, pre.length);
        rightIn = Arrays.copyOfRange(in, index+1, in.length);

        root.left = reConstructBinaryTree(leftPre, leftIn);
        root.right = reConstructBinaryTree(rightPre, rightIn);
        
        return root;       
    }
}
17.树的子结构
/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public boolean HasSubtree(TreeNode root1,TreeNode root2) {
        if(root1 == null || root2 == null){
            return false;
        }
        
        boolean res = false;
        if(root1.val == root2.val){
           res = isContains(root1, root2);
        }
        if(!res){
            res = HasSubtree(root1.left, root2) || HasSubtree(root1.right, root2);
        }
        
        return res;       
    }
    
    // 判断Tree1是否包含(不一定相等)Tree2,从两棵树的根节点依次往下比较,递归实现
    public boolean isContains(TreeNode root1, TreeNode root2){
        //root1==null, root2==null 为true;  root1!=null,root2==null 为true
        if(root2 == null){
            return true;
        }
        // 此时root2必不为null,故root1==null 为false
        if(root1 == null){
            return false;
        }
        
        if(root1.val != root2.val){
            return false;
        }
        
        return isContains(root1.left, root2.left) && isContains(root1.right, root2.right);       
    }
}
18.二叉树的镜像

递归或非递归(借助栈)来实现

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public void Mirror(TreeNode root) {
        if(root == null){
            return;
        }
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;
        
        Mirror(root.left);
        Mirror(root.right);
    }
}
23.二叉搜索树的后序遍历序列
import java.util.*;
public class Solution {
    private int flag = 0;  // 记录是否是首次进入
    
    public boolean VerifySquenceOfBST(int [] sequence) {
        int len = sequence.length;
        if(len == 0 && flag == 0){
            return false;
        }
        
        if(len == 0 && flag == 1){
            return true;
        }
        
        flag = 1;
        int last = sequence[len-1];
        
        int index = 0;
        for(int i=0; i<len-1; i++){
            if(sequence[i]<last){
                index++;
            }
        }
        
        for(int i=index; i<len-1; i++){
            if(sequence[i]<last){
                return false;
            }
        }
                
        return VerifySquenceOfBST(Arrays.copyOfRange(sequence,0,index)) && 
            VerifySquenceOfBST(Arrays.copyOfRange(sequence,index,len-1));
    }
}
24.二叉树中和为某一值的路径
import java.util.ArrayList;
/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    private ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
    private ArrayList<Integer> list = new ArrayList<>();
    
    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
        if(root == null){
            return res;
        }
        
        list.add(root.val);
        target = target-root.val;
        
        if(root.left == null && root.right == null && target == 0){
            res.add(new ArrayList<Integer>(list));
        }else{
            FindPath(root.left, target);
            FindPath(root.right, target);
        }
        
        list.remove(list.size()-1);
        return res;
    }
}
26.二叉搜索树与双向链表

二叉搜索树中序遍历就是排好序的结果

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public TreeNode Convert(TreeNode pRootOfTree) {
        if(pRootOfTree == null){
            return null;
        }
        
        TreeNode head = sub(pRootOfTree, null);
        while(head != null && head.left != null){
            head = head.left;
        }
        
        return head;
    }
    
    // 中序遍历
    private TreeNode sub(TreeNode root, TreeNode last){
        if(root == null){
            return null;
        }
       
        if(root.left != null){
            last = sub(root.left, last);
        }
        
        root.left = last;
        if(last != null){
            last.right = root;
        }
        last = root;
        
        if(root.right != null){
            last = sub(root.right, last);
        }
        return last;
    }    
}
38.二叉树的深度

利用递归遍历分别返回左右子树深度

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public int TreeDepth(TreeNode root) {
        if(root == null){
            return 0;
        }
        
        return Math.max(TreeDepth(root.left), TreeDepth(root.right))+1;        
    }
}
39.平衡二叉树

利用二叉树的深度, 判断是否平衡因子的绝对值<= 1.

public class Solution {
    public boolean IsBalanced_Solution(TreeNode root) {
        if(root == null){
            return true;
        }
        int l = getDepth(root.left);
        int r = getDepth(root.right);
        return Math.abs(l-r)<=1;
    }
    
    // 求一棵树的深度
    private int getDepth(TreeNode root){
        if(root == null){
            return 0;
        }
        
        return Math.max(getDepth(root.left), getDepth(root.right))+1;
    }
}

进一步,可以通过减枝降低时间复杂度

public class Solution {
    public boolean IsBalanced_Solution(TreeNode root) {
        if(root == null){
            return true;
        }
        return getDepth(root)!=-1;
        
    }
    private int getDepth(TreeNode root){
        if(root == null){
            return 0;
        }
        
        // 减枝 
        if(getDepth(root.left) == -1 || getDepth(root.right) == -1){
            return -1;
        }
        
        return Math.abs(getDepth(root.left)-getDepth(root.right))>1 ? 
            -1 : Math.max(getDepth(root.left), getDepth(root.right)) + 1;
    }
}
57.二叉树的下一个结点
/*
public class TreeLinkNode {
    int val;
    TreeLinkNode left = null;
    TreeLinkNode right = null;
    TreeLinkNode next = null;

    TreeLinkNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public TreeLinkNode GetNext(TreeLinkNode pNode)       
    {
        if(pNode == null){
            return null;
        }
        
        if(pNode.right != null){
            pNode = pNode.right;
            while(pNode.left != null){
                pNode = pNode.left;
            }
            return pNode;
        }
        
        while(pNode.next != null){
            if(pNode.next.left == pNode){
                return pNode.next;
            }
            pNode = pNode.next;
        }
        
        return null;       
    }
}
58.对称的二叉树
/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    boolean isSymmetrical(TreeNode pRoot)
    {
        if(pRoot == null){
            return true;
        }
        return subIsSymmetrical(pRoot.left, pRoot.right);
    }
    private boolean subIsSymmetrical(TreeNode left, TreeNode right){
        if (left == null && right == null){
            return true;
        }
        if(left != null && right != null){
            return left.val == right.val && subIsSymmetrical(left.left, right.right) && subIsSymmetrical(right.left, left.right);         
        }
        
        return false;
    }
}
62.二叉搜索树的第k个结点
/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    private TreeNode res;
    private int k;
    
    TreeNode KthNode(TreeNode pRoot, int k)
    {   
        this.k = k;
        if(k==0){
            return null;
        }
        
        sub(pRoot);
        return res; 
    }
    
    void sub(TreeNode node){
        if(node == null || k<1){
            return;
        }
        
        sub(node.left);
        if(k==1){
            res = node;
        }
        k--;
        sub(node.right);
        
        return;   
    }
}
打印二叉树
22.从上往下打印二叉树
import java.util.*;
/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        ArrayList<Integer> res = new ArrayList<>();
        if(root == null){
            return res;
        }
        
        LinkedList<TreeNode> q = new LinkedList<>();
        q.add(root);
        
        while(!q.isEmpty()){
            TreeNode node = q.remove();
            res.add(node.val);
            
            if(node.left != null){
                q.add(node.left);
            }
            
            if(node.right != null){
                q.add(node.right);
            }        
        }
        
        return res;        
    }
}
59.按之字形顺序打印二叉树
import java.util.*;

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
        if(pRoot == null){
            return res;
        }
        
        Stack<TreeNode> s1 = new Stack<>();
        Stack<TreeNode> s2 = new Stack<>();
        
        s1.push(pRoot);
        
        while(!s1.isEmpty() || !s2.isEmpty()){
            ArrayList<Integer> list = new ArrayList<>();
            if(!s1.isEmpty()){
                while(!s1.isEmpty()){
                    TreeNode node = s1.pop();
                    list.add(node.val);
                    if(node.left != null){
                        s2.push(node.left);
                    }
                    if(node.right != null){
                        s2.push(node.right);
                    }
                }
                res.add(list);                                                
            }else {
                while(!s2.isEmpty()){
                    TreeNode node = s2.pop();
                    list.add(node.val);
                    if(node.right != null){
                        s1.push(node.right);
                    }
                    if(node.left != null){
                        s1.push(node.left);
                    }
                }
                res.add(list);
            }
        }
        
        return res;
    }
}
60.把二叉树打印成多行
import java.util.*;
/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
        if(pRoot == null){
            return res;
        }
        
        LinkedList<TreeNode> q = new LinkedList<>();
        q.add(pRoot);
        int total = 1;
        int cnt = 0;
        
        ArrayList<Integer> list = new ArrayList<>();
        while(!q.isEmpty()){
            TreeNode node = q.remove();
            list.add(node.val);
            cnt++;
            if(node.left != null){
                q.add(node.left);
            }
            if(node.right != null){
                q.add(node.right);
            }
            
            if(cnt == total){
                cnt = 0;
                total = q.size();
                res.add(new ArrayList(list));
                list.clear();
            }
        }
        
        return res;             
    }    
}

数组相关

剑指offer

1.二维数组中的查找
public class Solution {
    public boolean Find(int target, int [][] array) {
        if(array == null){
            return false;
        }
        
        int i = 0;
        int j = array[0].length-1;
        
        while(i<array.length && j>=0){
            if(array[i][j] == target){
                return true;
            }            
            if(array[i][j]>target){
                j--;
            }else{
                i++;
            }            
        }        
        return false;
    }
}
public class Solution {
    public boolean Find(int target, int [][] array) {
        if(array.length == 0 || array[0].length == 0){
            return false;
        }
        
        int i=array.length-1;
        int j=0;
        
        while(array[i][j] != target){
            if(array[i][j]<target){
                j++;
            }else if(array[i][j]>target){
                i--;
            }
            if(i<0 || j>array[0].length-1){
                return false;
            }
        }
        
        return true;
    }
}
13.调整数组顺序使奇数位于偶数前面

冒泡法思路,时间复杂度为O(n*n), 空间复杂的为O(1)

public class Solution {
    public void reOrderArray(int [] array) {
        int len = array.length;
        
        for(int i=len-1; i>=0; i--){
            for(int j=0; j<i; j++){
                if(array[j]%2==0 && array[j+1]%2==1){
                    int temp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = temp;
                }
            }
        }       
    }
}

双指针法

public class Solution {
    public void reOrderArray(int [] array) {
        int len = array.length;
        
        int odd = -1;
        int cur = 0;
        for(cur = 0; cur<len; cur++){
            if(array[cur]%2 == 1){
                int temp = array[cur];
                for(int k=cur; k>odd+1; k--){
                    array[k] = array[k-1];
                }
                array[odd+1] = temp;
                odd++;
            }            
        }
    }
}

使用一个辅助数组,时间复杂度为O(n), 空间复杂度为O(n), 以空间换时间

public class Solution {
    public void reOrderArray(int [] array) {
        int oddNum = 0;
        for(int i=0; i<array.length; i++){
            if(array[i]%2 == 1){
                oddNum++;
            }
        }
        
        int[] arr = array.clone();
        int odd=0;
        int even = oddNum;
        for(int e : arr){
            if(e%2 == 1){
                array[odd] = e;
                odd++;
            }else{
                array[even] = e;
                even++;
            }
        }       
    }
}
28.数组中出现次数超过一半的数字

多数投票问题(摩尔投票问题),使用 Boyer-Moore Majority Vote Algorithm 来解决这个问题,使得时间复杂度为 O(n), 空间复杂的为O(1)
若数组中存在出现次数超过一半的数字,则major最终记录的一定是这个数

public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        int major = array[0];
        int cnt = 1;
        
        for(int i=1; i<array.length; i++){
            if(array[i] == major){
                cnt++;
            }else{
                cnt--;
                if(cnt == 0){
                    major = array[i];
                    cnt=1;
                }
            }            
        }
        
        int n = 0;
        for(int e : array){
            if(e == major){
                n++;
            }
        }
        
        return n>array.length/2? major:0;
    }
}

使用一个额外的辅助HashMap, 时间复杂度为O(n), 但空间复杂度也为O(n)

import java.util.*;
public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        HashMap<Integer, Integer> map = new HashMap<>();
        
        for(int i=0; i<array.length; i++){
            
            if(map.containsKey(array[i])){
                map.put(array[i], map.get(array[i])+1);
            }else{
                map.put(array[i], 1);
            }
               
            if(map.get(array[i])>array.length/2){
                return array[i];
            }            
        }
        return 0;        
    }
}
29.最小的K个数

使用最大优先队列维护前k个最小元素

import java.util.*;
public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> res = new ArrayList<>();
        if(input.length < k){
            return res;
        }
        PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>(){
              public int compare(Integer a, Integer b){
                return b-a;
            }
        });
        for(int i=0; i<input.length; i++){
            pq.add(input[i]);
            if(pq.size()>k){
                pq.remove();
            }
        }
        
        for(int e: pq){
            res.add(e);
        }
        
        return res;
    }
}
32.把数组排成最小的数

可以看成是一个排序问题,在比较两个字符串 S1 和 S2 的大小时,应该比较的是 S1+S2 和 S2+S1 的大小,如果 S1+S2 < S2+S1,那么应该把 S1 排在前面,否则应该把 S2 排在前面。

import java.util.*;
public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> res = new ArrayList<>();
        if(input.length < k){
            return res;
        }
        PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>(){
              public int compare(Integer a, Integer b){
                return b-a;
            }
        });
        for(int i=0; i<input.length; i++){
            pq.add(input[i]);
            if(pq.size()>k){
                pq.remove();
            }
        }
        
        for(int e: pq){
            res.add(e);
        }
        
        return res;
    }
}
35.数组中的逆序对

归并过程

public class Solution {
    private int res=0;
    public int InversePairs(int [] array) {
        merge(array, 0, array.length-1);
        return res;
    }
    private void merge(int [] arr, int l, int r){
        if(l>=r){
            return;
        }
        int mid = (r-l)/2+l;
        merge(arr, l, mid);
        merge(arr, mid+1, r);
        submerge(arr,l,mid,r);
    }
    private void submerge(int [] arr, int l, int mid, int r){
        int [] temp = new int[r-l+1];
        for(int i=l; i<=r; i++){
            temp[i-l] = arr[i];
        }
        int m=l;
        int n=mid+1;
        for(int i=l; i<=r; i++){
            if(m>mid){
                arr[i] = temp[n-l];
                n++;
            }else if(n>r){
                arr[i] = temp[m-l];
                m++;
            }else{
                if(temp[m-l]<temp[n-l]){
                    arr[i] = temp[m-l];
                    m++;
                }else{
                    arr[i] = temp[n-l];
                    n++;
                    res+=mid-m+1;
                    res%=1000000007;
                }
            }
        }
    }
}
41.和为S的连续正数序列

滑动窗口法

import java.util.ArrayList;
public class Solution {
    public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {        
        ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
        int l = 1, r = 2;
        while(l<r){
            int curSum = (l+r)*(r-l+1)/2;
            if(curSum == sum){
                ArrayList<Integer> list = new ArrayList<>();
                for(int i=l; i<=r; i++){
                    list.add(i);                  
                }
                res.add(list);
                l++;
            }else if(curSum < sum){
                r++;
            }else{
                l++;
            }
        }
        return res;
    }
}
42.和为S的两个数字

对撞指针法,距离越远的两个数,积越小

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> list = new ArrayList<>();
        int l = 0, r = array.length-1;
        while(l<r){
            if(array[l] + array[r] < sum){
                l++;
            }else if(array[l] + array[r] > sum){
                r--;
            }else{
                list.add(array[l]);
                list.add(array[r]);
                return list;
            }
        }
        return list;
    }
}
50.数组中重复的数字

要求时间复杂度 O(N),空间复杂度 O(1)。因此不能使用排序的方法,也不能使用额外的标记数组。
对于这种数组元素在 [0, n-1] 范围内的问题,可以将值为 i 的元素调整到第 i 个位置上进行求解。
以 (2, 3, 1, 0, 2, 5) 为例,遍历到位置 4 时,该位置上的数为 2,但是第 2 个位置上已经有一个 2 的值了,因此可以知道 2 重复:

public class Solution {
    // Parameters:
    //    numbers:     an array of integers
    //    length:      the length of array numbers
    //    duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation;
    //                  Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++
    //    这里要特别注意~返回任意重复的一个,赋值duplication[0]
    // Return value:       true if the input is valid, and there are some duplications in the array number
    //                     otherwise false
    public boolean duplicate(int numbers[],int length,int [] duplication) {
        if(length < 2 || numbers == null){
            return false;
        }
        
        for(int i=0; i<numbers.length; i++){
            while(numbers[i] != i){
                if(numbers[i] == numbers[numbers[i]]){
                    duplication[0] = numbers[i];
                    return true;
                }
                
                // 交换numbers[i] 和·numbers[numbers[i]]
                int temp = numbers[i];
                numbers[i] = numbers[numbers[i]];
                numbers[temp] = temp;                                   
            }
        }
        
        return false;   
    }
}
51.构建乘积数组

分解成上下三角形处理

import java.util.ArrayList;
public class Solution {
    public int[] multiply(int[] A) {
        if(A.length<=1){
            return null;
        }
        int[] B = new int[A.length];
        B[0] = 1;
        for(int i=1; i<B.length; i++){
            B[i] = B[i-1]*A[i-1];
        }
        
        int temp = 1;
        for(int i=B.length-1; i>=0; i--){
            B[i] = B[i]*temp;
            temp = temp*A[i];
        }       
        return B;       
    }
}
二分查找

在有序的数组中,考虑二分查找

6.旋转数组的最小数字

注意:数组中只剩两个数时,mid=l+(r-l)时, mid == l,
通过array[mid] 与 array[l] 比较,当array[mid] == array[l]时,需要额外考虑 mid==i 的情况
并且,l 指向的位置不可越过最小值处,比较复杂

import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        if(array.length == 0){
            return 0;
        }  
     
        int l = 0; 
        int r = array.length-1;
        int mid = l;
        
        while(l<=r){
            mid = l+(r-l)/2;
            if(array[mid] == array[l] && array[mid] == array[r]){
                return sub(array, l, r);
            }
            
            if(array[mid]>array[l]){
                l = mid;                
            }else if(array[mid]<array[l]){
                r = mid;
            }else{
                if(mid != l){
                    l = mid ;
                }else{
                    // 只剩下两个数的时候
                    mid = array[l]<array[r]? l:r;
                    break;
                }
            }           
        }   
        return array[mid];
    }
    
    private int sub(int[] arr, int l, int r){
        int res = arr[l];
        for(int i=l+1; i<=r; i++){
            if(arr[i] < arr[i-1]){
                return arr[i];
            }            
        }       
        return res;
    }
}

通过array[mid] 与 array[l] 比较, 简单许多

import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        if(array.length == 0){
            return 0;
        }  
     
        int l = 0; 
        int r = array.length-1;
        
        while(l<r){
            int mid = l+(r-l)/2;
            if(array[mid] == array[l] && array[mid] == array[r]){
                return sub(array, l, r);
            }
            
            if(array[mid]<=array[r]){
                r = mid;                
            }else{
                l = mid+1;
            }           
        }   
        return array[l];
    }
    
    private int sub(int[] arr, int l, int r){
        int res = arr[l];
        for(int i=l+1; i<=r; i++){
            if(arr[i] < arr[i-1]){
                return arr[i];
            }            
        }       
        return res;
    }
}
37.数字在排序数组中出现的次数

二分搜索的变形

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
        int start = binary(array, k);
        int end = binary(array, k+1);
        // 当k大于array中的最大值时,start=array.length ,此时使用array[start]会越界
        // 当k小于array中的最小值时,start=0
        // 当k在array的范围中但在array中不存在时,start=array中大于k的第一个元素的位置
        if(start == array.length || array[start] != k){
            return 0;
        }
        
        return end-start;      
    }
    
    // 寻找第一个大于等于k的元素
    // k大于arr中的最大值时,最终l=r=arr.length
    // k小于arr中的最小值时,最终l=r=0
    private int binary(int[] arr, int k){
        int l=0, r=arr.length;
        while(l<r){
            int mid = l+(r-l)/2;
            if(k>arr[mid]){
                l = mid+1;  
            }else{
                r=mid;
            }
        }
        return l;
    }
}

使用二分查找的变形,查找第一个和最后一个重复的元素

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
        int l = getFirstK(array, k);
        int r = getLastK(array, k);
        if(l == -1 || r == -1){
            return 0;
        }
        return r-l+1; 
    }
    
    private int getFirstK(int[] arr, int k){
        int l = 0;
        int r = arr.length-1;        
        
        while(l<=r){
            int mid = (r-l)/2+l;
            if(k < arr[mid]){
                r = mid - 1;
            }else if(k > arr[mid]){
                l = mid + 1;
            }else if(mid-1 >= 0 && arr[mid-1] == k){
                r = mid -1 ;
            }else{
                return mid;
            }           
        }        
        return -1;
    }
    
    private int getLastK(int[] arr,int k){
        int l = 0; 
        int r = arr.length - 1;        
        
        while(l<=r){
            int mid = (r-l)/2 + l;
            if(arr[mid] < k){
                l = mid + 1;
            }else if(arr[mid] > k){
                r = mid -1;
            }else if(mid+1 <= r && arr[mid+1] == k){
                l = mid + 1;
            }else{
                return mid;
            }            
        }
        return -1;                                
    }
}

字符串操作

剑指offer

2.替换空格

额外使用一个StringBuilder, 空间复杂度为O(n)

public class Solution {
    public String replaceSpace(StringBuffer str) {
        String s = str.toString();
        StringBuilder sb = new StringBuilder();
        for(int i=0; i<s.length(); i++){
            char c = s.charAt(i);
            if(c == ' '){
                sb.append("%20");
            }else{
                sb.append(String.valueOf(c));
            }
        }        
        return sb.toString();   	
    }
}

直接在原string上操作,空间复杂度为O(1)

public class Solution {
    public String replaceSpace(StringBuffer str) {
        int p1 = str.length()-1;  // 初始字符串的尾部
        // 一个空格占一个字符,要将空格替换为%20这样的三个字符,所以每遇到一个空格,先在末尾扩充两个字符
        for(int i=0; i<=p1; i++){
            if(str.charAt(i) == ' '){
                str.append("  ");   //
            }
        }
        
        int p2 = str.length()-1;  // 扩充后字符串的尾部
        
        while(p1>=0 && p1<p2){
            char c = str.charAt(p1--);
            if(c == ' '){
                str.setCharAt(p2--,'0');
                str.setCharAt(p2--,'2');
                str.setCharAt(p2--,'%');
            }else{
                str.setCharAt(p2--,c);
            }
        }
        
        return str.toString();    	
    }
}
27.字符串的排列
34.第一个只出现一次的字符位置

此题的考点是如何节省空间
最直观的解法是使用 HashMap 对出现次数进行统计。但是考虑到要统计的字符范围有限(一个英文字符只占一个Byte,使用256个空间即可记录所有英文字符),因此可以使用整型数组代替 HashMap,从而将空间复杂度由 O(N) 降低为 O(1)。

public class Solution {
    public int FirstNotRepeatingChar(String str) {
        int[] freq = new int[256];
        for(int i=0; i<str.length(); i++){
            char c = str.charAt(i);
            freq[c]++;
        }
        for(int i=0; i<str.length(); i++){
            char c = str.charAt(i);
            if(freq[c]==1){
                return i;
            }
        }
        return -1;
    }
}

以上实现的空间复杂度还不是最优的。考虑到只需要找到只出现一次的字符,那么需要统计的次数信息只有 0,1,更大,使用两个比特位就能存储这些信息。

import java.util.*;
public class Solution {
    public int FirstNotRepeatingChar(String str) {
        BitSet bs0 = new BitSet(256);
        BitSet bs1 = new BitSet(256);
        for(int i=0; i<str.length(); i++){
            char c = str.charAt(i);
            if(!bs0.get(c) && !bs1.get(c)){
                bs0.set(c);  // 0 0 ===> 0 1 表示第c位的字符第一次出现
            }else if(bs0.get(c) && !bs1.get(c)){
                bs1.set(c);  // 0 1 ===> 1 1 表示第c位的字符第二次出现
            }
        }
        
        for(int i=0; i<str.length(); i++){
            char c = str.charAt(i);
            if(bs0.get(c) && !bs1.get(c)){
                return i;
            }
        }
        return -1;
    }
}
49.把字符串转换成整数
52.正则表达式匹配
53.表示数值的字符串
54.字符流中第一个不重复的字符
旋转字符串

要求在不使用额外空间的条件下对字符串进行旋转操作
可以通过多次局部翻转结合全部翻转完成最终的旋转

43.左旋转字符串

字符串拼接

public class Solution {
    public String LeftRotateString(String str,int n) {
        if(str.length() == 0){
            return "";
        }
        
        //n = n%str.length();
        String res = "";
        res = str.substring(n,str.length()) + str.substring(0,n);
        return res;
    }
}

翻转法
例,先将 “abc” 和 “XYZdef” 分别翻转,得到 “cbafedZYX”,然后再把整个字符串翻转得到 “XYZdefabc”。

public class Solution {
    public String LeftRotateString(String str,int n) {
        if(n>=str.length()){
            return str;
        }
        char[] cs = str.toCharArray();
        reverse(cs, 0, n-1);
        reverse(cs, n, cs.length-1);
        reverse(cs, 0, cs.length-1);
        
        return String.valueOf(cs);
        
    }
    
    // 对数组arr中范围为[l,r]的元素进行翻转
    private void reverse(char[] arr, int l, int r){
        while(l<r){
            char temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;
            l++;
            r--;
        }
    }
}
44.翻转单词顺序列

转为String数组直接处理

public class Solution {
    public String ReverseSentence(String str) {
        if(str == null){
            return "";
        }
        
        String[] ss = str.split("\\s+");
        // 防止str=" " 的情况
        if(ss.length == 0){
            return str;
        }
        String res = "";
        for(int i=ss.length-1; i>=0; i--){
            res += ss[i];
            if(i != 0){
                res += " ";
            }
        }    
        return res;
    }
}

题目应该有一个隐含条件,就是不能用额外的空间。虽然 Java 的题目输入参数为 String 类型,需要先创建一个字符数组使得空间复杂度为 O(N),但是正确的参数类型应该和原书一样,为字符数组,并且只能使用该字符数组的空间。任何使用了额外空间的解法在面试时都会大打折扣,包括递归解法。

正确的解法应该是和书上一样,先旋转每个单词,再旋转整个字符串。

public class Solution {
    public String ReverseSentence(String str) {
        char[] cs = str.toCharArray();
        int n = str.length();
        int i=0;
        for(int j=0; j<=n; j++){
            if(j==n || cs[j] == ' '){
                reverse(cs, i, j-1);
                i=j+1;
            }
        }
        
        reverse(cs,0,n-1);
        return String.valueOf(cs);        
    }
    private void reverse (char[] arr, int l, int r){
        while(l<r){
            char temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;
            l++;
            r--;
        }
    }
}

回溯

剑指offer

62. 矩阵中的路径
public class Solution {
    private boolean[][] used;
    private int m, n;
    private int[][] dist = {{0,1}, {0,-1}, {1,0}, {-1,0}};
    
    public boolean hasPath(char[] matrix, int rows, int cols, char[] str){
        // 构建字符矩阵                
        char[][] grid = new char[rows][cols];                          
        for(int i=0; i<matrix.length; i++){
            grid[i/cols][i%cols] = matrix[i];
        }
        
        m = rows;
        n = cols;
        used = new boolean[rows][cols];
        
        for(int i=0; i<m; i++){
            for(int j=0; j<n; j++){
                if(bt(grid, i, j, str, 0)){
                    return true;
                }
            }
        }
        
        return false;
    }
    
    // 边界检查
    private boolean inArea(int i, int j){
        return i>=0 && i<m && j>=0 && j<n;
    }
    
    // 回溯算法
    private boolean bt(char[][] grid, int i, int j, char[] str, int index){
        // 注意,此处递归到底的回溯条件,两个判断不可反
        if(grid[i][j] != str[index]){
            return false;
        }  
        if(index == str.length-1){  // 此处是str.length-1 而不是str.length ; case:"AAAAAAAAAAAA",3,4,"AAAAAAAAAAAA"时, 当index=str.length时,返回给上层的可能是false
            return true;
        }                                      
        
        used[i][j] = true;
        for(int d=0; d<4; d++){
            int ni = i+dist[d][0];
            int nj = j+dist[d][1];
            
            if(inArea(ni, nj) && !used[ni][nj] && bt(grid, ni, nj, str, index+1)){
                return true;
            }
        }
        // 回溯
        used[i][j] = false;
        return false;       
    }
}

DFS

剑指offer

63. 机器人的运动范围
public class Solution {
    private int[][] dire = {{0,1}, {0,-1}, {1,0}, {-1,0}};
    private boolean[][] used;
    private int m, n;
    private int res;
    
    public int movingCount(int threshold, int rows, int cols){
        m = rows;
        n = cols;
        used = new boolean[m][n];
            
        // 构建矩阵
        int[][] grid = new int[m][n];
        for(int i=0; i<m; i++){
            for(int j=0; j<n; j++){
                grid[i][j] = getSum(i, j);
            }
        }
        
        dfs(grid, 0, 0, threshold);
        return res;
    }
    
    // 返回坐标位数之和
    private int getSum(int i, int j){
        int sum = 0;
        while(i>0 || j>0){
            sum += i%10;
            sum += j%10;
            i /= 10;
            j /= 10;
        }
       
        return sum;
    }
    
    // 边界检查
    private boolean inArea(int i, int j){
        return i>=0 && i<m && j>=0 && j<n;
    }
    
    private void dfs(int[][] grid, int i, int j, int thre){
        if(grid[i][j]>thre){
            return;
        }
        
        used[i][j] = true;
        res++;
        for(int d=0; d<4; d++){
            int ni = i+dire[d][0];
            int nj = j+dire[d][1];
            
            if(inArea(ni, nj) && !used[ni][nj]){
                dfs(grid, ni, nj, thre);
            }
        }
    }
}

位运算

剑指offer

40.数组中只出现一次的数字
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值