440. 字典序的第K小数字 / 80. 删除有序数组中的重复项 II

440. 字典序的第K小数字【困难题】【每日一题】

思路:【面向题解解题】

  1. 将字典序排列看成是一个十叉树,根节点未知,第一层节点依次为1,2,…
  2. 那么按照字典序排序恰好就是对这个十叉树进行前序遍历。
  3. 于是模拟十叉树的前序遍历,每前进一步,k–,当k减到0时,当前节点的值就是字典序第k小的数字,于是问题转换成了对十叉树进行前序遍历。
  4. 由于这个十叉树是有规律的,假设当前节点值为i,那么它的子节点为[i10,i10+9],所以可以不用真的去构造十叉树,而根据这个规律来对十叉树进行前序遍历。
  5. 具体遍历过程写在注释。

代码:

class Solution {
    public int findKthNumber(int n, int k) {
        long curNode = 1;//从节点1开始遍历,遍历到极限时可能发生数字溢出,因此curNode用long类型存储
        k--;//已经走了1步,所以k--
        while (k>0){//当k=0时,找到了第k小的数,循环退出
            int nodes = getNodes(n,curNode);//得到当前节点在<=n的约束下能生成的所有子树节点个数(包括当前节点)
            if (k >= nodes){//如果剩余步数大于等于当前节点所有子树节点个数,那么需要在当前节点的同层节点中继续遍历
                k -= nodes;//直接走nodes步
                curNode++;//同时 当前节点右移,
            }else {//如果剩余步数小于当前节点所有子树节点个数,说明字典序第k的数就在当前节点的子树节点里
                k--;//走一步
                curNode *= 10;//节点更新为当前节点的第一个子节点
            }
        }
        return (int) curNode;//循环遍历结束之后返回当前节点值即可
    }
    public int getNodes(int n,long curNode){
        long allChildren = 0;//定义当前节点所有子树节点个数
        long rightBound = curNode+1;//定义当前层右端点为当前节点的右侧第一个节点
        while (curNode<=n){
            //累加本层节点的个数,本次节点可能全部排满,也可能触碰边界未排满
            //n-curNode+1: 触碰边界,当前层左端点到边界n之间节点的个数
            //bound-curNode: 未触到边界,当前层左端点到右端点之间节点的个数
            allChildren += Math.min(n-curNode+1,rightBound-curNode);
            //跳至下一次层子节点
            curNode *= 10;
            rightBound *= 10;
        }
        return (int) allChildren;
    }
}

80. 删除有序数组中的重复项 II【中等题】

思路:

  1. 逐个遍历数组nums,定义新数组指针pos。
  2. 在遍历nums的过程中,设遍历到的当前数组为num。如果pos<2,或者当pos大于2时,当前num不等于nums[pos-2],那么nums[pos]
    = num,并pos++,可以简化为nums[pos++]=num。
  3. 上述操作的原理是,当pos小于2时,那么前2个元素必被保留,当pos>2时,那么当前位置数字如果与当前位置往前2个位置数字相同,则不用保留,直接遍历下一个位置元素,如果不同,那么保留当前位置元素,并更新pos。
  4. 最后返回pos,直接记结论,我证明不了为毛这么写是对的。
  5. 本题还可以更一般化为保留k个可重复位,删除其他元素,此时将上边的2改为k即可。

代码:

class Solution {
    public int removeDuplicates(int[] nums) {
        int pos = 0;
        for (int num : nums) {
            if ((pos < 2) || nums[pos-2] != num){
                nums[pos++] = num;
            }
        }
        return pos;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值