【路飞】面试题 17.14. 最小K个数、95. 不同的二叉搜索树 II、394. 字符串解码

106 阅读4分钟

面试题 17.14. 最小K个数

设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。
示例:

输入: arr = [1,3,5,7,2,4,6,8], k = 4
输出: [1,2,3,4]

解题思路:求最小k个数,其实用优先队列也可以解决,但是我们今天学习的是排序,所以这题用的是快速排序加无监督partition法,代码如下:

var smallestK = function(arr, k) {
    if(k == 0) return [];
    //先快速排序
    arr = quick_sort(arr,0,arr.length -1);
    //无监督partition法
    arr = find_sort(arr);
    return arr.splice(0,k);
};
//以下代码上篇文章有注释,这里就不写了
let quick_sort = function(arr,l,r){
    while(r - l > 16){
        let x = l, y = r, m = middle(arr[l],parseInt((arr[l] + arr[r])/2),arr[r]);
        do{
            while(arr[x] < m) x ++;
            while(arr[y] > m) y --;
            if(x <= y){
                let mm = arr[x];
                arr[x] = arr[y];
                arr[y] = mm;
                x ++, y --;
            }
        }while(x <= y);
        //当左边最小的长度为k时,直接返回arr前k个数就可以了,此时前k的是就是最小的k个数
        if(r - l == k) return arr.splice(0,k);
        quick_sort(arr,x,r);
        r = y;
    }
    return arr;
}
let find_sort = function(arr){
    let min = 0;
    for(let i = 1; i < arr.length; i ++){
        min = arr[i] < arr[min] ? i : min;
    }
    while(min > 0){
        if(arr[min] < arr[min -1]){
            let mm = arr[min];
            arr[min] = arr[min -1];
            arr[min -1] = mm;
        }
        min --;
    }
    for(let i = 2; i < arr.length; i ++){
        let j = i;
        while(arr[j] < arr[j -1]){
            let mm = arr[j];
            arr[j] = arr[j -1];
            arr[j -1] = mm;
            j --;
        }
    }
    return arr;
}
let middle = function(l,m,r){
    let mm = l;
    if(l > m){
        l = m;
        m = mm;
    }
    mm = l;
    if(l > r){
        l = r;
        r = mm;
    }
    mm = m;
    if(m > r){
        m = r;
        r = mm;
    }
    return m;
}

95. 不同的二叉搜索树 II

给你一个整数 n ,请你生成并返回所有由 n 个节点组成且节点值从 1 到 n 互不相同的不同 二叉搜索树 。可以按 任意顺序 返回答案。

示例 1:

输入:n = 3
输出:[[1,null,2,null,3],[1,null,3,2],[2,1,3],[3,1,null,null,2],[3,2,null,1]]

示例 2:

输入:n = 1
输出:[[1]]

解题思路:这题是想知道1-n个节点能组合成多少个二叉搜索树,二叉搜索树的特性是左节点小于根节点,根节点小于右节点,所有这题可以用递归来做,就是依次遍历1-n的节点,每个节点都当做根节点来查找二叉树组成情况最终集合就是所求答案,因为是搜索二叉树,所有需要注意三种情况,1.当左右节点相等的时候说明是根节点,直接返回根节点即可,2.左节点大于右节点,那就说明不符合搜索二叉树特性,返回null,3.左节点小于右节点复核要求,代码如下:

var generateTrees = function(n) {
    if(n == 0) return [];
    return dfs(1,n);
};

let dfs = function(l,r){
    //当左右节点相等的时候说明是根节点,所有返回根节点
    if(l == r) return [new TreeNode(l)];
    //左节点大于右节点,不符合要求,返回null
    if(l > r) return [null];
    let ans = [];
    for(let i = l; i <= r; i ++){
        //获取所有左子树组成情况
        let lertTree = dfs(l,i -1);
        //获取所有右子树组成情况
        let rightTree = dfs(i + 1,r);
        //组合所有左右子树组成情况
        for(let li = 0; li < lertTree.length; li ++){
            for(let ri = 0; ri < rightTree.length; ri ++){
                ans.push(new TreeNode(i,lertTree[li],rightTree[ri]));
            }
        }
    }
    return ans;
}

394. 字符串解码

给定一个经过编码的字符串,返回它解码后的字符串。

编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。

你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。

此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。

示例 1:

输入:s = "3[a]2[bc]"
输出:"aaabcbc"

示例 2:

输入:s = "3[a2[c]]"
输出:"accaccacc"

示例 3:

输入:s = "2[abc]3[cd]ef"
输出:"abcabccdcdcdef"

示例 4:

输入:s = "abc3[cd]xyz"
输出:"abccdcdcdxyz"

解题思路:这个我们可以用两个栈来实现,一个数字栈,一个字符串栈,栈的特性是先入后出,当碰到“】”就将数字入栈,此时可以将前一个字符串与当前字符串拼接,字符串在遇到 “【” 代表结束,入字符串的栈,代码如下:

var decodeString = function(s) {
    //保存数字的栈
    let numArr = [];
    //保存字符串的栈
    let strArr = [];
    //存储当个阶段的数字,因为当个阶段数字可以大于10
    let num = '';
    //存储当个阶段的数字,因为当个阶段字符串可以有多个
    let ss = '';
    for(let i = 0; i < s.length; i ++){
       // isNaN(s[i])判断是否是数字,false代表是字符串,就拼接,直到遇到“【”,结束
        if(!isNaN(s[i])){
            num += s[i];
        } else if(s[i] == '['){
            //此时num已经是一个完整的字符串,可以入栈
            numArr.push(parseInt(num));
            //此时字符串是一个完整的字符串入栈
            strArr.push(ss);
            //情况重新开始记录
            num = '';
            //同上
            ss = '';
        } else if(s[i] == ']'){
            //当遇到“】”就表示一个阶段完成,因为可能会符号套符号,所有将当前字符串与上一个字符串拼接,就是当前这个阶段的完整字符串
            ss = strArr.pop() + ss.repeat(numArr.pop());
        } else {
            ss += s[i];
        }
    }
    return ss;
};