1. 安卓系统手势解锁

在这里插入图片描述

(1)回溯(模版方法)

// 至少 需要经过 m 个点,但是 不超过 n 个点的。

class Solution {
    int count = 0;
    public int numberOfPatterns(int m, int n) {
        if (m > n)
            return 0;

        //对于每个路径长度进行回溯
        for (int i = m; i <= n; i++) {
            HashSet<Integer> inPath = new HashSet<>();
            Deque<Integer> path = new LinkedList<>();
            trackBacking(0, i, path, inPath);
        }
        /* 考虑对称,对称可以节省计算
		numberOfPatterns(0, m, n, visited, 1);
        numberOfPatterns(1, m, n, visited, 1);
        count *= 4;
        numberOfPatterns(4, m, n, visited, 1);
		*/
        return count;
    }
    
	/*
     * @param cur 当前的点
     * @param len 路径长度
     * @param path 路径
     * @param inPath 路径中已存在的点
     */


    private void trackBacking(int cur, int len, Deque<Integer> path, HashSet<Integer> inPath) {
        if (path.size() == len) {
            count++;
            return;
        }

        for (int tar = 1; tar <= 9; tar++) {
            //目标点是当前点或者已经在路径内
            if (tar == cur || inPath.contains(tar))
                continue;

            //计算两个点之间有没有存在的点,如果没有,返回null
            Integer pointBetween = getPointBetween(cur, tar);

             //当前点和目标点之间有点,并且不在已访问的路径内
            if (pointBetween != null && !inPath.contains(pointBetween))
                continue;

            path.addLast(tar);
            inPath.add(tar);

            trackBacking(tar, len, path, inPath);

            path.pollLast();
            inPath.remove(tar);
        }
    }

    //计算两个点之间有没有存在的点,如果没有,返回null
    public Integer getPointBetween(int startPoint, int targetPoint) {
        if (startPoint == -1)
            return null;

        int[] startP = getPosition(startPoint);
        int[] targetP = getPosition(targetPoint);

        int diff = Math.abs(startP[0] - targetP[0]) + Math.abs(startP[1] - targetP[1]);
        //两点之间无其他点,之间相连
        if (diff == 1) 
            return null;
            
        //两点之间有一个点
        else if (diff == 2) {
            //同行相隔一个点:1,3 ; 4,6 ; 7,9 等
            if (startP[0] == targetP[0])
                return 3 * startP[0] + Math.min(startP[1], targetP[1]) + 2;
            //同列相隔一个点:1,7 ; 2,8 ; 3,9 等
            else if (startP[1] == targetP[1])
                return 3 * (Math.min(startP[0], targetP[0]) + 1) + startP[1] + 1;
            //其他情况都不存在
            else    
                return null;
        }
        
        //两点之间为4,只有中间节点为5时有效
        else if (diff == 4) {
            if (startP[0] + targetP[0] == 2 && startP[1] + targetP[1] == 2) {
                return 5;
            }
        }

        //其他情况比如:2,9相连时,相当于中间没有节点
        return null;
    }

    //存储 1 - 9 每个节点的位置
    public int[] getPosition(int point) {
        switch (point) {
            case 1:
                return new int[]{0, 0};
            case 2:
                return new int[]{0, 1};
            case 3:
                return new int[]{0, 2};
            case 4:
                return new int[]{1, 0};
            case 5:
                return new int[]{1, 1};
            case 6:
                return new int[]{1, 2};
            case 7:
                return new int[]{2, 0};
            case 8:
                return new int[]{2, 1};
            case 9:
                return new int[]{2, 2};
            default:
                return new int[]{-1, -1};
        }
    }
}

(2)回溯(简洁写法)

  • 矩形的遍历搜索
  • 本题比较特殊,方向不再是上下左右,还有四个对角线,仔细观察示例,发现还有与当前位置偏移(1,2)的也算
  • 如果中间有已经访问点,仍可以连接其后的点,实际上就是在当前方向遇到已访问点,遍历其后面的点是否未访问,未访问则选择这个点
class Solution {
    int count;
    // 上下及对角线、斜线,总共16个组合
    int[][] direction = {{1,0}, {-1,0}, {0,1}, {0,-1}, {1,1}, {-1,-1}, {1,-1}, {-1,1}, 
                         {1,2}, {2,1}, {1,-2},{-2,-1}, {-1,2}, {2,-1},{-1,-2}, {-2,1}};

    public int numberOfPatterns(int m, int n) {
        // 判断是否访问过
        boolean[] visited = new boolean[9];

        // 9个起点
        for (int i = 0; i < 9; i++) {
            numberOfPatterns(i, m, n, visited, 1);
        }
        return count;
    }

    private void numberOfPatterns(int start, int m, int n, boolean[] visited, int num) {
        //已经遍历过,或者节点超过n个
        if (visited[start] || num > n) 
            return;
        //符合条件的节点,count++
        if (num >= m && num <= n) 
            count++;

        // 试探
        visited[start] = true;
        //例如2:在0行,1列 : 2/3 = 0, 2 % 3 = 1, 所以这里行用 / , 列用%
        int row = start / 3, col = start % 3;

        for (int[] dir : direction) {
            int x = row + dir[0], y = col + dir[1];
            
            // 新坐标非法
            if (x < 0 || x > 2 || y < 0 || y > 2) 
                continue;
            
            // 如果新坐标被访问过,根据题意,在该方向继续查找,直到找到一个未访问的有效点
            while (visited[x * 3 + y]) {
                x += dir[0];
                y += dir[1];

                // 新坐标非法
                if (x < 0 || x > 2 || y < 0 || y > 2) 
                    break;
            }

            // 新坐标非法
            if (x < 0 || x > 2 || y < 0 || y > 2) 
                continue;

            numberOfPatterns(3 * x + y, m, n, visited, num + 1);
        }
       
        // 回溯
        visited[start] = false;
    }
}


Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐