常见的排序算法、笔面试常考(Java版),2024年最新中软国际面试问题

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

4.希尔排序

=========================================================================

希尔排序是Donald Shell于1959年提出的一种排序算法,是对直接插入排序的改进之后的高效版本。

希尔排序需要准备一组数据作为增量序列。这组数据需要满足以下三个条件:

  • 数据递减排列

  • 数据中的最大值小于待排序数据的长度

  • 数据中的最小值是

只要满足上述要求的数组都可以作为增量序列,但是不同的增量序列会影响到排序的效率。这是我们用 {5,3,1} 作为增量序列来进行讲解。

请添加图片描述

增量为5,就是第一个和第六个为一组进行比较,第二个和第七个一组进行比较,以此类推

我们知道,直接插入排序下列情况效率较高:

  1. 数据基本有序

  2. 待排序个数较少的时候(因为数据量小,所以 O(n) 和 O (n2)的差距并不大)

希尔排序就是利用了这样的性质来优化效率的。在希尔排序开始时增量较大,分组较多。每组的记录数目少,故各组内直接插入较快,后来增量逐渐缩小,分组数逐渐减小,各组的数据逐渐增多,但这时数据已趋于有序状态,所以进行插入得操作较少,排序过程也较快。

4.1 希尔排序增量的选择


理论上来说任选一组增量都可以进行排序,因为最后的增量就是1,就相当于是最后肯定还会对数据整体进行一次插入排序。但是希尔排序算法效率增量序列的取值有关。下面列出3种常见的增量序列供大家草靠,为了方便以公式的形式表达,我们用升序的方式来写增量序列。

请添加图片描述

public class Solution {

public int[] MySort (int[] arr) {

int n = arr.length;

int gap = n / 2;

while (gap >= 1) {

for (int i = gap; i < n; i++) {

int temp = arr[i];

int j = i - gap;

while (j >= 0 && arr[j] > temp) {

arr[j + gap] = arr[j];

j = j - gap;

}

arr[j + gap] = temp;

}

gap = gap / 2;

}

return arr;

}

}

5.堆排序

========================================================================

是一种完全二叉树,分为大根堆,小根堆两种。

  • 可以O(1)取最大/小值;

  • 可以O(logn)删除最大/小值;

  • 可以O(logn)插入元素。

大(小)根堆始终满足一个性质,父亲节点的值大(小)于等于其所有子孙,那么显而易见的,整棵树中的根节点的值就是最大(小)的。

完全二叉树这种数据结构可以用数组实现:

请添加图片描述

排序数据其实是可以看作是一棵完全二叉树,如何快速地把一棵完全二叉树调整成小根堆呢?

5.1MIN-HEAPIFY(i) 操作


我们假设完全二叉树中某节点 i 的左子树和右子树都满足小根堆的性质,假设 i 节点的左孩子是lefti,i 节点的右孩子是righti。那如果a[ i ] 大于 a [lefti] 或 a [righti]的话,那么以 i 节点为根节点的整棵子树就不满足小根堆的性质了。我们现在要进行一个操作:把以 i 为根节点的这棵子树调整成小根堆。

请添加图片描述

5.2删除堆顶元素操作


可以将最后一个数临时的补到堆顶的位置,然后判断他是否可以放在堆顶——即是否小于其两个儿子,如果不是则说明他应该往下交换,将其和两个儿子中的小的那个交换,交换之后继续和他的儿子进行判断和交换,一直交换到合适的位置。

这样就可以保证删除堆顶元素之后还维持小根堆的性质。其实也可以理解为把最后一个数补到堆顶位置之后,堆的大小减一,再进行一次MIN-HEAPIFY操作。

请添加图片描述

5.3排序过程


具体的图解可以看这篇博课 -->> Java实现堆排序和图解

我们先把待排序数组调整成堆,那么堆顶就是整个数组中的最小值。然后我们删除堆顶元素,删除堆顶元素的操作第一步要把最后一个数,临时补到堆顶的位置,这个时候对于堆这个数据结构来说,数组的最后一位其实就没意义了,但是对于实现这个数据结构的数组来说,最后一位还是在物理上存在的。那么我们可以把删除的堆顶元素赋值到数组的最后一位。相当于把最小值放到了数组的最后。这样的话依次删除堆顶元素就相当于依次把最小的值放在了最后。对于小根堆来说,所有元素都删除完毕了以后,整个数组就形成了降序。如果我们需要升序的话,就需要把数组调整成大根堆。

/*

注意:这里的数组是从0开始存储的,

所以我们取左儿子的时候是i*2+1

所以我们取右儿子的时候是i*2+2

*/

public class Solution {

//记录堆的大小

int headSize;

//分别取左右节点的下标

public int Left (int i) {

if (i * 2 + 1 >= headSize) return -1;

return i * 2 + 1;

}

public int Right (int i) {

if (i * 2 + 2 >= headSize) return -1;

return i * 2 + 2;

}

//调整大根堆的操作

public void minHeapify (int[] arr, int i) {

//得到i左右节点的下标

int left = Left(i);

int right = Right(i);

//比较大小

//最大值下标

int maxi = i;

if (left != -1 && arr[left] > arr[i]) {

maxi = left;

}

if (right != -1 && arr[right] > arr[maxi]) {

maxi = right;

}

if (maxi != i) {

swap(arr, i, maxi);

i = maxi;

minHeapify(arr, i);

}

}

public void Delete (int[] arr) {

swap(arr, headSize - 1, 0);

headSize–;

minHeapify(arr, 0);

}

public void swap(int arr[], int i, int j){

int temp = arr[i];

arr[i] = arr[j];

arr[j] = temp;

}

public int[] MySort (int[] arr) {

int n = arr.length;

headSize = n;

for (int i = (n - 1) / 2; i >= 0; i–) {

minHeapify(arr, i);

}

for (int i = 0; i < n; i++) {

Delete(arr);

}

return arr;

}

}

6.归并排序

=========================================================================

归并排序是分治法的典型应用,先来介绍一下分治法,分治法是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题…直到最后的子问题规模很小可以直接求解,再将子问题的解进行合并来得到原问题的解。

请添加图片描述

归并排序分解子问题的过程就是每一次把数组分成2份,直到数组的长度为1(因为只有一个数字的数组是有序的)。然后再将相邻的有序数组合并成一个有序数组,知道全部合到一起,整个数组就排序完毕了。现在要解决的问题就是如何将两个有序的数组合并成一个有序数组。其实就是每次比较两个数组当前最小的两个元素,哪个小就选哪个。

请添加图片描述

public class Solution {

//将排好序的两个数组,重新合并到新的数组里面

public void merge (int[] arr, int l, int r) {

int mid = (l + r) / 2;

// 新建两个数组,lenL、lenR分别为新数组的长度

int lenL = mid - l + 1;

int lenR = r - mid;

int[] L = new int[lenL];

int[] R = new int[lenR];

for (int i = 0; i < lenL; i++) {

L[i] = arr[l + i];

}

for (int i = 0; i < lenR; i++) {

R[i] = arr[mid + 1 + i];

}

int i = 0, j = 0;

for (int index = l; index <= r; index++) {

// 选左边数组中的值

if (j >= lenR || (i < lenL && L[i] < R[j])) {

arr[index] = L[i];

i++;

} else {

// 选右边数组中的值

arr[index] = R[j];

j++;

}

}

}

public void mergeSort (int[] arr, int l, int r) {

if (l < r) {

int mid = (l + r) / 2;

mergeSort(arr, l, mid);

mergeSort(arr, mid + 1, r);

// 排好序后,进行合并

// 从 l 到 mid 是排好序的

// 从 mid + 1 到 r 是排好序的,才会调用meger()方法

merge(arr, l ,r);

}

}

public int[] MySort (int[] arr) {

int n = arr.length;

mergeSort(arr, 0, n - 1);

return arr;

}

}

7.快速排序

=========================================================================

快速排序的工作原理是:**从待排序数组中随便挑选一个数字作为基准数,把所有比它小的数字放在它的左边,所有比它大的数字放在它的右边。然后再对它左边的数组和右边的数组递归进行这样的操作。**全部操作完成以后整个数组就是有序的了。

把所有比基准数小的数字放在它的左边,所有比基准数大的数字放在它的右边。这个操作,我们称为“划分”(Partition)。

7.1“划分”(Partition)算法导论写法


“划分”(Partition)的操作可以在O(n)的时间复杂度进行,有很多种方法可以进行这个操作.。

《算法导论》里给出的解法如下:

请添加图片描述

每次以数组中的最后一个数作为基准数。

请添加图片描述

public class Solution {

// 划分方法

public int partition (int[] arr, int l, int r) {

int i = l;

int x = arr[r]; // 取基准数字

for (int j = l; j < r; j++) {

if (arr[j] < x) {

swap(arr, i, j);

i++;

}

}

swap(arr, r, i);

return i;

}

public void quickSort (int[] arr, int l, int r) {

if (l < r) {

int mid = partition(arr, l, r);

quickSort(arr, l, mid - 1);

quickSort(arr, mid + 1, r);

}

}

public void swap(int arr[], int i, int j){

int temp = arr[i];

arr[i] = arr[j];

arr[j] = temp;

}

public int[] MySort (int[] arr) {

int n = arr.length;

quickSort(arr, 0, n - 1);

return arr;

}

}

7.2 “填坑法”


直接看图&代码 ↓

请添加图片描述

public class Solution {

// 划分方法

public int partition (int[] arr, int l, int r) {

int x = arr[l]; //基准数

总结

虽然面试套路众多,但对于技术面试来说,主要还是考察一个人的技术能力和沟通能力。不同类型的面试官根据自身的理解问的问题也不尽相同,没有规律可循。

上面提到的关于这些JAVA基础、三大框架、项目经验、并发编程、JVM及调优、网络、设计模式、spring+mybatis源码解读、Mysql调优、分布式监控、消息队列、分布式存储等等面试题笔记及资料

有些面试官喜欢问自己擅长的问题,比如在实际编程中遇到的或者他自己一直在琢磨的这方面的问题,还有些面试官,尤其是大厂的比如 BAT 的面试官喜欢问面试者认为自己擅长的,然后通过提问的方式深挖细节,刨根到底。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
不同类型的面试官根据自身的理解问的问题也不尽相同,没有规律可循。

[外链图片转存中…(img-jog0o74y-1713467689486)]

[外链图片转存中…(img-8hBQARTE-1713467689487)]

上面提到的关于这些JAVA基础、三大框架、项目经验、并发编程、JVM及调优、网络、设计模式、spring+mybatis源码解读、Mysql调优、分布式监控、消息队列、分布式存储等等面试题笔记及资料

有些面试官喜欢问自己擅长的问题,比如在实际编程中遇到的或者他自己一直在琢磨的这方面的问题,还有些面试官,尤其是大厂的比如 BAT 的面试官喜欢问面试者认为自己擅长的,然后通过提问的方式深挖细节,刨根到底。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-rpUpOi14-1713467689488)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 20
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值