vlambda博客
学习文章列表

面试复习之基础数据结构与算法第二篇

一、堆

1.Priority Queue是堆数据结构代表

    Priority Queue,优先级队列,正常入队,按照优先级出队。


2.Priority Queue的两种实现机制:

Heap:二叉堆(大顶堆、小顶堆),二项堆,斐波那契  小顶堆,值小的始终排在前面,堆顶是最小值 大顶堆,值大的始终排在前面,堆顶是最大值
Binary Search Tree


3.Heap不同实现的算法复杂度


4.返回数据流中第K大元素?

    思路1:记录前K大元素,每次对记录的元素排序

N个元素,每个元素计算一次,复杂度是O(N)K个元素快速排序,复杂度是O(K * log K)算法的复杂度是O(N * K * log K)

    思路2:利用优先级队列,小顶堆,最小元素(即第K大元素)始终在堆顶

/*** N个元素,每个元素计算一次,复杂度是O(N)*  K个元素快速排序及堆顶比较,复杂度是O(log2 k)*  算法的复杂度是O(N * log2 K)*/class KthLargest {
private PriorityQueue<Integer> priorityQueue;
private int k;
public KthLargest(int k, int[] nums) {
        //利用小顶堆(Java默认实现) priorityQueue = new PriorityQueue<>(k);
this.k = k;
//遍历数组把num加入 for (int num : nums) { add(num); } }
public int add(int num) { if (priorityQueue.size() < k){          //加入元素到小顶堆 priorityQueue.offer(num); }else if(priorityQueue.peek() < num){          //如果小顶堆的堆顶元素值 < num priorityQueue.poll(); priorityQueue.offer(num);        }else{          //返回第k大元素,即小顶堆的堆顶         return priorityQueue.peek();        }}


5.计算滑动窗口中最大值?

    思路1:优先级队列,大顶堆,最大元素始终在堆顶

维护Max Heap,时间复杂度是O(log k)查询堆顶,时间复杂度是O(1)算法复杂度是O(N * log K)

    思路2:双端队列

/*** 输入*  nums [1,3,-1,-3,5,3,6]*   k = 3* 输出(每次计算连续的k个元素最大值)*   [3,3,5,5,6]** 特征分析,每次滑动后,可以放弃左侧已经计算过的部分*   N个元素入队,时间复杂度是O(N)*     保持队列左侧永远是最大值*   新元素入队维护,时间复杂度是O(N)*   算法复杂度是O(N * log K)*/public int[] maxSlidingWindow(int[] nums, int k) {
if(nums.length == 0) { return new int[0];  }
  //保存数组元素的下标  Deque<Integer> window = new ArrayDeque<>();
  //计算结果  int[] result = new int[nums.length - k + 1];
  for(int i = 0; i < nums.length; i++) {   //从k开始,window.peek()查询队首元素    //删除队列中小于窗口左边下标的元素    if(i >= k && window.peek() <= i - k) {     //移除队首元素 window.remove();    }        //新入队元素nums[i]    //从队列右侧开始,删除小于nums[i]的元素 while(!window.isEmpty() && nums[window.peekLast()] < nums[i]){ window.removeLast(); }        window.add(i);        //队列左侧是最大值,加入结果    if(i >= k - 1){ result[i - k + 1] = nums[window.peek()];    }  }    return result;}


二、哈希表

1.哈希表、哈希函数、哈希碰撞。

哈希表、哈希函数  解决的问题是方便查找  查询的复杂度是O(1)  哈希碰撞  99%情况会存在碰撞  拉链法解决冲突,即数组元素退化为一个链表的数据结构存储


2.不同语言的内置实现:

3.映射(Map),元素的Key值不允许重复

HashMap 哈希表的实现 无序  插入、删除、查询的平均复杂度是O(1)  TreeMap 二叉搜索树的实现 有序  查询的复杂度是O(log N)


4.集合(Set),元素不允许重复

HashSet 哈希表的实现 无序 插入、删除、查询的平均复杂度是O(1)
TreeSet 二叉搜索树的实现 有序 查询的复杂度是O(log N)


5.有效的字母异位词?

    思路1:数组排序,对不同字符串的排序结果做比较,相同即可。

/*** 时间复杂度是O(N * log N)*/public boolean isAnagram(String a, String b) {
if (a.length() != b.length()) { return false; }
char[] c1 = a.toCharArray();  char[] c2 = b.toCharArray();
Arrays.sort(c1); Arrays.sort(c2);
return String.copyValueOf(c1).equals(String.copyValueOf(c2));}

    思路2:Map计数

/*** 时间复杂度是O(N)*/public boolean isAnagram(String a, String b) {
if (a.length() != b.length()) { return false; }
char[] c1 = a.toCharArray(); char[] c2 = b.toCharArray();
//a.length() == b.length() int length = a.length(); Map<Character, Integer> map1 = new HashMap<>(); Map<Character, Integer> map2 = new HashMap<>(); for (int i = 0; i < length; i++) { if (map1.get(c1[i]) == null) { map1.put(c1[i], 0); } else { map1.put(c1[i], map1.get(c1[i]) + 1); }
if (map2.get(c2[i]) == null) { map2.put(c2[i], 0); } else { map2.put(c2[i], map2.get(c2[i]) + 1); } }
return map1.equals(map2);}


6.数组中两数之和等于目标值?

/*** 输入* nums = [2,7,11,15]* target = 9* 输出*  因为nums[0] + nums[1] = 9*  因此输出数组下标[0,1]*/

    思路1:暴力求解,两层循环

/**时间复杂度是O(N^2)*/public int[] twoSum(int[] nums, int target) {  if (nums == null) { return new int[]{}; }   int[] result = new int[2];  for(int i = 0; i nums.length-1i++){    for(int j = i+1; j < nums.length-1j++){      if(nums[i] + nums[j] == 9){       result[0] = i;       result[1] = j;       return result;      } } }  return result;}

    思路2:Set,一次循环 + 一次查找

/*** 时间复杂度是O(2N)*/public int[] twoSum(int[] nums, int target) {
if (nums == null) { return new int[]{}; } int[] result = new int[2];  HashSet<Integer> set = new HashSet<>();  for (int i = 0; i < nums.length-1; i++) { set.add(nums[i]); }   int temp;  for (int i = 0; i<nums.length-1; i++){    temp = target - nums[i];    if(set.contains(temp)){   result[0] = i;      //找到result[1]即可;      return result;   }  }   return result;}

    思路3:Map,key用来记录nums[i]的值,value用来记录对应的下标。

/*** 时间复杂度是O(2N)*/public int[] twoSum(int[] nums, int target) {
if (nums == null) { return new int[]{}; } int[] result = new int[2]; Map<Integer,Integer> map = new HashMap<>();  for (int i = 0; i < nums.length-1; i++) { map.put(nums[i], i); } for (int i = 0; i < nums.length-1; i++) { int temp = target - nums[i]; if(map.containsKey(temp) && map.get(temp) != i){      result[0] = i; result[1] = map.get(temp); } } return result;}        


7.数组中数之和等于目标值

/*** 输入*  nums = [-1,0,1,2,-1,-4]* target = 0*输出数组元素* [-1,0,1]、[-1,2,-1]*/

    思路1:暴力求解,三层循环

/*** 时间复杂度是O(N^3)*/

    思路2:Set,两次循环 + 一次查找

/*** 使用Map/Set消除一层循环* 时间复杂度是O(N^2),空间复杂度是O(N)*/public List<Integer[]> threeSum(int[] arr) {
if(arr ==null || arr.length == 0){ return new ArrayList(); }
List<Integer[]> resultList = new ArrayList();
Arrays.sort(arr);
for(int i=0; i<arr.length-1; i++){ //重复的过滤掉 if(i>0 && arr[i] == arr[i-1]){ continue; }
Map<Integer,Integer> targetMap = new HashMap<>(); for(int j=i+1; j<arr.length; j++){ if(targetMap.containsKey(arr[j])){ if(targetMap.get(arr[j]) == 0){ resultList.add(new Integer[]{arr[i], arr[j], -arr[i] - arr[j]}); targetMap.put(arr[j],1); } }else{ targetMap.put(-arr[i] - arr[j],0); } } } return resultList;}

    思路3:Sort + Find

/*** 时间复杂度是O(N^2)*/public List<Integer[]> threeSum(int[] arr) {  if (arr == null || arr.length < 3) {    return new ArrayList<>(); }  Arrays.sort(arr);
List<Integer[]> resultList = new ArrayList<>();  for(int i=0; i<arr.length-2; i++){    if(i>0 && arr[i]==arr[i-1]){ continue; } int left = i+1;    int right = arr.length-1;    while(left < right){      int s = arr[i] + arr[left] + arr[right];      if(s < 0){ left += 1;      }else if(s > 0){       right -= 1;      }else{       resultList.add(new Integer[]{i, left, right});              while(left < right && arr[left] == arr[left+1]){       left += 1;       }               while(left < right && arr[right] == arr[right-1]){       right -= 1; } left += 1; right -= 1;      }    }     }
  return resultList;    }