JAVA数据结构 08.排序算法(1)
1.排序算法的介绍和分类
排序:将一组数据,以指定的顺序进行排列的过程
排序的分类:
- 内部排序:将需要的所有数据加载到内存中进行排序**(8大排序算法)**
- 插入排序
- 直接插入排序
- 希尔排序
- 选择排序
- 简单选择排序
- 堆排序
- 交换排序
- 冒泡排序
- 快速排序
- 归并排序
- 基数排序
- 插入排序
- 外部排序:数据量过大,无法全部加载到内存中,需要借助外存进行排序,先部分排序后合并
度量一个排序算法:
- 算法的时间复杂度
- 算法的空间复杂度
2. 八大排序算法 实现 解析 和优化
2.1 冒泡排序
基本思想:通过对待排序序列从前向后,依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后面.
简单方法(待优化):不考虑其他条件,直接对数组内所有元素进行两层for循环实现
public static void stupid(int arr[]){
for (int i = 0; i <arr.length ; i++) {
for (int j = 0; j <arr.length -1 ; j++) {
if(arr[j]>arr[j+1]){
//swap
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
算法图解:
冒泡规律小结: 使用规律来优化
- 对所有元素的排序只需要进行数组长度-1轮
- 每一轮的排序,元素比较和交换的次数减少
- 因为排序的过程中,各元素不断接近自己的位置,如果一趟比较下来没有进行过交换,就说明序列有序,因此要在排序过程中设置一个标志flag判断元素是否进行过交换。从而减少不必要的比较
算法优化:
public static void smart(int arr[]){
for (int i = 0; i <arr.length -1 ; i++) {
//优化1:一共进行数组的大小-1次大的循环
boolean flag = true; //优化3:设置一个表示flag判断元素是否进行交换,从而减少不必要的比较
for (int j = 0; j <arr.length -1 - i ; j++) {
//优化2:每一趟排序的次数在逐渐的减少i
if(arr[j]>arr[j+1]){
//swap
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
flag = false;
}
}
if(flag) break;
}
}
2.2 选择排序
算法思路:从欲排序的数据中,按指定的规则选出某一元素,再依次规定交换位置后达到排序的目的.
- 以从小到大排序为例,将n个待排序的元素看称一个有序表和一个无序表
- 每一轮从无序表元素中找到最小的数字,与无序表的第一个元素进行交换.(无序表场地-1,有序表长度+1)
- 总共通过n-1次,得到一个有序序列
算法图解:
- 选择排序进行数组大小-1轮排序
- 每一轮的排序,又是一个循环,找到剩下待排序数中最小的数执行交换,与冒泡相比减少了交换次数
算法实现和优化:
public static void selectSort(int arr[]){
for (int i = 0; i <arr.length - 1 ; i++) {
int index = i;
for (int j = i+1 ; j <arr.length ; j++) {
//优化1. j从i+1开始进行比较,减少依次 j和i的对比(自我比较)
//找arr[j]~arr[length]中最小的数的下标
if(arr[index]>arr[j]){
index = j;
}
}
if(index != i){
//优化2.当最小值是arr[i]本身时 无需交换
//交换最小数和i的位置
int temp = arr[index];
arr[index] = arr[i];
arr[i] = temp;
}
}
}
2.3 插入排序
算法思路:对欲排序的元素以插入的方式找寻该元素的适当位置,以达到排序的目的.
- 将n个待排序的元素看称一个有序表和一个无序表
- 开始时有序表中只包含一个元素,无序表中包含n-1个元素
- 排序过程中每次从无序表中取出第一个元素,将它依次与有序表进行比较
- 找到合适顺序位置后插入操作(数组的插入需要整体移动)
算法图解:
算法实现:
public static void inserSort(int arr[]){
for (int i = 1; i <arr.length ; i++) {
//待插入的数字
int insertVal = arr[i];
//已经排好序的数组中最大的一个元素
int insertIndex = i-1;
//判断:待排序数是否大于插入位置,找到插入点
while (insertIndex >=0 && insertVal < arr[insertIndex]){
//后移arr[insertIndex]
arr[insertIndex+1] = arr[insertIndex];
insertIndex --; //判断有序表前一个元素
}
//循环结束以后,已经找到待插入的位置为insertIndex+1
arr[insertIndex +1 ] = insertVal;
}
}
插入排序存在的明显问题:当需要插入较小的数时,需要将多位有序表中的数进行后移,后移次数增多,对效率影响大(优化方法:希尔排序)
2.4 希尔排序
算法介绍:希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效版本,也称为缩小增量排序
算法思想:
- 希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;
- 随着随着增量逐渐减少,每组包含的元素越来越多,当增量减到1时,整个数组恰被分成一组,算法终止
算法图解:
代码实现:
交换式希尔排序实现: 这是对冒泡排序的升级,先进行分组冒泡,再进行合并冒泡,减少元素之间的交换次数,但需要多轮冒泡,速度很慢
public static void sheelSortSwap(int arr[]){
int count = 0;
int temp = 0;
for(int gap = arr.length/2;gap>0;gap /=2){
//分组内可用理解为一个冒泡排序,每次将组内最小的数换到第一个,实现组内有序
for (int i = gap; i <arr.length ; i++) {
for (int j = i-gap; j >=0 ; j-=gap) {
if(arr[j]>arr[j+gap]){
temp = arr[j];
arr[j] = arr[j+gap];
arr[j+gap] = temp;
count ++;
}
}
}
}
System.out.println(count);
}
移动式希尔排序:这是对插入排序的升级,先进行分组插入,再进行合并插入,减少元素之间的移动
public static void sheelSortMove(int arr[]) {
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
//从gap个元素开始,逐个对其所在的组进行直接插入排序
//元素间隔为gap的插入排序
for (int i = gap; i < arr.length; i++) {
int insertIndex = i;
int insertValue = arr[insertIndex];
while (insertIndex - gap >= 0 && insertValue < arr[insertIndex - gap]) {
arr[insertIndex] = arr[insertIndex - gap];
insertIndex -= gap;
}
//此时找到了temp的位置
arr[insertIndex] = insertValue;
}
}
}
rr[insertIndex] = arr[insertIndex - gap];
insertIndex -= gap;
}
//此时找到了temp的位置
arr[insertIndex] = insertValue;
}
}
}