2. 选择排序
***算法思想:***以从小到大排序为例,首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
选择排序是给每个位置选择当前元素最小的,比如给第一个位置选择最小的,在剩余元素里面给第二个元素选择第二小的,依次类推,直到第n-1个元素,第n个元素不用选择了,因为只剩下它一个最大的元素了。
public static void selectSort(int[] arr){
int n = arr.length;
//从无序区间中不断挑选出最小值,挑选n-1次,最后一个元素不用挑选
for(int i = 0;i < n - 1;i++){
int minIndex = i;
for(int j = i + 1;j < n;j++){
if(arr[j] < arr[minIndex]){
minIndex = j; //更新最小值下标
}
}
//交换最小值与无序区间第一个元素
if(minIndex != i){
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
}
此排序算法是不稳定的,解释见下面:
原始数据:3 5 3 2
i = 0时: 2 5 3 3
i = 1时:2 3 5 3
i = 2时:2 3 3 5
可以看到经过排序以后,原来加粗的3在前面,现在跑到后面去了,因此是不稳定的。
3. 插入排序
类似于玩扑克牌时,左手放置了若干已经排好顺序的牌,右手中是新摸的一张牌,因此需要将抽到的牌插入到当前手中牌的合适位置。
***算法思想:***将待排序序列分成两个序列,前面的序列保持有序(默认第一个元素是有序的),后面的序列保持无序。每次从无序序列中取出第一个元素,把该元素在已经排序的序列中从右往前扫描进行比较,然后插入到合适的位置,使之成为新的有序表。
public static void insertSort(int[] arr){
int n = arr.length;
//i从第二个元素开始,默认第一个元素是有序的
for(int i = 1;i < n;i++){
int inserted = arr[i]; //待插入元素
int index = i - 1; //待插入元素前一个元素的索引
while(index >= 0 && arr[index] > inserted){
//每当前面的元素比待插入元素大,就向后移动
arr[index + 1] = arr[index];
index--;
}
//已经找到了待插入位置,即index+1
arr[index + 1] = inserted;
}
}
此排序算法是稳定的。
4. 希尔排序
希尔排序可以说是插入排序的一种变种。由于插入排序每次只能交换两个相邻的元素,因此希尔排序加快速度简单地改进了插入排序,交换不相邻的元素以对数组的局部进行排序。
***算法思想:***希尔排序的思想是采用插入排序的方法,让数组中任意间隔为 h 的元素有序,刚开始 h 的大小可以是 h = n / 2,接着让 h = n / 4,让 h 一直缩小,当 h = 1 时,也就是此时数组中任意间隔为1的元素有序,此时的数组就是有序的了。
首先它把较大的数据集合分割成若干个小组(逻辑上分组),然后对每一个小组分别进行插入排序,此时,插入排序所作用的数据量比较小(每一个小组),插入的效率比较高。
public static void insertI(int[] arr,int i,int gap){
int inserted = arr[i];
int j = i - gap;
while(j >= 0 && arr[j] > inserted){
arr[j + gap] = arr[j];
j = j - gap;
}
arr[j + gap] = inserted;
}
public static void shellSort(int[] arr){
int n = arr.length;
//进行分组,最开始的时候,gap为数组长度一半
for(int gap = n / 2;gap > 0;gap /= 2){
//对各个分组进行插入排序
for(int i = gap;i < n;i++){
//将arr[i]插入到所在分组正确的位置上
insertI(arr,i,gap);
}
}
}