仅以这几篇博文记录我的秋招之路
数据结构面试题
- 排序
- 归并
- 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++;
}
}
}