一、插入排序
直接插入排序-原理:
整个区间被分为
- 有序区间
- 无序区间
每次选择无序区间的第一个元素,在有序区间内选择合适的位置插入
具体实现的代码如下:
import java.util.Arrays;
public class SortDemo {
public static void insertSort(int[] array){
//[0,bound)为拍好序的区间,[bound,size)为未排序的区间
for (int bound = 1;bound < array.length;bound++){
int tmp = array[bound];//下标为bound的元素为要插入的值,用tmp保存
int cur = bound - 1;
//对以排序号的区间从后往前遍历,为了让tmp也就是要插入的值找到合适的位置插入
for(;cur >= 0;cur--){
if(array[cur] > tmp){
//当前位置如果大于要插入的值,则需要将当前值向后搬运
array[cur + 1] = array[cur];//搬运
}else {
//tmp找到合适的位置了
break;
}
}
//下标为cur 的值<=tmp,所以cur+1为合适位置
array[cur + 1] = tmp;
}
}
public static void main(String[] args) {
//验证程序
int[] array = {19,5,2,7,1,20,47,12,45};
insertSort(array);
System.out.println(Arrays.toString(array));
}
}
[1, 2, 5, 7, 12, 19, 20, 45, 47]
排序是正确的
性能分析
时间复杂度 |
---|
最好:O(N) – 数组有序 |
平均:O(N^2) |
最坏:O(N^2) --数组逆序 |
- 稳定性:稳定
- 插入排序,初始数据越接近有序,时间效率越高。
- 空间复杂度:O(1)
二、希尔排序
希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达=1时,
所有记录在统一组内排好序。
- 希尔排序是对直接插入排序的优化。
- 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
public static void shellSort(int[] array){
int gap = array.length;
while (gap > 1){
//这里gap>1都是预排序,为了让数组一步一步接近有序
insertSortbyGap(array,gap);
gap /= 2;
}
//最终排序
insertSortbyGap(array,1);
}
public static void insertSortbyGap(int[] array,int gap){
//是直接插序的优化方法,但是有所差异
for(int bound = gap;bound < array.length;bound++){
//边界可以从gap开始,因为分组了,前gap个元素,都是每一组的第一元素,后面进行插入
int tmp = array[bound];//要插入的元素
int cur = bound - gap;//为了找到tmp合适的插入位置,向前遍历
for(;cur >= 0;cur -= gap ){
//预排序注意是--gap,
if(array[cur] > tmp){
//搬运
array[cur + gap] = array[cur];
}else {
break;
}
}
array[cur + gap] = tmp;
}
}
public static void main(String[] args) {
int[] array = {9,5,2,7,6,2,8,1};
shellSort(array);
System.out.println(Arrays.toString(array));
}
[1, 2, 2, 5, 6, 7, 8, 9]
性能分析:
时间复杂度 |
---|
最好:O(N)–数组有序 |
平均:O(N^(4/3)) |
最坏:O(N^2)–比较难构造 |
- 空间复杂度:O(1)
- 稳定性:不稳定
三、选择排序
原理:
每一次从无序区间选出最大(或最小)的一个元素,存放在无序区间的最后(或最前),直到全部待排序的数据元素排完 。
public static void select(int[] arrays){
for(int bound = 0;bound < arrays.length;bound++){
int cur = bound + 1;//待排序的区间
for (;cur<arrays.length;cur++){
if(arrays[cur] < arrays[bound]){
swap(arrays,cur,bound);
}
}
}
}
public static void swap(int[] array,int cur ,int bound){
int tmp = array[cur];
array[cur] = array[bound];
array[bound] = tmp;
}
选择排序就比较简单了,遍历这个数组,取到当前元素,当作擂台,嵌套循环(后面的待排序区间元素再依次打擂台,如果赢了就交换擂台的元素,嵌套循环结束后,待排序区间最小值站上擂台)。擂台后移,遍历结束也就完成排序。
性能分析:
- 时间复杂度:O(N^2),数据不敏感
- 空间复杂度:O(1),数据不敏感
- 不稳定
四、堆排序
思路如下:
堆排序思路和直接选择排序很像,每次取出一个最大值放到数组最后
- 先针对整个数组建堆
- 把堆顶元素放到数组最后
- 重新调整堆
public class SortDemo {
public void creatHeap(int[] arrays){
//创建堆
for(int i = (arrays.length - 1 - 1)/2; i >= 0;i--){
//找到最后一个非叶子节点,从后往前遍历,向下调整
shiftDown(arrays,arrays.length,i);
}
}
public void swap(int[] arrays,int x,int y){
int tmp = arrays[x];
arrays[x] = arrays[y];
arrays[y] = tmp;
}
public void shiftDown(int[] arrays,int size,int index){
//向下调整,三个参数,数组,调整的长度,下标
int parent = index;
int child = 2*parent + 1;
while (child < size){
//千万注意,这里的长度用size,不用arrays.length!!!!!!!!!!!!!!
if(child + 1 < size && arrays[child + 1] > arrays[child]){
child = child + 1;
}
if(arrays[parent] < arrays[child]){
swap(arrays,parent,child);
}else {
break;
}
//更新
parent = child;
child = 2*parent + 1;
}
}
public void heapSort(int[] arrays){
creatHeap(arrays);
//将堆顶元素放最后,待排序区间向下调整
for (int i = 0; i < arrays.length; i++) {
//[0,array.length - i) 待排序区间
//[array.length - i,array.length)已排序区间
swap(arrays,0,arrays.length - i - 1);
shiftDown(arrays,arrays.length - i - 1,0);
}
}
}
性能分析
- 时间复杂度:O(n*log(n)) 数据不敏感
- 空间复杂度:O(1) 数据不敏感
- 稳定性:不稳定