常见算法—排序算法
1. 冒泡排序
给定一个数组,按要求对其进行排序。
冒泡排序就是相邻元素两两比较,如使从小到大排序,小的放前面,大的放后面,每轮得到一个最大值。
是从数组最右侧向前开始逐渐建立了顺序。
假使其从小到大进行排列:
- 从第一个元素开始,相邻元素两两进行比较,小的放左边,大的放右边;
- 第一轮比较完毕后,数组 最大值就被置于数组的最右边,即最大索引处;
- 每一轮比较完毕,就会出现一个小于上一轮最大值的元素置于其左侧,即索引-1处;
- 每一轮比较可以比上一轮比较少一次比大小的循环;
- 有n个数据,执行n-1轮循环即可。
举例:对数组[2, 4, 5, 3, 1]进行由大到小的排序,并输出结果。
代码实现:
int[] arr = {2, 4, 5, 3, 1}; for (int i = 0; i < arr.length - 1; i++) { for (int j = 0; j < arr.length - 1 - i; j++) { if(arr[j] > arr[j + 1]){ int temp = arr[j + 1]; arr[j + 1] = arr[j]; arr[j] = temp; } } } for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); }
2. 选择排序
从0索引开始,拿着每一个索引的元素依次与后面的元素进行比较,若是由小到大排序,小的放前面,大的放后面,依此类推。第一轮循环就得到了数组中最小的元素。
是从最左侧向后开始逐渐建立了顺序。
借助冒泡排序的方式就可以很好理解选择排序,下面是代码实现(由小到大排序):
int[] arr = {2, 4, 5, 3, 1};
for (int i = 0; i < arr.length - 1; i++) {
for (int j = i; j < arr.length; j++) {
if(arr[i] > arr[j]){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
3. 插入排序
插入排序就是先找出有序部分,将其定为A部分,无序部分定为B部分;
从B部分的第一个元素开始,与A部分里面的数据进行比较,小者放右边,大者放右边,由此就将B组中的一个元素插入到了A部分中,成为了有顺序的一部分;
将B组的每一个元素都用这种方法插入,就完成了所有数据的排序。
下面是代码实现:
int[] arr = {2, 4, 5, 3, 1, 44, 23, 51, 6, 8};
//找到无顺序部分的起始索引
int startIndex = -1;
for (int i = 0; i < arr.length - 1; i++) {
if (arr[i] > arr[i + 1]) {
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] + " ");
}
4. 快速排序
4.1 递归算法
在学习快速排序之前,先对递归算法做一个理解。
作用:
把一个复杂的问题层层转化为一个与原问题相似的规模较小的问题进行求解;
递归策略只需少量程序就可以描述出解题过程所需的多次运算过程。
书写递归的两个核心:
**找出口:**什么时候不再调用方法。
**找规律:**如何把大问题变成规模较小的问题。
如利用递归算法求取100内所有整数之和,下面是代码实现:
public static void main(String[] args) {
System.out.println(getSum(100)); //调用方法
}
//递归算法:
public static int getSum(int num) {
if(num == 1){
return 1;
}
return num + getSum(num - 1);
}
4.2 快速排序
进入正题,来说一下快速排序。
- 将排序范围中的第一个数字作为基准数,再定义两个变量start和end;
- start从前往后寻找比基数大的元素,end从前往后寻找比基数小的元素;
- 在找到后交换start和end指向的元素,并循环该过程,至start和end在同一个位置,该位置就是基准位;
- 使基准数归位;
- 之后开始递归,基准数左边组和右边组分别执行前面的代码,递归出口为start < end时(当排序完成后,再次运行代码,start和end会处于同一位置,之后再进行递归时,方法参数中end-1,会造成下一次start > end,故以此作为递归出口)。
以下为代码实现:
int[] arr = {6, 1, 2, 7, 9, 16, 4, 5, 10, 8};
// 5, 7,
// 4, 9,
quickSort(arr, 0, arr.length - 1);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
//快速排序算法:
private static void quickSort(int[] arr, int i, int j) {
int start = i;
int end = j;
//递归出口
if(start > end){
return;
}
//记录基准数
int baseNum = arr[i];
//找到基准位
while (start != end) {
while (true) {
if (end <= start || arr[end] < baseNum) {
break;
}
end--;
}
while (true) {
if (end <= start || arr[start] > baseNum) {
break;
}
start++;
}
int temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
}
//基准数归位
int temp = arr[i];
System.out.println(arr[i]);
System.out.println(temp);
arr[i] = arr[start];
arr[start] = temp;
//递归
quickSort(arr, i, start - 1);
quickSort(arr, start + 1, j);
}
}
细节:
很重要的细节!!!
在书写快速排序算法代码时,对start和end谁先移动会不会影响结果,为什么会影响结果思考了相当长的时间。
答案是会影响,应该先移动end。
如本题为:由小到大进行排序。
当end开始移动时,说明前面已经移动了0~n对start和end,它们已经完成了成对移动,此时start指向的元素就是比基准数小的元素(在基准数归位时,保证了与基准数互换位置的元素比基准数小,保证了小数在右边的规定),之后end开始移动,与start到达同一位置,从而跳出循环,基准数与该位置的元素进行互换,完成基准数的归位。