4.11 为次日和后天面试前复习内容
详见:《数仓建设学习路线.xmind》
常见面试sql题:
-
连续登录(查询连续登录3天以上用户、查询连续登录最大天数用户) (sum(case …when…then…end))
-
lead/lag使用(股票波峰波谷)(波峰,比前一天,后一天数值都高;波谷相反)
-
天/月gmv汇总
-
相互关注
-
炸裂函数(可用于行列转换问题,
行转列(split + explode + laterview)
列转行 ( concat_ws + collect_list/set )行列转换问题也可以用sum(case…when …then. .end)(列转行)和union all(行转列)来处理)
-
排序
-
每一门课大于60分(意味着最低分大于60)
-
七日留存
算法题(简单or中等)
-
快排(mapreduce中shuffle端),时间复杂度nlogn,和冒泡一样,都是交换排序。需要定基准值,一般可以取首值
写快排就3个主要方法,一个是交换swap,一个是找基准partition,一个是递归过程quick方法,递归直到left >= right 的时候返回
public class QuickSort{ public static void main(String[] args){ int[] arr = {1,3,6,4,5,2,8,7}; quickSort(arr,0,arr.length-1); for(int a : arr){ System.out.println(a); } } public static void quickSort(int[] arr,int L,int R){ // 递归退出的条件 if(L < R){ // 随机生成一个数作为基准值 // swap(arr,L+(int)(Math.random() * (R-L+1)),R); // 在数组中随机找一个基准值并与数组最后一个数交换位置 swap(arr,L+(int)(Math.random() * (R-L+1)),R); // 根据标准值进行patition int[] p = partition(arr,L,R); // 左边递归 quickSort(arr,L,p[0]-1); // 右边递归 quickSort(arr,p[1]+1,R); } } public static int[] partition(int[] arr, int L,int R ) { int less = L-1; //左边界的位置 int more = R; //右边界的位置 // 判断当前的数的位置是否与右边界more碰撞,发生碰撞就退出循环 while(L < more){ if(arr[L] < arr[R]){ // ++less 和 L++区别就是,在调用swap前,less先自增在传值, //L++先传值,等交换完之后在自增,注意方法传参时,先加加(++i)和后加加(i++)的区别 swap(arr,++less,L++); }else if(arr[L] > arr[R]){ // --more的效果就是把当前遍历到的数与more前面一个进行交换,所以是先-- swap(arr,--more,L); }else{ // 如果遍历到的数与基准值arr[R]相等就跳过 L++; } } // 这里就是遍历到的数与大于区域的边界碰撞时,把基准值与大于区域的第一个数进行交换 // 那么基准值的位置就确定了 swap(arr,more,R); // 然后把基准值的数组所在的范围返回 return new int[] {less+1,more}; } public static void swap(int[] arr,int i,int j){ int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } }
另外版本:好理解,但是时间复杂度高些:0(n^2)
public class Quicksort { public void quicksort(int[] a, int low,int high){ int start = low; int end = high; int key = a[low];//基准值 while (end > start){ //从后往前遍历 while(end > start && a[end] >= key){ //没有比基准值小的,则比较下一个,直到有比关键值小的交换位置 end--; if (a[end] <= key){ int temp = key; a[end] = a[start]; a[start] = temp; } } //从前往后比较 while (end > start && a[start] <= key){ //如果没有比基准值大的,比较下一个,直到有比关键值大的交换位置 start++; if (a[start] >= key){ int temp = a[start]; a[start] = a[end]; a[end] = temp; } //此时第一次循环比较结束,关键值的位置已经确定。左边的值都比关键值小,右边的都比关键值大, //但是两边的顺序还有可能是不一样的,进行下面的递归调用 } //递归 if (start > low){ quicksort(a,low,start - 1); } if (end < high){ quicksort(a,end + 1,high); } } } }
-
归并排序(mapreduce中reduce端),时间复杂度nlogn,空间复杂度O(n),相同元素的前后顺序并没有改变,所以是一种稳定排序算法
import java.util.Arrays; public class Gui { public static void main(String[] args) { // TODO Auto-generated method stub int arr[] = {6,5,3,1,8,7,2,4}; mergeSort(arr,0,arr.length-1); System.out.println("排序结果:"+Arrays.toString(arr)); } public static void mergeSort(int[] a, int low, int high) { //首先判断 low 和 high是否指向一个地方 // 正常情况下就是 == if(low == high) { return; } int mid = (low + high)/2; //先递归左边 mergeSort(a, low, mid); //在递归右边 mergeSort(a, mid+1, high); //合并 merge(a,low,mid,high); System.out.println(Arrays.toString(a)); } //合并 public static void merge(int[] a,int low,int mid,int high) { //定义第一段 int s1 = low; //定义第二段 int s2 = mid+1; //定义临时数组 int[] temp =new int[high-low+1]; int i = 0; //判断s1,s2是否有数据,放入临时数组 while(s1<=mid) { temp[i++]=a[s1++]; } while(s2<=high) { temp[i++]=a[s2++]; } for(int j = 0;j < temp.length;j++) { a[j+low]=temp[j]; } } }
-
二分查找
定义好,左右边界,mid = left + (right - left) / 2,while(left <= right)
左闭右闭区间
class Solution { public int search(int[] nums, int target) { // 避免当 target 小于nums[0] nums[nums.length - 1]时多次循环运算 if (target < nums[0] || target > nums[nums.length - 1]) { return -1; } int left = 0, right = nums.length - 1; while (left <= right) { int mid = left + ((right - left) >> 1); if (nums[mid] == target) return mid; else if (nums[mid] < target) left = mid + 1; else if (nums[mid] > target) right = mid - 1; } return -1; } }
-
链表反序
双指针法,两指针cur,pre,cur指向头结点,pre初始化null
// 双指针 class Solution { public ListNode reverseList(ListNode head) { ListNode prev = null; ListNode cur = head; ListNode temp = null; while (cur != null) { temp = cur.next;// 保存下一个节点 cur.next = prev; prev = cur; cur = temp; } return prev; } }
-
两数之和
可暴力,可哈希表Hashmap,存(nums[i],i)
public int[] twoSum(int[] nums, int target) { int[] res = new int[2]; if(nums == null || nums.length == 0){ return res; } Map<Integer, Integer> map = new HashMap<>(); for(int i = 0; i < nums.length; i++){ int temp = target - nums[i]; // 遍历当前元素,并在map中寻找是否有匹配的key if(map.containsKey(temp)){ res[1] = i; res[0] = map.get(temp); break; } map.put(nums[i], i); // 如果没找到匹配对,就把访问过的元素和下标加入到map中 } return res; }