八大排序算法
目录
注意:以下排序均属于内部排序
(1)插入排序 (2)交换排序 (3)选择排序 (4)归并排序
排序总结分析表
一、插入排序
1. 直接插入排序
(1)整体思路
通过动图可以形象的理解到
1. 抽出一张牌,和当前元素之前的元素逐个比较,如果比当前元素小,该元素就后移,直到找到插入位置
说明:(惯用思想)初始时视第一个元素是有序的 ,之后通过排序逐渐增大有序序列的长度
(2)代码实现
第一种写法:while 循环 (更规范 )
public static void insert_sort ( int [ ] arr) { for ( int i = 1 ; i < arr. length; i++ ) { int insertvalue = arr[ i] ; int j = i - 1 ; while ( j >= 0 && insertvalue < arr[ j] ) { arr[ j + 1 ] = arr[ j] ; j-- ; } arr[ j + 1 ] = insertvalue; }
}
第二种写法:使用for 循环
public static void insert_sort_1 ( int [ ] arr) { for ( int i = 1 ; i < arr. length; i++ ) { int insertvalue = arr[ i] ; int j = i - 1 ; for ( ; j >= 0 ; j-- ) { if ( insertvalue < arr[ j] ) { arr[ j + 1 ] = arr[ j] ; } else { break ; } } arr[ j + 1 ] = insertvalue; }
}
2. 折半插入排序
改进点:使用折半查找提高效率,使用循环遍历逐个匹配的效率太差
public static void binary_insert_sort ( int [ ] arr) { for ( int i = 1 ; i < arr. length; i++ ) { int insertvalue = arr[ i] ; int left = 0 ; int right = i - 1 ; while ( left <= right) { int mid = left + ( ( right - left) / 2 ) ; if ( arr[ mid] < insertvalue) { left = mid + 1 ; } else { right = mid - 1 ; } } for ( int j = i - 1 ; j >= right + 1 ; j-- ) { arr[ j + 1 ] = arr[ j] ; } arr[ right + 1 ] = insertvalue; }
}
3. 希尔排序
动图演示
使用间隔 gap,gap 逐渐递减,最后 gap 的值必须是一 ,每一轮排序对 gap 产生的序列进行排序
快速写希尔排序:把直接插入排序中 “ 1 ” 的位置全部替换 成 “ gap ”
public static void shell_sort ( int [ ] arr) { int gap = arr. length / 2 ; while ( gap >= 1 ) { shell ( arr, gap) ; gap /= 2 ; }
}
public static void shell ( int [ ] arr, int gap) { for ( int i = gap; i < arr. length; i++ ) { int insertvalue = arr[ i] ; int j = i - gap; while ( j >= 0 && insertvalue < arr[ j] ) { arr[ j + gap] = arr[ j] ; j -= gap; } arr[ j + gap] = insertvalue; }
}
二、交换排序
1. 冒泡排序
动图演示
(1)普通版本
public static void bubble_sort ( int [ ] arr) { 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] ; arr[ j] = arr[ j + 1 ] ; arr[ j + 1 ] = temp; } } }
}
(2)改进版本
亮点:通过变量标记的方式标记是否执行了交换操作,可以一定程度上减少比较次数
public static void bubble_sort_improve ( int [ ] arr) { for ( int i = 0 ; i < arr. length - 1 ; i++ ) { int flag = 0 ; for ( int j = 0 ; j < arr. length - 1 - i; j++ ) { if ( arr[ j] > arr[ j + 1 ] ) { flag = 1 ; int temp = arr[ j] ; arr[ j] = arr[ j + 1 ] ; arr[ j + 1 ] = temp; } } if ( flag == 0 ) { break ; } }
}
2. 快速排序
动图演示
主要思想:递归,双指针(对撞指针)
思路分析
把第一个元素当作枢纽,然后通过两个指针 left 和 right right : 在后面 找比枢纽小 的元素搬到 枢纽的前面
public static int partition ( int [ ] arr, int left, int right) { int pivot = arr[ left] ; while ( left < right) { while ( arr[ right] >= pivot && left < right) { right-- ; } arr[ left] = arr[ right] ; while ( arr[ left] <= pivot && left < right) { left++ ; } arr[ right] = arr[ left] ; } arr[ left] = pivot; return left;
} public static void quicksort ( int [ ] arr, int left, int right) { if ( left < right) { int pivot = partition ( arr, left, right) ; quicksort ( arr, left, pivot - 1 ) ; quicksort ( arr, pivot + 1 , right) ; }
}
三、选择排序
1. 简单选择排序
动图演示
基本思路:选一个数,在这个数的后面找有没有比本身还小的,有就交换位置
优化点:在后面找一个最小的数 ,避免重复的覆盖,减少比较次数
区别冒泡排序 的优化
冒泡排序 中是比较相邻两个元素之间 是否有序,如果有序就不交换位置
(1)普通版本
public static void select_sort ( int [ ] arr) { for ( int i = 0 ; i < arr. length; i++ ) { for ( int j = i + 1 ; j < arr. length; j++ ) { if ( arr[ j] < arr[ i] ) { int temp = arr[ j] ; arr[ j] = arr[ i] ; arr[ i] = temp; } } }
}
(2)改进版本
public static void select_sort_improve ( int [ ] arr) { for ( int i = 0 ; i < arr. length; i++ ) { int min_index = i; for ( int j = i + 1 ; j < arr. length; j++ ) { if ( arr[ j] < arr[ min_index] ) { min_index = j; } } if ( min_index != i) { int temp = arr[ i] ; arr[ i] = arr[ min_index] ; arr[ min_index] = temp; } }
}
2. 堆排序(二叉堆)
(1)基本介绍
堆的性质是符合完全二叉树的,在结构上是递归定义的树结构 1. 若一个非叶子节点节点 的物理位置为 “ i ” 2. 若一个数组的元素可以构成一棵树,数组大小为 n (1)最后一个元素(叶子节点 )在树中的标号对应的数组下标是n / 2
补充:关于树中节点的序号和数组下标的关系
方法:从左到右,从上到下依次标号
图例(对应数组下标 )
0 / \1 2 / \ / \3 4 5 6
分类 大根堆 :对于完全二叉树中的任意一个节点,其值都大于或等于其子树 中所有节点的值。这意味着大根堆的根节点是堆中最大的元素
大根堆示例
10 / \8 6 / \ / \5 3 2
小根堆示例
1 / \3 4 / \ / \6 8 9
(2)堆排序思想
1. 构建一个大根堆 从后面的=第一个非叶子节点 (下标:n / 2 - 1 )开始,依次递归的往上调整,使得整棵树形成大根堆的结构 交换思想:交换的过程就是排序的过程,把最大的和最小的交换,即把最大的放入有序区,数组从后往前依次递减排序 理解:在调整过程中,指向的最大根节点会发生变化,对根节点调整即可实现对左右子树的调整 ) 关于n - 1 的说明 (1)进行n - 1 趟排序 (3)循环过程中,刚好对应堆的大小的变化 ,每一轮排序一个元素(交换的过程),堆中需要调整的元素总数减小一
堆排序代码
public static void heapify ( int [ ] arr, int n, int i) { int max_index = i; int left = 2 * i + 1 ; int right = 2 * i + 2 ; if ( left < n && arr[ left] > arr[ max_index] ) { max_index = left; } if ( right < n && arr[ right] > arr[ max_index] ) { max_index = right; } if ( max_index != i) { int temp = arr[ i] ; arr[ i] = arr[ max_index] ; arr[ max_index] = temp; heapify ( arr, n, max_index) ; }
}
public static void heap_sort ( int [ ] arr) { int n = arr. length; for ( int i = n / 2 - 1 ; i >= 0 ; i-- ) { heapify ( arr, n, i) ; } for ( int i = n - 1 ; i > 0 ; i-- ) { int temp = arr[ 0 ] ; arr[ 0 ] = arr[ i] ; arr[ i] = temp; heapify ( arr, i, 0 ) ; }
}
四、归并排序
动图演示
思路:运用合并为有序序列的思想、递归思想,两个有序序列通过比较的方式合并成一个更长的有序序列,而比较的过程正好是排序的过程
小结:排序左区间,排序右区间,两个区间合并,然而排序的过程又是两个区间合并的过程,即采用递归思想
归并排序代码
五、基数排序