排序算法:冒泡排序、选择排序、插入排序、快速排序、希尔排序、堆排序、桶排序、归并排序、计数排序、基数排序
1.冒泡排序
排序步骤:
- 相邻的元素两两比较,大的放右边,小的放左边
- 第一轮循环结束,最大值已经找到,在数组的最右边
- 第二轮循环只需要在剩余的元素中找最大值就可以了
- 第二轮循环结束,次大值已经找到,第三轮循环继续在剩余元素中找最大值,以此类推
冒泡排序核心:
- 相邻的元素两两比较,大的放右边,小的放左边
- 第一轮比较完毕之后,最大值就已经确定,第二轮可以少循环一次,后面以此类推
- 如果数组中有n个数据,总共我们只需要执行n-1轮的代码就可以
public class BubbleDemo {
public static void main(String[] args) {
//冒泡排序
//定义数组
int[] arr = {2,4,3,5,1};
//利用冒泡排序将数组变成1,2,3,4,5
//第一轮:结束之后,最大值在最右边 . . . . 5
for (int i = 0; i < arr.length - 1; i++) {
if(arr[i] > arr[i+1]){
int temp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = temp;
}
}
printArr(arr);//2 3 4 1 5
//第二轮:结束之后 . . . 4 5
for (int i = 0; i < arr.length - 1 - 1; i++) {
if(arr[i] > arr[i+1]){
int temp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = temp;
}
}
printArr(arr);//2 3 1 4 5
//第三轮:结束之后 . . 3 4 5
for (int i = 0; i < arr.length - 1 - 1 - 1; i++) {
if(arr[i] > arr[i+1]){
int temp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = temp;
}
}
printArr(arr);//2 1 3 4 5
//第四轮:结束之后 . 2 3 4 5
for (int i = 0; i < arr.length - 1 - 1 - 1 - 1; i++) {
if(arr[i] > arr[i+1]){
int temp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = temp;
}
}
printArr(arr);//1 2 3 4 5
}
public static void printArr(int[] arr){
//遍历数组
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
}
public class BubbleDemo1 {
public static void main(String[] args) {
//冒泡排序
int[] arr = {2,4,5,3,1};
//外循环:表示轮数,要执行多少轮 如果n个数据,执行n-1轮
for (int i = 0; i < arr.length - 1; i++) {
//内循环:每一轮的数据挨个比较,找到当前轮的最大值
//-1:为了防止索引越界
//-i:每一轮执行的次数比上一轮少一次
for (int j = 0; j < arr.length - 1 - i; j++) {
if(arr[j] > arr[j+1]){
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
}
2.选择排序
排序步骤:
- 从0索引开始,用0索引的元素跟后面的元素一一比较
- 小的放前面,大的放后面
- 第一次循环结束后,最小的数据已经确定
- 第二次循环从1索引开始以此类推
有数组{2,4,5,3,1}
第一次循环后结果:{1,4,5,3,2}
第二次循环后结果:{1,2,5,4,3}
第三次循环后结果:{1,2,3,5,4}
第四次循环后结果:{1,2,3,4,5}
public class SelectionDemo {
public static void main(String[] args) {
//选择排序
int[] arr = {2,4,5,3,1};
//外循环:轮数
//i:表示这一轮中,我要拿着哪个索引上的数据跟后面的数据进行比较并交换
for (int i = 0; i < arr.length - 1; i++) {
//内循环:表示每一轮要干的事情
//拿着i跟i后面的每一个元素一一进行比较交换
for (int j = i + 1; j < arr.length ; j++) {
if(arr[j] < arr[i]){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
}
3.插入排序
核心思想:
将0索引的元素到n索引的元素看成是有序的,把n+1索引的元素到最后一个索引的元素看成是无序的。依次遍历无序的数据,将遍历到的元素依次插入有序序列中合适的位置,如果遇到相同的数据,插在后面。n的范围:0~最大索引
public class InsertDemo {
public static void main(String[] args) {
//插入排序
int[] arr = {3,44,38,5,47,15,5,36,26,27,2,46,4,19,50,29};
//定义变量表示无序序列的起始索引
int startIndex = 0;
//循环找到无序索引的起始位置
for (int i = 0; i < arr.length; i++) {
if(arr[i] > arr[i+1]){
//System.out.println(i);
startIndex = i + 1;
break;
}
}
//循环遍历无序序列,依次得到无序序列中的每一个元素
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--;
}
}
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
}
4.递归算法
递归指的是方法中调用方法本身的现象
递归的注意点:递归一定要有出口,否则就会出现内存溢出
递归算法的作用:
*把一个复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解
*递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算
书写递归的两个核心:
找出口:方法什么时候不再调用自己
找规则:如何把大问题变成规模较小的问题
public class RecursionDemo1 {
public static void main(String[] args) {
//递归算法
//利用递归求解1~100之间的和
//100 + 99 + 98 + ... + 2 + 1
//大问题拆解成小问题
//1~100之间的和=100 + (1~99之间的和)
//1~99之间的和=99 + (1~98之间的和)
//1~98之间的和=98 + (1~97之间的和)
//...
//1~2之间的和=2 + (1~1之间的和)
//1~1之间的和=1 (递归出口)
int result = getSum(100);
System.out.println(result);
}
public static int getSum(int number){
if(number == 1){
return 1;
}
return number + getSum(number - 1);
}
}
public class RecursionDemo2 {
public static void main(String[] args) {
//利用递归求5的阶乘
System.out.println(getJC(5));
}
public static int getJC(int number){
if(number == 1){
return 1;
}
return number * getJC(number - 1);
}
}
5.快速排序
算法步骤:
- 1. 从数列中挑出一个元素,一般都是左边第一个数字,称为 "基准数";
- 2. 创建两个指针,一个从前往后走,另一个从后往前走。
- 3. 先执行后面的指针,找出第一个比基准数小的数字
- 4. 再执行前面的指针,找出第一个比基准数大的数字
- 5. 交换两个指针指向的数字
- 6. 直到两个指针相遇
- 7. 将基准数跟指针指向位置的数字交换位置,称之为:基准数归位。
- 8. 第一轮结束之后,基准数左边的数字都是比基准数小的,基准数右边的数字都是比基准数大的。
- 9. 把基准数左边看做一个序列,把基准数右边看做一个序列,按照刚刚的规则递归排序
注意点:必须先执行后面的指针再执行前面的指针,二者顺序不能颠倒
public class QuickSortDemo {
public static void main(String[] args) {
/*快速排序:
* 第一轮:以0索引的数字为基准数,确定基准数在数组中正确的位置。
* 比基准数小的全部在左边,比基准数大的全部在右边。
* 后面以此类推。*/
int[] arr = {6,1,2,7,9,4,3,5,10,8};
quickSort(arr,0,arr.length - 1);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
/*
* 参数一:我们要排序的数组
* 参数二:要排序数组的起始索引
* 参数三:要排序数组的结束索引
* */
public static void quickSort(int[] arr,int i,int j){
//定义两个变量记录要查找的范围
int start = i + 1;
int end = j;
if(start > end){
//递归的出口
return;
}
//记录基准数
int baseNumber = arr[i];
//利用循环找到要交换的数字
while(start != end){
//利用end,从后往前开始找,找比基准数小的数字
while(true){
if(end <= start || arr[end] < baseNumber){
break;
}
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;
//确定基准数左边的范围,重复刚刚所做的事情
quickSort(arr,i,start - 1);
//确定基准数右边的范围,重复刚刚所做的事情
quickSort(arr,start + 1,j);
}
}
public static void main(String[] args) {
int[] arr = new int[1000000];
Random r = new Random();
for (int i = 0; i < arr.length; i++) {
arr[i] = r.nextInt();
}
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));
//练习:
//我们可以利用相同的办法去测试一下,选择排序,冒泡排序以及插入排序运行的效率
//得到一个结论:快速排序真的非常快。
}
总结:
冒泡排序 | 相邻的元素两两比较,小的放前面,大的放后面 |
选择排序 | 从0索引开始,拿着每一个索引上的元素跟后面的元素依次比较,小的放前面,大的放后面,以此类推 |
插入排序 | 将数组分为有序和无序两组,遍历无序数组,将元素依次插入有序数组中即可 |
快速排序 | *将排序范围中的第一个数字作为基准数,再定义两个变量start,end *end从后往前找比基准数小的,start从前往后找比基准数大的 *找到之后交换start和end指向的元素,并循环这一过程,直到start和end处于同一位置,该位置是基准数在数组中应存入的位置,再让基准数归位 *归为后的效果:基准数左边的都比基准数小,基准数右边的都比基准数大 |