【leetcode刷题第39天】969.煎饼排序、1104.二叉树寻路、838.推多米诺、717. 1比特和2比特的字符

第三十九天

969 煎饼排序

给你一个整数数组 arr ,请使用 煎饼翻转 完成对数组的排序。

一次煎饼翻转的执行过程如下:

  • 选择一个整数 k1 <= k <= arr.length

  • 反转子数组 arr[0...k-1]下标从 0 开始

例如,arr = [3,2,1,4] ,选择 k = 3 进行一次煎饼翻转,反转子数组[3,2,1] ,得到 arr = [**1**,**2**,**3**,4]

以数组形式返回能使 arr 有序的煎饼翻转操作所对应的 k 值序列。任何将数组排序且翻转次数在 10 * arr.length 范围内的有效答案都将被判断为正确。

方法

依照煎饼排序的定义,我们可以考虑依次将每一个数按照如下规则从小到大进行排列:

  • 从原数组中找到第i个数的下标k,然后我们将整个数组按k翻转
  • 经过如上操作,我们就将第i个数翻转到了数组的头部,然后依据已经排好的元素的数量cnt,我们翻转cnt,这样就将上述元素放到了该在的位置
class Solution {
    public List<Integer> pancakeSort(int[] arr) {
        List<Integer> res = new ArrayList<>();
        for (int i = 0; i < arr.length; ++i) {
            int index = findIndex(arr, i + 1);
            flip(arr ,index);
            res.add(index + 1);
            if (check(arr)) return res;
            flip(arr, index - i);
            res.add(index - i + 1);
            if (check(arr)) return res;
            flip(arr, index);
            res.add(index + 1);
            if (check(arr)) return res;
        }
        return res;
    }

    public boolean check(int[] arr) {
        for (int i = 0; i < arr.length; ++i) {
            if (arr[i] != i + 1) return false;
        }
        return true;
    }

    public int findIndex(int[] arr, int target) {
        for (int i = 0; i < arr.length; ++i) if (arr[i] == target) return i;
        return 0;
    }

    public void flip(int[] arr, int index){
        int s = 0, e = index;
        int temp = 0;
        while (s < e){
            temp = arr[s];
            arr[s] = arr[e];
            arr[e] = temp;
            s++;
            e--;
        }
    }
}

1104 二叉树寻路

在一棵无限的二叉树上,每个节点都有两个子节点,树中的节点 逐行 依次按 “之” 字形进行标记。

如下图所示,在奇数行(即,第一行、第三行、第五行……)中,按从左到右的顺序进行标记;

而偶数行(即,第二行、第四行、第六行……)中,按从右到左的顺序进行标记。

给你树上某一个节点的标号 label,请你返回从根节点到该标号为 label节点的路径,该路径是由途经的节点标号所组成的。

方法

对于一个给定的label,我们考虑其父元素是什么。

由于二叉树是按照"之"字形排列的,我们可以先考虑如果元素按照从做到右排列,那么容易知道label的父元素就是label / 2(此处为整除),由于加入了"之"字形排列的规则,那么其父元素的值应为 按照原来顺序排列的值 在对应层的位置 的倒数位置所对应的值。例如第三层的元素是4,5,6,7,我们让label=15,如果二叉树不按照"之"字形排列,其父元素是7,其在第三层的位置是最后一个元素,故其实际的位置应当是该层的第一个元素。即 最后一个元素对应第一个元素,第一个元素对应最后一个元素。

依据上述结论,我们容易知道labe的父元素就是:cnt + cnt - 1 - (label / 2 - cnt)

label / 2表示原来的父元素的位置,label / 2 - cnt表示它原来是父层的第几个元素,cnt - 1 - (label / 2 - cnt)表示它对应的父层倒数第几个元素,cnt + cnt - 1 - (label / 2 - cnt)表示其实际的值。

class Solution {
    public List<Integer> pathInZigZagTree(int label) {
        int cnt = 0;
        int temp = label;
        int[] powTwo = new int[32];
        int base = 1;
        for (int i = 1; i < 25; ++i){
            powTwo[i] = base;
            base *= 2;
        }
        while (temp > 0) {
            cnt++;
            temp >>= 1;
        }

        List<Integer> res = new ArrayList<>();
        while (cnt > 0){
            res.add(label);
            label =powTwo[cnt - 1] + powTwo[cnt - 1] - 1 - (label / 2 - powTwo[cnt - 1]);
            cnt--;
        }
        Collections.reverse(res);
        return res;
    }
}

717 1比特与2比特的字符

有两种特殊字符:

  • 第一种字符可以用一比特 0 表示

  • 第二种字符可以用两比特(1011)表示

给你一个以 0 结尾的二进制数组 bits ,如果最后一个字符必须是一个一比特字符,则返回 true

方法

我们从头至尾对输入的字符串进行解码,遇到第二类字符就让flagfalse,否则为true,由于flag记录的是最后一次解码的字符的种类,因此我们只需要返回flag即可。

class Solution {
    public boolean isOneBitCharacter(int[] bits) {
        boolean flag = false;
        int index = 0;
        while (index < bits.length){
            if (bits[index] == 0) flag = true;
            else {
                index++;
                flag = false;
            }
            index++;
        }
        return flag;
    }
}

838 推多米诺

n 张多米诺骨牌排成一行,将每张多米诺骨牌垂直竖立。在开始时,同时把一些多米诺骨牌向左或向右推。

每过一秒,倒向左边的多米诺骨牌会推动其左侧相邻的多米诺骨牌。同样地,倒向右边的多米诺骨牌也会推动竖立在其右侧的相邻多米诺骨牌。

如果一张垂直竖立的多米诺骨牌的两侧同时有多米诺骨牌倒下时,由于受力平衡, 该骨牌仍然保持不变。

就这个问题而言,我们会认为一张正在倒下的多米诺骨牌不会对其它正在倒下或已经倒下的多米诺骨牌施加额外的力。

给你一个字符串 dominoes 表示这一行多米诺骨牌的初始状态,其中:

  • dominoes[i] = 'L',表示第 i 张多米诺骨牌被推向左侧,

  • dominoes[i] = 'R',表示第 i 张多米诺骨牌被推向右侧,

  • dominoes[i] = '.',表示没有推动第 i 张多米诺骨牌。

返回表示最终状态的字符串。

方法

我们使用广度优先搜索来处理这个问题,首先我们记下所有LR的位置并将其放入队列,由于当RL之间如果相差奇数个未动的骨牌时,中间的那个得保持不动,因此我们与此同时需要统计一下,当遇到R字符时,这个R字符和下一个L字符之间的距离,如果这个距离是奇数,我们就将中间这个元素的状态标记下来,让它在广度优先搜索的过程中不被错误的标记成L或者R

class Solution {
    public String pushDominoes(String dominoes) {
        StringBuilder res = new StringBuilder();
        Queue<Integer> queue = new LinkedList<>();
        int[] status = new int[dominoes.length()];
        for (int i = 0; i < dominoes.length(); ++i){
            if (dominoes.charAt(i) == 'R') {
                queue.add(i + 1);//由于存在下标0 我们让位置偏移一个位置 符号代表方向 右边为正 左边为负
                status[i] = 1;
                int index = i + 1;
                while (index < dominoes.length()) {
                    if (dominoes.charAt(index) == 'L') {//记录特殊的位置 标记为3
                        if ((index - i) % 2 == 0) status[(index + i) >> 1] = 3;
                        break;
                    }
                    if (dominoes.charAt(index) == 'R') break;
                    index++;
                }
            }
            else if (dominoes.charAt(i) == 'L') {
                queue.add(-i - 1);
                status[i] = -1;
            }
        }
        while (!queue.isEmpty()) {
            int dir = queue.peek() > 0 ? 1 : -1;
            int index = Math.abs(queue.poll()) - 1;
            if (index + dir >= 0 && index + dir < dominoes.length()) {
                if (status[index + dir] == 0) {
                    status[index + dir] = dir;
                    queue.add(dir * (index + dir + 1));
                }
            }
        }
        for (int i : status) {
            if (i == 1) res.append('R');
            else if (i == -1) res.append('L');
            else res.append('.');
        }
        return res.toString();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值