1. 冒泡排序
冒泡排序是一种简单直观的排序算法,相邻的数据两两比较,小的放前面,大的放后面
它重复的遍历过要排序的数列,一次比较相邻的两个元素,如果他们的顺序错误就把他们交换过来。这个算法的名字由来是因为越大的元素会经由交换慢慢"浮"到最后面,当然,也可以按照从大到小的方式进行排列。
算法步骤:
-
相邻的元素两两比较,大的放右边,小的放左边
-
第一轮比较完毕之后,最大值就已经确定,第二轮可以少循环一次,后面以此类推
-
如果数组中有n个数据,总共我们只要执行n-1轮的代码就可以
动图演示:
public class A01_BubbleDemo { public static void main(String[] args) { //1.定义数组 int[] arr = {2, 4, 5, 3, 1}; //2.利用冒泡排序将数组中的数据变成 1 2 3 4 5 //外循环:表示要执行多少轮。 如果有n个数据,那么执行n - 1 轮 for (int i = 0; i < arr.length - 1; i++) { //内循环:每一轮中我如何比较数据并找到当前的最大值 //-1:为了防止索引越界 //-i:提高效率,每一轮执行的次数应该比上一轮少一次。 for (int j = 0; j < arr.length - 1 - i; j++) { //i 依次表示数组中的每一个索引:0 1 2 3 4 if(arr[j] > arr[j + 1]){ int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } printArr(arr); } private static void printArr(int[] arr) { //3.遍历数组 for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); } System.out.println(); } }
2. 选择排序
从0索引开始,拿着每一个索引上的元素跟后面的元素依次比较,小的放前面,大的放后面,以此类推。
算法步骤:
-
从0索引开始,跟后面的元素一一比较
-
小的放前面,大的放后面
-
第一次循环结束后,最小的数据已经确定
-
第二次循环从1索引开始以此类推
-
第三轮循环从2索引开始以此类推
-
第四轮循环从3索引开始以此类推。
动图演示:
public class A02_SelectionDemo { public static void main(String[] args) { //1.定义数组 int[] arr = {2, 4, 5, 3, 1}; //2.利用选择排序让数组变成 1 2 3 4 5 //第一轮:从0索引开始,跟后面的元素一一比较。 for (int i = 0 + 1; i < arr.length; i++) { //拿着0索引跟后面的数据进行比较 if(arr[0] > arr[i]){ int temp = arr[0]; arr[0] = arr[i]; arr[i] = temp; } } //最终代码: //外循环:几轮 //i:表示这一轮中,我拿着哪个索引上的数据跟后面的数据进行比较并交换 for (int i = 0; i < arr.length -1; i++) { //内循环:每一轮我要干什么事情? //拿着i跟i后面的数据进行比较交换 for (int j = i + 1; j < arr.length; j++) { if(arr[i] > arr[j]){ int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } printArr(arr); } private static void printArr(int[] arr) { //3.遍历数组 for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); } System.out.println(); } }
3. 插入排序
插入排序的代码实现虽然没有冒泡排序和选择排序那么简单粗暴,但它的原理应该是最容易理解的了。
插入排序是一种最简单直观的排序算法,它的工作原理是通过创建有序序列和无序序列,然后再遍历无序序列得到里面每一个数字,把每一个数字插入到有序序列中正确的位置。
插入排序在插入的时候,有优化算法,在遍历有序序列找正确位置时,可以采取二分查找
算法步骤:
-
将0索引的元素到N索引的元素看做是有序的,把N+1索引的元素到最后一个当成是无序的。
-
遍历无序的数据,将遍历到的元素插入有序序列中适当的位置,如遇到相同数据,插在后面。
-
N的范围:0~最大索引
动图演示:
package com.itheima.mysort; public class A03_InsertDemo { public static void main(String[] args) { int[] arr = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48}; //1.找到无序的哪一组数组是从哪个索引开始的。 2 int startIndex = -1; for (int i = 0; i < arr.length; i++) { if(arr[i] > arr[i + 1]){ startIndex = i + 1; break; } } //2.遍历从startIndex开始到最后一个元素,依次得到无序的那一组数据中的每一个元素 for (int i = startIndex; i < arr.length; i++) { //问题:如何把遍历到的数据,插入到前面有序的这一组当中 //记录当前要插入数据的索引 int j = i; while(j > 0 && arr[j] < arr[j - 1]){ //交换位置 int temp = arr[j]; arr[j] = arr[j - 1]; arr[j - 1] = temp; j--; } } printArr(arr); } private static void printArr(int[] arr) { //3.遍历数组 for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); } System.out.println(); } }
递归算法
递归算法:递归指的是方法中调用方法本身的现象,递归一定要有出口,否则就会出现内存溢出。
递归算法的作用
把一个复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解
递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算
书写递归的两个核心
-
找出口:什么时候不再调用方法
-
找规则:如何把大问题变成规模较小的问题
方法内部再次调用方法的时候,参数必须要更加靠近出口
4. 快速排序
算法步骤:
-
从数列中挑出一个元素,一般都是左边第一个数字,称为 "基准数";
-
创建两个指针,一个从前往后走,一个从后往前走。
-
先执行后面的指针,找出第一个比基准数小的数字
-
再执行前面的指针,找出第一个比基准数大的数字
-
交换两个指针指向的数字
-
直到两个指针相遇
-
将基准数跟指针指向位置的数字交换位置,称之为:基准数归位。
-
第一轮结束之后,基准数左边的数字都是比基准数小的,基准数右边的数字都是比基准数大的。
-
把基准数左边看做一个序列,把基准数右边看做一个序列,按照刚刚的规则递归排序
动图演示
package com.itheima.mysort; import java.util.Arrays; public class A05_QuickSortDemo { public static void main(String[] args) { System.out.println(Integer.MAX_VALUE); System.out.println(Integer.MIN_VALUE); int[] arr = {1,1, 6, 2, 7, 9, 3, 4, 5, 1,10, 8}; long start = System.currentTimeMillis(); quickSort(arr, 0, arr.length - 1); long end = System.currentTimeMillis(); System.out.println(end - start); System.out.println(Arrays.toString(arr)); } /* * 参数一:我们要排序的数组 * 参数二:要排序数组的起始索引 * 参数三:要排序数组的结束索引 * */ public static void quickSort(int[] arr, int i, int j) { //定义两个变量记录要查找的范围 int start = i; int end = j; if(start > end){ //递归的出口 return; } //记录基准数 int baseNumber = arr[i]; //利用循环找到要交换的数字 while(start != end){ //利用end,从后往前开始找,找比基准数小的数字 //int[] arr = {1, 6, 2, 7, 9, 3, 4, 5, 10, 8}; while(true){ if(end <= start || arr[end] < baseNumber){ break; } end--; } System.out.println(end); //利用start,从前往后找,找比基准数大的数字 while(true){ if(end <= start || arr[start] > baseNumber){ break; } start++; } //把end和start指向的元素进行交换 int temp = arr[start]; arr[start] = arr[end]; arr[end] = temp; } //当start和end指向了同一个元素的时候,那么上面的循环就会结束 //表示已经找到了基准数在数组中应存入的位置 //基准数归位 //就是拿着这个范围中的第一个数字,跟start指向的元素进行交换 int temp = arr[i]; arr[i] = arr[start]; arr[start] = temp; //确定6左边的范围,重复刚刚所做的事情 quickSort(arr,i,start - 1); //确定6右边的范围,重复刚刚所做的事情 quickSort(arr,start + 1,j); } }
总结
冒泡排序:相邻的元素两两比较,小的放前面,大的放后面
选择排序:从0索引开始,拿着每一个索引上的元素跟后面的元素依次比较,小的放前面,大的放后面,以此类推
插入排序:将数组分为有序和无序两组,遍历无序数据,将元素插入有序序列中即可
快速排序:
-
将排序范围中的第一个数字作为基准数,再定义两个变量start、end
-
start从前往后找比基准数大的,end从后往前找比基准数小的
-
找到之后交换start和end指向的元素,并循环这一过程,直到start和end处于同一个位置,该位置是基准数在数组中应存入的位置,再让基准数归位。
-
归位后的效果:基准数左边的,比基准数小,基准数右边的,比基准数大