本
文
摘
要
冒泡排序
思路:俩俩交换,大的放在后面,第一次排序后最大值已在数组末尾。因为俩俩交换,需要n-1趟排序(比如10个数,需要9趟排序)
代码实现要点:两个for循环,外层循环控制排序的趟数,内层循环控制比较的次数。每趟过后,比较的次数都应该要减1
//装载临时变量 int temp; //记录是否发生了置换, 0 表示没有发生置换、 1 表示发生了置换 int isChange; //记录执行了多少趟 int num = 0; //外层循环是排序的趟数 for (int i = 0; i < arrays.length -1 ; i++) { //每比较一趟就重新初始化为0 isChange = 0; //内层循环是当前趟数需要比较的次数 for (int j = 0; j < arrays.length - i - 1; j++) { //前一位与后一位与前一位比较,如果前一位比后一位要大,那么交换 if (arrays[j] > arrays[j + 1]) { temp = arrays[j]; arrays[j] = arrays[j + 1]; arrays[j + 1] = temp; //如果进到这里面了,说明发生置换了 isChange = 1; } } //如果比较完一趟没有发生置换,那么说明已经排好序了,不需要再执行下去了 if (isChange == 0) { break; } num++; }选择排序
思路:找到数组中最大的元素,与数组最后一位元素交换。当只有一个数时,则不需要选择了,因此需要n-1趟排序
代码实现要点:两个for循环,外层循环控制排序的趟数,内层循环找到当前趟数的最大值,随后与当前趟数组最后的一位元素交换
//记录当前趟数的最大值的角标 int pos ; //交换的变量 int temp; //外层循环控制需要排序的趟数 for (int i = 0; i < arrays.length - 1; i++) { //新的趟数、将角标重新赋值为0 pos = 0; //内层循环控制遍历数组的个数并得到最大数的角标 for (int j = 0; j < arrays.length - i; j++) { if (arrays[j] > arrays[pos]) { pos = j; } } //交换 temp = arrays[pos]; arrays[pos] = arrays[arrays.length - 1 - i]; arrays[arrays.length - 1 - i] = temp; } System.out.pr插入排序
思路:将一个元素插入到已有序的数组中,在初始时未知是否存在有序的数据,因此将元素第一个元素看成是有序的。与有序的数组进行比较,比它大则直接放入,比它小则移动数组元素的位置,找到个合适的位置插入。当只有一个数时,则不需要插入了,因此需要n-1趟排序
代码实现:一个for循环内嵌一个while循环实现,外层for循环控制需要排序的趟数,while循环找到合适的插入位置(并且插入的位置不能小于0)
//临时变量 int temp; //外层循环控制需要排序的趟数(从1开始因为将第0位看成了有序数据) for (int i = 1; i < arrays.length; i++) { temp = arrays[i]; //如果前一位(已排序的数据)比当前数据要大,那么就进入循环比较[参考第二趟排序] int j = i - 1; while (j >= 0 && arrays[j] > temp) { //往后退一个位置,让当前数据与之前前位进行比较 arrays[j + 1] = arrays[j]; //不断往前,直到退出循环 j--; } //退出了循环说明找到了合适的位置了,将当前数据插入合适的位置中快速排序
学习快速排序的前提:需要了解递归思路:在数组中找一个元素(节点),比它小的放在节点的左边,比它大的放在节点右边。一趟下来,比节点小的在左边,比节点大的在右边。不断执行这个操作….
代码实现:支点取中间,使用L和R表示数组的最小和最大位置。不断进行比较,直到找到比支点小(大)的数,随后交换,不断减小范围。递归L到支点前一个元素(j)。递归支点后一个元素(i)到R元素
/** * 快速排序 * * @param arr * @param L 指向数组第一个元素 * @param R 指向数组最后一个元素 */ public static void quickSort(int[] arr, int L, int R) { int i = L; int j = R; //支点 int pivot = arr[(L + R) / 2]; //左右两端进行扫描,只要两端还没有交替,就一直扫描 while (i <= j) { //寻找直到比支点大的数 while (pivot > arr[i]) i++; //寻找直到比支点小的数 while (pivot < arr[j]) j--; //此时已经分别找到了比支点小的数(右边)、比支点大的数(左边),它们进行交换 if (i <= j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; i++; j--; } } //上面一个while保证了第一趟排序支点的左边比支点小,支点的右边比支点大了。 //“左边”再做排序,直到左边剩下一个数(递归出口) if (L < j) quickSort(arr, L, j); //“右边”再做排序,直到右边剩下一个数(递归出口) if (i < R) quickSort(arr, i, R)归并排序
学习归并排序的前提:需要了解递归思路:将两个已排好序的数组合并成一个有序的数组。将元素分隔开来,看成是有序的数组,进行比较合并。不断拆分和合并,直到只有一个元素
代码实现:在第一趟排序时实质是两个元素(看成是两个已有序的数组)来进行合并,不断执行这样的操作,最终数组有序,拆分左边,右边,合并…
/** * 归并排序 * * @param arrays * @param L 指向数组第一个元素 * @param R 指向数组最后一个元素 */ public static void mergeSort(int[] arrays, int L, int R) { //如果只有一个元素,那就不用排序了 if (L == R) { return; } else { //取中间的数,进行拆分 int M = (L + R) / 2; //左边的数不断进行拆分 mergeSort(arrays, L, M); //右边的数不断进行拆分 mergeSort(arrays, M + 1, R); //合并 me/** * 合并数组 * * @param arrays * @param L 指向数组第一个元素 * @param M 指向数组分隔的元素 * @param R 指向数组最后的元素 */ public static void merge(int[] arrays, int L, int M, int R) { //左边的数组的大小 int[] leftArray = new int[M - L]; //右边的数组大小 int[] rightArray = new int[R - M + 1]; //往这两个数组填充数据 for (int i = L; i < M; i++) { leftArray[i - L] = arrays[i]; } for (int i = M; i <= R; i++) { rightArray[i - M] = arrays[i]; } int i = 0, j = 0; // arrays数组的第一个元素 int k = L; //比较这两个数组的值,哪个小,就往数组上放 while (i < leftArray.length && j < rightArray.length) { //谁比较小,谁将元素放入大数组中,移动指针,继续比较下一个 if (leftArray[i] < rightArray[j]) { arrays[k] = leftArray[i]; i++; k++; } else { arrays[k] = rightArray[j]; j++; k++; } } //如果左边的数组还没比较完,右边的数都已经完了,那么将左边的数抄到大数组中(剩下的都是大数字) while (i < leftArray.length) { arrays[k] = leftArray[i]; i++; k++; } //如果右边的数组还没比较完,左边的数都已经完了,那么将右边的数抄到大数组中(剩下的都是大数字) while (j < rightArray.length) { arrays[k] = rightArray[j]; k++; j++;堆排序
学习堆排序的前提:需要了解二叉树思路:堆排序使用到了完全二叉树的一个特性,根节点比左孩子和右孩子都要大,完成一次建堆的操作实质上是比较根节点和左孩子、右孩子的大小,大的交换到根节点上,直至最大的节点在树顶。随后与数组最后一位元素进行交换
代码实现:只要左子树或右子树大于当前根节点,则替换。替换后会导致下面的子树发生了变化,因此同样需要进行比较,直至各个节点实现父>子这么一个条件
public class HeapifySort { public static void main(String[] args) { int[] arrays = {6, 3, 8, 5,2,-1,-5,-2,-6,345,7, 5, 1, 2, 23, 4321, 432, 3, 2, 34234, 2134, 1234, 5, 132423, 234, 4, 2, 4, 1, 5, 2, 5}; // 完成一次建堆.. maxHeapify(arrays, arrays.length - 1); int size = arrays.length - 1; for (int i = 0; i < arrays.length; i++) { //交换 int temp = arrays[0]; arrays[0] = arrays[(arrays.length - 1) - i]; arrays[(arrays.length - 1) - i] = temp; // 调整位置 heapify(arrays, 0, size); size--; */ public static void maxHeapify(int[] arrays, int size) { for (int i = size - 1; i >= 0; i--) { heapify(arrays, i, size); } } /** * 建堆 * * @param arrays 看作是完全二叉树 * @param currentRootNode 当前父节点位置 * @param size 节点总数 */ public static void heapify(int[] arrays, int currentRootNode, int size) { if (currentRootNode < size) { //左子树和右字数的位置 int left = 2 * currentRootNode + 1; int right = 2 * currentRootNode + 2; //把当前父节点位置看成是最大的 int max = currentRootNode; if (left < size) { //如果比当前根元素要大,记录它的位置 if (arrays[max] < arrays[left]) { max = left; } } if (right < size) { //如果比当前根元素要大,记录它的位置 if (arrays[max] < arrays[right]) { max = right; } } //如果最大的不是根元素位置,那么就交换 if (max != currentRootNode) { int temp = arrays[max]; arrays[max] = arrays[currentRootNode]; arrays[currentRootNode] = temp; //继续比较,直到完成一次建堆 heapify(arrays, max, size); } } } }希尔排序
思路:希尔排序实质上就是插入排序的增强版,希尔排序将数组分隔成n组来进行插入排序,直至该数组宏观上有序,最后再进行插入排序时就不用移动那么多次位置了~
代码思路:希尔增量一般是gap = gap / 2,只是比普通版插入排序多了这么一个for循环而已。
/** * 希尔排序 * * @param arrays */ public static void shellSort(int[] arrays) { //增量每次都/2 for (int step = arrays.length / 2; step > 0; step /= 2) { //从增量那组开始进行插入排序,直至完毕 for (int i = step; i < arrays.length; i++) { int j = i; int temp = arrays[j]; // j - step 就是代表与它同组隔壁的元素 while (j - step >= 0 && arrays[j - step] > temp) { arrays[j] = arrays[j - step]; j = j - step; } arrays[j] = temp; }基数排序(桶排序)
思路:基数排序(桶排序):将数字切割成个、十、百、千位放入到不同的桶子里,放一次就按桶子顺序回收一次,直至最大位数的数字放完~那么该数组就有序了
代码实现:先找到数组的最大值,然后根据最大值/10来作为循环的条件(只要>0,那么就说明还有位数)。将个位、十位、…分配到桶子上,每分配一次就回收一次
/** * 基数排序(桶排序) * @param arrays */ public static void radixSort(int[] arrays) { int max = findMax(arrays, 0, arrays.length - 1); //需要遍历的次数由数组最大值的位数来决定 for (int i = 1; max / i > 0; i = i * 10) { int[][] buckets = new int[arrays.length][10]; //获取每一位数字(个、十、百、千位...分配到桶子里) for (int j = 0; j < arrays.length; j++) { int num = (arrays[j] / i) % 10; //将其放入桶子里 buckets[j][num] = arrays[j]; } //回收桶子里的元素 int k = 0; //有10个桶子 for (int j = 0; j < 10; j++) { //对每个桶子里的元素进行回收 for (int l = 0; l < arrays.length; l++) { //如果桶子里面有元素就回收(数据初始化会为0) if (buckets[l][j] != 0) { arrays[k++] = buckets[l][ * 递归,找出数组最大的值 * * @param arrays 数组 * @param L 左边界,第一个数 * @param R 右边界,数组的长度 * @return */ public static int findMax(int[] arrays, int L, int R) { //如果该数组只有一个数,那么最大的就是该数组第一个值了 if (L == R) { return arrays[L]; } else { int a = arrays[L]; int b = findMax(arrays, L + 1, R);//找出整体的最大值 if (a > b) { return a; } else { return b; } } }排序算法有部分需要递归和树相关的基础知识,我都已经整理到PDF上了。
【Java开源】消息推送平台
我推荐一个拥有从零开始的文档的项目,既能用于毕设又可以在面试的时候大放异彩。
该项目业务极容易理解,代码结构还算是比较清晰,最可怕的是几乎每个方法和每个类都带有中文注释
拥有非常全的文档,作者从零搭建的过程一一都有记录,项目使用了蛮多的可靠和稳定的中间件的,包括并不限于SpringBoot、SpringDataJPA、MySQL、Docker、docker-compose、Kafka、Redis、Apollo、prometheus、Grafana、GrayLog、xxl-job等等。在使用每一个技术栈之前都讲述了为什么要使用,以及它的业务背景。我看过,他所说的场景是完全贴合线上环境的。
跟着README文档的部署使用姿势就能跑起来,一步一步debug挺有意思的,作者还搞了个前端后台管理系统就让整个系统变得更好理解了。并且在GitHub或者Gitee提的Issue几乎都会有回复,也非常乐于合并开发者们的pull request,会让人参与感贼强。
我相信在校、工作一年左右或常年做内网CRUD后台的同学去看看肯定会有所启发,作者会经常在群里回答该项目相关的问题和代码设计思路。
B站也在开始更新消息推送平台的视频哟!
目前这个项目GitHub和Gitee加起来已经6Kstars了,我相信破万是迟早的事情。 嗯,没错。这个项目叫做austin,是我写的
消息推送平台-Austin就是奔着真实互联网线上项目去设计和实现的,将项目克隆下来把中间件换成目前公司在用的,完善下基础建设它就能成为线上项目
austin项目核心功能:统一的接口发送各种类型消息,对消息生命周期全链路追踪
项目出现意义:只要公司内有发送消息的需求,都应该要有类似austin的项目,对各类消息进行统一发送处理。这有利于对功能的收拢,以及提高业务需求开发的效率
austin项目核心流程:austin-api接收到发送消息请求,直接将请求进MQ。austin-handler消费MQ消息后由各类消息的Handler进行发送处理
项目Gitee链接:
项目GitHub链接:
《对线面试官》Java面试八股文
《对线面试官》是我一字字敲出来的,是我准备跳槽时按照自己的思路撰写而成。
它不会花长的篇幅讲述基础API相关的知识,会着重讲解重点高频率面试题,又或是实际工作中如何使用这项技术的它。模拟真实的面试场景,面试者实际的回答内容可能是怎么样的。
,至少我认为它是有价值。等我下次跳槽,我绝对会回看我自己写的对线面试官系列。
为了增添趣味性会有图片版本(在其中穿插大量表情包或者梗),为了复习效率性会有纯文版本,纯文版本还有离线PDF电子书,离线PDF电子书有明亮版和暗黑版。
对线面试官 第一季 共40篇已结束
对线面试官 第二季 会有吗?会的
最近我把时间都花在写我的个人项目(消息推送平台),在项目上用到了很多的中间件。
这些中间件都值得拉出来做成面试题(事实上,当我作为面试官的时候也很喜欢问面试者在项目中相关的技术栈问题),我一直信奉着只要写在简历上的内容,肚子就得有墨水
《对线面试官 第一季》纯文版
简历【对线面试官】如何写简历Java基础【对线面试官】Java注解【对线面试官】Java泛型【对线面试官】 Java NIO【对线面试官】Java反射 && 动态代理Java并发【对线面试官】多线程基础【对线面试官】 CAS【对线面试官】synchronized【对线面试官】AQS&&ReentrantLock【对线面试官】线程池【对线面试官】ThreadLocal【对线面试官】CountDownLatch和CyclicBarrier【对线面试官】为什么需要Java内存模型?【对线面试官】深入浅出 Java 内存模型Java虚拟机【对线面试官】Java从编译到执行,发生了什么?【对线面试官】双亲委派机制【对线面试官】JVM内存结构【对线面试官】垃圾回收机制【对线面试官】CMS垃圾回收器【对线面试官】G1垃圾收集器【对线面试官】JVM调优Java *** 【对线面试官】List【对线面试官】MapSpring【对线面试官】SpringMVC【对线面试官】Spring基础【对线面试官】SpringBean生命周期Redis【对线面试官】Redis基础【对线面试官】Redis持久化【对线面试官】Redis主从架构【对线面试官】Redis分片集群消息队列【对线面试官】Kafka基础【对线面试官】使用Kafka会考虑什么问题?MySQL【对线面试官】MySQL索引【对线面试官】MySQL 事务&&锁机制&&MVCC【对线面试官】MySQL调优项目场景相关【对线面试官】如何实现幂等和去重?【对线面试官】系统需求多变时,如何设计【对线面试官】设计模式计算机网络【对线面试官】TCP【对线面试官】HTTP《对线面试官 第一季》图片版
简历【对线面试官】如何写简历Java基础【对线面试官】Java注解【对线面试官】Java泛型【对线面试官】 Java NIO【对线面试官】Java反射 && 动态代理Java并发【对线面试官】多线程基础【对线面试官】 CAS【对线面试官】synchronized【对线面试官】AQS&&ReentrantLock【对线面试官】线程池【对线面试官】ThreadLocal【对线面试官】CountDownLatch和CyclicBarrier【对线面试官】为什么需要Java内存模型?【对线面试官】深入浅出 Java 内存模型Java虚拟机【对线面试官】Java从编译到执行,发生了什么?【对线面试官】双亲委派机制【对线面试官】JVM内存结构【对线面试官】垃圾回收机制【对线面试官】CMS垃圾回收器【对线面试官】G1垃圾收集器【对线面试官】JVM调优Java *** 【对线面试官】List【对线面试官】MapSpring【对线面试官】SpringMVC【对线面试官】Spring基础【对线面试官】SpringBean生命周期Redis【对线面试官】Redis基础【对线面试官】Redis持久化【对线面试官】Redis主从架构【对线面试官】Redis分片集群消息队列【对线面试官】Kafka基础【对线面试官】使用Kafka会考虑什么问题?MySQL【对线面试官】MySQL索引【对线面试官】MySQL 事务&&锁机制&&MVCC【对线面试官】MySQL调优项目场景相关【对线面试官】如何实现幂等和去重?【对线面试官】系统需求多变时,如何设计【对线面试官】设计模式计算机网络【对线面试官】TCP【对线面试官】HTTP我的原创电子书
在自学之路上,我已经把【基础重要的知识点】、【简历模板】、【思维导图】【对线面试官】等等全部整理成电子书,共有1263页!已经有8756个初学者都下载了!
我把这些上传到网盘,你们有需要直接下载就好了。做到这份上了,不会还想白嫖吧?点赞和转发又不用钱。
链接:pan.baidu.com/s/1pQTuKBYs… 密码:3wom