top interview questions 2

之前阿里的一个笔试题,能否存在三个点,将数组进行四分操作,O(n)复杂度的,只需利用一个数组加上一个HashMap 来进行缓存即可。
无思路:三板斧,digui,hash,类比,
时刻要有递归与动态规划结合的思想。
有树有图多用递归
实现大小堆的时候两种 方式 1.自己构造实现Comparator接口,在实例化PriorityQueue时自己传入参数
return a - b;就是从小到大,return b - a,就是从大到小
2.jdk8的环境支持直接传入比较器

  Collections.sort(height, (a, b) -> {
                    if(a[0] != b[0]) 
                        return a[0] - b[0];
                    return a[1] - b[1];
            });
            Queue<Integer> pq = new PriorityQueue<>((a, b) -> (b - a));
218. The Skyline Problem

微信的那个考题,找出边界线
很不错的一个方法
一个很重要的思路:用高度的正负号进行缓存是入队列还是出队列。
思想还是降维度的思想


  public List<int[]> getSkyline(int[][] buildings) {
        List<int[]> result = new ArrayList<>();
        List<int[]> height = new ArrayList<>();
        for (int[] temp : buildings ) {
            height.add(new int[] {temp[0], -temp[2]});
            height.add(new int[] {temp[1], temp[2]});
        }
        Collections.sort(height, (a, b) -> {
            if (a[0] != b[0]) {
                return a[0] - b[0];
            } else {
                return a[1] - b[1];
            }
        });
        Queue<Integer> pq = new PriorityQueue<Integer>((a, b) -> (b - a));
        int pre = 0;
        pq.offer(0);
        for (int [] h : height) {
            if (h[1] < 0) {
                pq.offer(-h[1]);
            } else {
                pq.remove(h[1]);
            }
            int cur = pq.peek();
            if (pre != cur) {
                result.add(new int[]{h[0], cur});
                pre = cur;
            }
        }
        return result;
     }
124. Binary Tree Maximum Path Sum

思路:依据最高点的想法,更新本身的参数,依次进行迭代处理,注意当节点小与0的时候

73. Set Matrix Zeroes

思路:如果m * n 矩阵中某个元素为0,那么整个行清零,整个列同时也清零。
O(m + n)空间复杂度,遍历了
最优解空间复杂度可以:缩减到O(1),用第一行,第一列进行标记

17. Letter Combinations of a Phone Number

给一串电话号码,输出其中电话号码代表的所有可能的字符串。
思路:1.递归回溯法,
2.迭代版本,生成一个map用来做映射每一个数组,然后对每一个字符进行迭代。

200. Number of Islands

思路:统计出孤立岛的个数,这个有一个很厉害的地方,存在1就至少存在一个岛,然后按照上下左右的方式进行蔓延。递归的方法,当数组越界的时候,或者当前这个结点不是1,就停止蔓延。

103. Binary Tree Zigzag Level Order Traversal

要求:“Z”字形遍历数组,返回一个list。
思路:用递归实现,传入level参数,正常来看是从左向右的,这里面加了一个小的技巧,就是偶数的话,正常从后面加,奇数的话,从前面开始插入,这样就保证了是后序。

49. Group Anagrams

要求:输出同一组的序列,包括颠倒了顺序的。
[“eat”, “tea”, “tan”, “ate”, “nat”, “bat”], 输出
[
[“ate”, “eat”,”tea”],
[“nat”,”tan”],
[“bat”]
]
最优解: 用HashMap对index进行缓存

33. Search in Rotated Sorted Array

注意只包含两个数的数组情况,两个小于等于

93. Restore IP Addresses

输入字符串,输出可能的IP地址信息
[“255.255.11.135”, “255.255.111.35”]. (Order does not matter)
设置3个循环变量,对IP地址进行4分段处理,半段每一个段是否符合要求。

131. Palindrome Partitioning

判断list里面的元素是不是回文,需要用到stack进行缓存,
判断String是不是回文,直接两个指针从两头开始循环即可

//这个是一个典型的回溯方法解决的问题,需要好好理解下
public class Solution {
    private List<List<String>> list;
    private List<String> currLst;
    public List<List<String>> partition(String s) {
        list = new ArrayList<List<String>>();
        currLst = new ArrayList<String>();
        backTrack(s, 0);
        return list;
    }
    public void backTrack(String s, int l){
        if(currLst.size()>0 //the initial str could be palindrome
            && l>=s.length()){
            List<String> r = new ArrayList<String>();
            for (String single : currLst) {
                r.add(single);
            }
            list.add(r);
        }
        for(int i=l;i<s.length();i++){
            if(isPalindrome(s,l,i)){
                if(l==i)
                    currLst.add(Character.toString(s.charAt(i)));
                else
                    currLst.add(s.substring(l,i+1));
                backTrack(s,i+1);
                currLst.remove(currLst.size()-1);
            }
        }
    }
    public boolean isPalindrome (String s, int i, int j) {
        if (i == j) {
            return true;
        }
        while (i < j) {
            if (s.charAt(i) != s.charAt(j)) {
                return false;
            }
            i++;
            j--;
        }
        return true;
    }
}
105. Construct Binary Tree from Preorder and Inorder Traversal

这个在用数组处理的时候需要注意,preorder根节点的index的变换问题

34. Search for a Range

给一个单调不递减函数,找出给定数的范围,这个数可能存在重复,首先找出左下届,然后缓存右下届的范围,二分法查找右下届。

207. Course Schedule

这个问题意思是判断这个图是否存在环路,
最优解是采用广度搜索的办法,两个数组,一个存储list,一个存储是否被依赖,degree。当degree为0的时候count++,返回时判断count是否与numcourse相等

236. Lowest Common Ancestor of a Binary Tree

先决条件是这两个结点都在二叉树中。二叉树的最小公共祖先结点

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    if (root == null || root == p || root == q) return root;
    TreeNode left = lowestCommonAncestor(root.left, p, q);
    TreeNode right = lowestCommonAncestor(root.right, p, q);
    return left == null ? right : right == null ? left : root;
}
55. Jump Game

Given an array of non-negative integers, you are initially positioned at the first index of the array.
给定一个非负的数组,每一个的元素代表的是从当前的位置最大能跳转的间距
思路:1,从后往前进行跳跃,方法很巧妙,就是寻找出位置为0的时候的能否被前面的结点给跳过去。
2.从初始位置进行累加,判断jumparea是否大约等于i

139. Word Break

单词拆解,给定字典,判断在不在字典中
思路: 1,递归加上map的缓存,注意递归基,递归调用,以及返回值
2.新建一个boolean数组,对数组中的每一个元素进行迭代处理。

56. Merge Intervals

合并重叠区间,
思路:1, 利用jdk8的排序功能, intervals.sort((i1, i2) -> Integer.compare(i1.start, i2.start));
依据开始进行升序排列。
最优2.对开始与结尾分别存储, 然后排序,判断开始是否大于上一个结尾,大于的话,新开辟空间。

134. Gas Station

环形跑道,加气gas[i], 消耗cost[i], 返回能跑下来的i的值
思路:1.分别从每个开始,迭代记录能否跑下来,不能的话换下一个起点。O(n平方)复杂度
2.最优解: 非常牛的一种解法! O(n)复杂度,一遍进行遍历即可, 思想:如果从A出发到不了B,那么A到B之间的任何点都到达不了B,只能从B+ 1出发,同时如果gas的和小于cost的和,怎么都到达不了。

227. Basic Calculator II

对一个符合加减乘除规则的字符串输入,
思路: 1.用stack, 进行缓存,默认是加法, 当是乘除的时候直接进行计算再入栈,符号直接取反再入栈。
2.因为只涉及到二级缓存, 所以三个变量即可
正常实现的思路是中缀转后缀的表达式。注意其中的出现的空格

148. Sort List

O(NlogN)时间复杂度,常数空间复杂度
最优解:采用归并排序,注意分割链表的时候,按照链表对前半部分的next设置为null

150. Evaluate Reverse Polish Notation

实现数的加减乘除,使用stack,需要注意的是String的equals方法。

210. Course Schedule II

之前的上课的升级版本,当课程可以完全被完成,返回上课顺序,
思路:数组缓存本课程有几个依赖,链表缓存依赖本课程的课程有什么,然后学习本课程的时候可以对好多数组的中的数进行减一操作

//典型的用链表构造图,并进行深度搜索与广度搜索的例子
//深度搜索在搜索的时候调用自己的搜索函数
//就是判断这个
public class Solution {
  public int[] findOrder(int numCourses, int[][] prerequisites) {
        int[] incLinkCounts = new int[numCourses];
        List<List<Integer>> adjs = new ArrayList<>(numCourses);
        initialiseGraph(incLinkCounts, adjs, prerequisites);
        return solveByBFS(incLinkCounts, adjs);
        return solveByDFS(adjs);
    }
    private void initialiseGraph(int[] incLinkCounts, List<List<Integer>> adjs, int[][] prerequisites){
        int n = incLinkCounts.length;
        while (n-- > 0) adjs.add(new ArrayList<>());
        for (int[] edge : prerequisites) {
            incLinkCounts[edge[0]]++;
            adjs.get(edge[1]).add(edge[0]);
        }
    }
    private int[] solveByBFS(int[] incLinkCounts, List<List<Integer>> adjs){
        int[] order = new int[incLinkCounts.length];
        Queue<Integer> toVisit = new ArrayDeque<>();
        for (int i = 0; i < incLinkCounts.length; i++) {
            if (incLinkCounts[i] == 0) toVisit.offer(i);
        }
        int visited = 0;
        while (!toVisit.isEmpty()) {
            int from = toVisit.poll();
            order[visited++] = from;
            for (int to : adjs.get(from)) {
                incLinkCounts[to]--;
                if (incLinkCounts[to] == 0) toVisit.offer(to);
            }
        }
        return visited == incLinkCounts.length ? order : new int[0]; 
    }
    private int[] solveByDFS(List<List<Integer>> adjs) {
        BitSet hasCycle = new BitSet(1);
        BitSet visited = new BitSet(adjs.size());
        BitSet onStack = new BitSet(adjs.size());
        Deque<Integer> order = new ArrayDeque<>();
        for (int i = adjs.size() - 1; i >= 0; i--) {
            if (visited.get(i) == false && hasOrder(i, adjs, visited, onStack, order) == false) return new int[0];
        }
        int[] orderArray = new int[adjs.size()];
        for (int i = 0; !order.isEmpty(); i++) orderArray[i] = order.pop();
        return orderArray;
    }

    private boolean hasOrder(int from, List<List<Integer>> adjs, BitSet visited, BitSet onStack, Deque<Integer> order) {
        visited.set(from);
        onStack.set(from);
        for (int to : adjs.get(from)) {
            if (visited.get(to) == false) {
                if (hasOrder(to, adjs, visited, onStack, order) == false) return false;
            } else if (onStack.get(to) == true) {
                return false;
            }
        }
        onStack.clear(from);
        order.push(from);
        return true;
    }
}
208. Implement Trie (Prefix Tree)

实现一个单词查找树,之前在cc150里面看到过,不同的是,这里面采用children进行子字符的缓存。

//典型的一个单词查找树的例子
class TrieNode {
    public char val;
    public boolean isWord; 
    public TrieNode[] children = new TrieNode[26];
    public TrieNode() {}
    TrieNode(char c){
        TrieNode node = new TrieNode();
        node.val = c;
    }
}
public class Trie {
    private TrieNode root;
    /** Initialize your data structure here. */
    public Trie() {
        root = new TrieNode();
        root.val = ' ';
    }

    /** Inserts a word into the trie. */
    public void insert(String word) {
        TrieNode ws = root;
        for(int i = 0; i < word.length(); i++){
            char c = word.charAt(i);
            if(ws.children[c - 'a'] == null){
                ws.children[c - 'a'] = new TrieNode(c);
            }
            ws = ws.children[c - 'a'];
        }
        ws.isWord = true;
    }

    /** Returns if the word is in the trie. */
    public boolean search(String word) {
        TrieNode ws = root; 
        for(int i = 0; i < word.length(); i++){
            char c = word.charAt(i);
            if(ws.children[c - 'a'] == null) return false;
            ws = ws.children[c - 'a'];
        }
        return ws.isWord;
    }

    /** Returns if there is any word in the trie that starts with the given prefix. */
    public boolean startsWith(String prefix) {
        TrieNode ws = root; 
        for(int i = 0; i < prefix.length(); i++){
            char c = prefix.charAt(i);
            if(ws.children[c - 'a'] == null) return false;
            ws = ws.children[c - 'a'];
        }
        return true;
    }
}

/**
 * Your Trie object will be instantiated and called as such:
 * Trie obj = new Trie();
 * obj.insert(word);
 * boolean param_2 = obj.search(word);
 * boolean param_3 = obj.startsWith(prefix);
 */
324. Wiggle Sort II

要求:1) Given nums = [1, 5, 1, 1, 6, 4], one possible answer is [1, 4, 1, 5, 1, 6].
摇摆排序,时间复杂度O(n),空间复杂度,O(1),此题综合了快速选择第k个最大的数,quickselect思想,选取出中位数,然后利用(1 + 2 * index)% (n | 1);得到映射后的idnex下标。
最优解:贴出代码如下:

public class Solution {
   public void wiggleSort(int[] nums) {
        int median = findKthLargest(nums, (nums.length + 1) / 2);
        int n = nums.length;
        int left = 0, i = 0, right = n - 1;
        while (i <= right) {
            if (nums[newIndex(i,n)] > median) {
                swap(nums, newIndex(left++,n), newIndex(i++,n));
            }
            else if (nums[newIndex(i,n)] < median) {
                swap(nums, newIndex(right--,n), newIndex(i,n));
            }
            else {
                i++;
            }
        }
    }
    private int newIndex(int index, int n) {
        return (1 + 2*index) % (n | 1);
    }
        public int findKthLargest(int[] a, int k) {
    int n = a.length;
    int p = quickSelect(a, 0, n - 1, n - k + 1);
    return a[p];
  }

  // return the index of the kth smallest number
  int quickSelect(int[] a, int lo, int hi, int k) {
    // use quick sort's idea
    // put nums that are <= pivot to the left
    // put nums that are  > pivot to the right
    int i = lo, j = hi, pivot = a[hi];
    while (i < j) {
      if (a[i++] > pivot) swap(a, --i, --j);
    }
    swap(a, i, hi);

    // count the nums that are <= pivot from lo
    int m = i - lo + 1;

    // pivot is the one!
    if (m == k)     return i;
    // pivot is too big, so it must be on the left
    else if (m > k) return quickSelect(a, lo, i - 1, k);
    // pivot is too small, so it must be on the right
    else            return quickSelect(a, i + 1, hi, k - m);
  }  
  void swap(int[] a, int i, int j) {
    int tmp = a[i];
    a[i] = a[j];
    a[j] = tmp;
  }
}

比较好的一个思路,如果发现相等用亦或,防止重复利用

    private boolean exist(char[][] board, int y, int x, char[] word, int i) {
        if (i == word.length) return true;
        if (y < 0 || x < 0 || y == board.length || x == board[y].length) return false;
        if (board[y][x] != word[i]) return false;
        board[y][x] ^= 256;
        boolean exist = exist(board, y, x+1, word, i+1)
            || exist(board, y, x-1, word, i+1)
            || exist(board, y+1, x, word, i+1)
            || exist(board, y-1, x, word, i+1);
        board[y][x] ^= 256;
        return exist;
    }
322. Coin Change

要求:输入所有零钱的金额,以及需要拼凑起来的所有总额,输出使用最小的硬币个数
思路 :1.采用原始的递归加缓存的大法宝,时间有点慢
2.最优解:利用迭代的方式进行处理,新建一个数组,从0 到 amout依次进行累加统计,好多的迭代版本的动态规划问题都可以参考这个模式

//与爬楼梯问题,走棋盘问题类似
public class Solution {
   public int coinChange(int[] coins, int amount) {
       if (amount < 0) {
           return -1;
       }
       int[] dp = new int[amount + 1];
       int sum = 0;
       while (++sum <= amount) {
           int min = -1;
           for (int co : coins) {
               if (co <= sum && dp[sum - co] != -1) {
                   int  temp = dp[sum - co] + 1;
                   min = min < 0 ? temp : (temp < min ? temp : min);
               }
           }
           dp[sum] = min;
       }
       return dp[amount];
   }
}
54. Spiral Matrix

要求: 像蜗牛壳一样螺旋的访问向里访问数组元素
思路:利用数学公式计算出响应的下标,注意一维数组和一维列向量的情况, 需要对k进行限制。

5. Longest Palindromic Substring

找到并返回一个字符串的最长的回文子串
思路:以字符串的第i个字符作为回文字符串的中心,奇数个或者偶数个,从本身或者本身 + 1开始向两边进行扩展

3. Longest Substring Without Repeating Characters

找到并返回没有重复的子串长度
思路:1.从开始进行遍历,得到稍微长一点的,时间超时,非常low的方法
2.用hashmap或者数组进行缓存上一次出现的长度,O(n)时间复杂度,很不错的方法。

        int max = 0;
        Map<Character, Integer> map = new HashMap<Character, Integer>();
        for (int i = 0, j =0; i < s.length(); i++) {
            if (map.containsKey(s.charAt(i))) {
                j = Math.max(j, map.get(s.charAt(i)) + 1);
            }
            map.put(s.charAt(i), i);
            max = Math.max(max, i - j + 1);
        }
        return max;
98. Validate Binary Search Tree

这个题目在cc150上做过
思路:1.利用查找二叉树的夹逼方法,long可以过
2.递归,这个竟然忘记了,中序遍历得到相应的解

179. Largest Number

For example, given [3, 30, 34, 5, 9], the largest formed number is 9534330.
给定数组,输出组成的最大的数
思路:本来是用Integer的sort方法,但是发现不好用,改用String的sort, 注意数组这块的排序问题,都只能使用包装类

15. 3Sum

For example, given array S = [-1, 0, 1, 2, -1, -4],
A solution set is:[ [-1, 0, 1], [-1, -1, 2]]
最优解:先对数组进行排序,然后再做累加处理
注意提前结束循环
非常巧妙的写法,不光实现了加减操作,同时去除了重复元素

 if(sum <= 0) while(x < y && nums[x] == nums[x + 1]) x++ ;
 if(sum >= 0) while(x < y && nums[y] == nums[y - 1]) y--;
127. Word Ladder

beginWord = “hit”
endWord = “cog”
wordList = [“hot”,”dot”,”dog”,”lot”,”log”,”cog”]
As one shortest transformation is “hit” -> “hot” -> “dot” -> “dog” -> “cog”,
return its length 5.
存在问题,暂时搁置

91. Decode Ways

Given encoded message “12”, it could be decoded as “AB” (1 2) or “L” (12).
要求:数字翻译成字母一共有多少种形式

// 思路: 当某个为等于0的时候,其实不作用,只和前面的那个一起作用,所以跳过,当不为0的时候,如果两位数在0 到 26之间,两种累计组合的方式,既可以和上一个不足和,也可以和上一个组合,所以加了两个。
public class Solution {
    public int numDecodings(String s) {
          int n = s.length();
        if (n == 0) return 0;

        int[] memo = new int[n+1];
        memo[n]  = 1;
        memo[n-1] = s.charAt(n-1) != '0' ? 1 : 0;

        for (int i = n - 2; i >= 0; i--)
            if (s.charAt(i) == '0') continue;
            else memo[i] = (Integer.parseInt(s.substring(i,i+2))<=26) ? memo[i+1]+memo[i+2] : memo[i+1];

        return memo[0];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值