仅以这几篇博文记录我的秋招之路


数据结构面试题

  • 排序
  • 归并
  • 1、面试高频问题:排序(O(1) 复杂度的归并)
  • 快排
  • 快速排序的递归实现
  • 数组中的K最大
  • 最小的数及其之前的数
  • 寻找两个有序数组的中位数
  • 堆排序
  • 堆排序的手写实现
  • 堆排序实现优先队列
  • 优先队列实现TopK
  • 优先队列实现中位数
  • 优先队列实现出现频率最大
  • 冒泡排序 (空天院一面)
  • 单调栈
  • 单调栈应用
  • 1、 环状数组求下一个最大
  • 2、 接雨水
  • 3、最大柱状矩形
  • 4、最大连续1的面积 ---> 力扣85
  • 5、每日温度
  • 单调队列
  • 单调队列的应用
  • 解决窗口内最优的问题 (网易一面)经典应用


排序

归并

1、面试高频问题:排序(O(1) 复杂度的归并)

//空间复杂度O(1),时间复杂度o(nlogn)
    public static void main(String[] args) {
        int[] arr = new int[]{14,12,15,13,11,16};
        //找到数组的最大值,并计算出maxval,然后调用递归的归并排序
        int maxval = Arrays.stream(arr).max().getAsInt() + 1;
        mergeSortDigui(arr, 0, arr.length - 1, maxval);
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }

    // 递归归并排序 算法优化
    public static void mergeSortDigui(int[] nums, int l, int r,int max){
        if(l < r){
            int mid = l + (r - l)/2;
            mergeSortDigui(nums,l, mid,max);
            mergeSortDigui(nums, mid + 1, r,max);
            mergeSortO1(nums, l, mid, r, max);
        }
    }
    //O(1)复杂度实现
    //用两个数来存储一个数
    public static void mergeSortO1(int[] arr, int l, int mid, int r, int max) {
        int start0 = l, start1 = mid+1, i = l;
        while(start0 <= mid && start1 <= r){
            if(arr[start0] % max <= arr[start1] % max){
                arr[i] = arr[i] + (arr[start0] % max) *max;
                i++;
                start0++;
            }
            else {
                arr[i] = arr[i] + (arr[start1] % max ) *max;
                i++;
                start1++;
            }
        }
        while (start0 <= mid){
            arr[i] = arr[i] + (arr[start0] % max) *max;
            i++;
            start0++;
        }
        while (start1 <= r){
            arr[i] = arr[i] + (arr[start1] % max ) *max;
            i++;
            start1++;
        }
        for(start0 = l; start0 <= r; start0++)
            arr[start0] = arr[start0] /max;
    }

快排

快速排序的递归实现

public static void main(String[] args) {
        int[] nums = new int[]{5,3,8,6,4};
        quickSort(nums, 0, nums.length-1);
    }
    public static void quickSort(int[] nums, int l, int r){
        if(l >= r) return;
        int base = nums[l], i = l, j = r;
        while (i < j){
            while (i < j && nums[j] > base)
                j--;
            if(i < j)
                nums[i++] = nums[j];
            while (i < r && nums[i] < base)
                i++;
            if(i < r)
                nums[j--] = nums[i];
        }
        nums[i] = base;
        quickSort(nums, l, i-1);
        quickSort(nums, i+1, r);
    }

数组中的K最大

public static int Knumber(int[] nums, int k){
	int n = nums.length;
	return quickSort(nums, 0, n-1, n-k);
}
public static int quickSort(int[] nums,int l, int r,int k){
        if(l >= r) return nums[l];
        int base = nums[l], i = l, j = r;
        while (i < j){
            while(i < j && nums[j] > base)
                j--;
            if(i < j)
                nums[i++] = nums[j];
            while (i < j && nums[i] < base)
                i++;
            if(i < r)
                nums[j--] = nums[i];
        }
        nums[i] = base;
        if(i == k)
            return nums[i];
        return i > k? quickSort(nums, l, i-1, k):quickSort(nums, i+1, r, k);
    }

最小的数及其之前的数

public static int[] KsmallerNumber(int[] nums, int k){
	int n = nums.length;
	quickSort(nums, 0, n - 1, k);//此时quickSort无返回值,只实现排序功能。
	int[] num = new int[k];
	for(int i = 0; i< k;i++){
		num[i] = nums[i];
	}
 }

寻找两个有序数组的中位数

public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int[] nums = new int[nums1.length + nums2.length];
        for(int i = 0; i< nums1.length;i++){
            nums[i] = nums1[i];
        }
        int j = 0;
        for(int i = nums1.length; i < nums.length;i++){
            nums[i] = nums2[j];
            j++;
        }
        int n = nums.length;
        if(n%2 == 0)
            return (double)(quickSort(nums, 0, n-1, n/2-1) + quickSort(nums,n/2, n-1,n/2))/2;
        else
            return quickSort(nums, 0, n-1, n/2);
    }

堆排序

堆排序的手写实现

public static void main(String[] args) {
        int[] nums = new int[]{14,12,15,13,11,16};
        heapSort(nums);
    }
    //堆排序空间复杂度O(1),时间复杂度o(nlogn)
    public static void heapSort(int[] arr) {
        for(int i = arr.length / 2 - 1; i >= 0; i--) {
            adjustHeap(arr, i, arr.length);
        }
        for(int j = arr.length - 1; j > 0; j--) {
            swap(arr, 0, j);
            adjustHeap(arr, 0, j);
        }
    }
    public static void swap(int[] array, int i, int j) {
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
    public static void adjustHeap(int[] arr, int i, int length) {
        int temp = arr[i];//先取出当前元素i
        for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {//从i结点的左子结点开始,也就是2i+1处开始
            if (k + 1 < length && arr[k] < arr[k + 1]) {//如果左子结点小于右子结点,k指向右子结点
                k++;
            }
            if (arr[k] > temp) {//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
                arr[i] = arr[k];
                i = k;
            } else {
                break;
            }
        }
        arr[i] = temp;//将temp值放到最终的位置
    }

堆排序实现优先队列

优先队列实现TopK

优先队列实现中位数

优先队列实现出现频率最大

冒泡排序 (空天院一面)

//时间复杂度O(N2),空间复杂度O1
public static void bubblesort(int[] nums){
        int n = nums.length;
        boolean didswap;  //设置标志位进行优化
        for (int j = 0; j < n; j++) {
            didswap = false;
            for (int i = 0; i < n-1; i++) {
                if(nums[i] > nums[i+1]){
                    swap(nums, i, i+1);
                    didswap = true;//减少在有序时的循环次数
                }
            }
            if (didswap == false)
                return;
        }
    }
    public static void swap(int[] nums, int a, int b){  // 此处注意函数传递,应把nums,i, j,同时传入才可更改
        int c;
        c = nums[a];
        nums[a] = nums[b];
        nums[b] = c;
    }

单调栈

单调栈应用

//单调栈问题,解决下一个更大,上一个比它大的问题。(由于是下一个更大,倒着入栈更易理解)
//更大问题,采用单调递减栈,此时栈顶元素为最大元素,若栈为空,则返回-1
	public static int[] NextGreaterNumberIndex(int[] nums){
        Stack<Integer> stack1 = new Stack<>();
        int n = nums.length, max = 0;
        int[] right = new int[n];
        for (int i = n-1; i >= 0; i--) {
            while (!stack1.isEmpty() && nums[i] >= nums[stack1.peek()])  //易错
                stack1.pop();
            right[i] = stack1.isEmpty() ? -1 : stack1.peek();  //下一个比它大的数,或下标
            stack1.push(i);
        }
        return right;
    }

    public static int[] PreviousGreaterNumber(int[] nums){
        int n = nums.length;
        Stack<Integer> stack1 = new Stack<>();
        int[] left = new int[n];
        for (int i = 0; i < n; i++) {
            while (!stack1.isEmpty() && nums[i] >= nums[stack1.peek()])
                stack1.pop();
            left[i] = stack1.isEmpty() ? -1 : stack1.peek();
            stack1.push(i);
        }
        return left;
    }

拓展应用+延伸:

1、 环状数组求下一个最大

public static int[] CircleNextGreaterNumberIndex(int[] nums){
        int n = nums.length;
        Stack<Integer> stack1 = new Stack<>();
        int[] right = new int[n];
        for (int i = 2*n - 1; i >= 0; i--) {
            while (!stack1.isEmpty() && nums[i % n] >= nums[stack1.peek()])
                stack1.pop();
            right[i % n] = stack1.isEmpty() ? -1 : stack1.peek();
            stack1.push(i % n);
        }
        return right;
    }

2、 接雨水

未做。

3、最大柱状矩形

//边界问题,采用单调递增栈
//此时栈顶元素为最小边界,若为空,则返回数组两边边界值。
public static int[] largestRectangleArea(int[] nums){
		int n = heights.length;
        int res = Integer.MIN_VALUE;
        int[] left = new int[n];
        int[] right = new int[n];
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < n ; i++) {
            while (!stack.isEmpty() && heights[i] <= heights[stack.peek()])
                stack.pop();
            left[i] = stack.isEmpty()? -1 : stack.peek();
            stack.push(i);
        }
        for (int i = 0; i < stack.size(); i++) {
            System.out.println(stack.pop());
        }
        stack.clear();
        for (int i = n-1; i >= 0 ; i--) {
            while (!stack.isEmpty() && heights[i] <= heights[stack.peek()])
                stack.pop();
            right[i] = stack.isEmpty()? n : stack.peek();
            stack.push(i);
        }

        for (int i = 0; i < n ; i++) {
            res = Math.max(res, (right[i]-left[i] - 1) * heights[i]);
        }
       	return res;
}

4、最大连续1的面积 —> 力扣85

public static int MaximalRectangle(int[][] matrix){
		int rows = matrix.length;
        if(rows == 0) System.out.println(0);
        int cols = matrix[0].length;
        int[][] ans = new int[rows][cols];
        int[][] left = new int[rows][cols];
        int[][] right = new int[rows][cols];
        int area = Integer.MIN_VALUE;
		for (int i = 0; i < cols; i++) {  //计算每列中连续1的个数
               int nums_1 = 0;
               for (int j = 0; j < rows; j++) {
                   nums_1 = matrix[j][i] * nums_1 + matrix[j][i];
                   ans[j][i] = nums_1;
               }
        }
        for (int i = 0; i < rows; i++) {  //计算矩阵中每个值的左边与右边的边界
            Stack<Integer> stack = new Stack<>();
            for (int j = 0; j < cols; j++) {   //计算边界值
                while (!stack.isEmpty() && ans[i][j] <= ans[i][stack.peek()])
                    stack.pop();
                left[i][j] = stack.isEmpty()? -1 : stack.peek();
                stack.push(j);
            }
            stack.clear();
            for (int j = cols-1; j >= 0; j--) {
                while (!stack.isEmpty() && ans[i][j] <= ans[i][stack.peek()])
                    stack.pop();
                right[i][j] = stack.isEmpty()? cols : stack.peek();
                stack.push(j);
            }
        }
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                area = Math.max((right[i][j] - left[i][j] - 1) * ans[i][j] , area);
            }
        }
        return area;
	}

5、每日温度

求下一个更大数离当前索引的距离,若无下一个最大数,返回0。维护单调递增栈。

public int[] dailyTemperatures(int[] temperatures) {
        int n = temperatures.length;
        Stack<Integer> stack = new Stack<>();
        int[] res = new int[n]; 
        for(int i = 0; i < n;i++){
            while(!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]){
                int index = stack.pop();
                res[index] = i - index;
            }
            stack.push(i);
        }
        return res;
    }

单调队列

单调队列的应用

解决窗口内最优的问题 (网易一面)经典应用

public static int MaxSub(int[] nums, int k){
		int n = nums.length; 
        int max = Integer.MIN_VALUE, min = Integer.MAX_VALUE;  //窗口内当前最大值与最小值
        int sub = 0;  
        int left = 0, right = 0;  //双指针控制窗口大小
        LinkedList<Integer> greater_queue = new LinkedList<>();  //维护一个单调递减队列,队头元素极为最大值
        LinkedList<Integer> lower_queue = new LinkedList<>();   //维护一个单调递增队列,队头元素极为最小值
        while (left <= n - k){  //限定循环结束条件,按最后一次计算sub后的变量值为准
            if(right - left < k){  // 窗口内执行
                while (greater_queue.size() != 0 && nums[right] > greater_queue.getLast())
                    greater_queue.removeLast();
                greater_queue.add(nums[right]);
                max = greater_queue.getFirst();

                while (lower_queue.size() != 0 && nums[right] < lower_queue.getLast())
                    lower_queue.removeLast();
                lower_queue.add(nums[right]);
                min =  lower_queue.getFirst();
                right++;
            }
            else {   // 缩小窗口
                sub = Math.max(max - min, sub);  // 计算sub
                if(nums[left] == greater_queue.getFirst())   //判断队首元素与出队元素之间的关系
                    greater_queue.removeFirst();
                if(nums[left] == lower_queue.getFirst())  
                    lower_queue.removeFirst();
                left++;
            }
        }
}