目录
选择排序
1.1直接选择
1.1.1思路
将指定排序位置与其他数组元素分别对比,如果满足条件就交换元素值,注意这里区别冒泡排序,不是交换相邻元素,而是把满足条件的元素与指定的排序位置交换(如从最后一个元素开始排序),这样排序好的位置逐渐扩大,最后整个数组都成为已排序好的格式。
1.1.2例子
由小到大进行选择排序,每次选择并交换后各排序码位置的变动情况,中括号中为待排序区间,中括号前面为已经排好的元素。
1.1.3 复杂度和稳定性分析
空间上看:
它只需要一个辅助空间temp ,因此其空间复杂度位O(1)。
时间上看:
待排序序列为正序,移动次数最小,为 0 次;待排序序列为逆序时,移动次数最多,为 3(n-1) 次。无论记录的初始排列如何,关键码的比较次数相同,第 i 趟排序需进行 n-i 次关键码的比较,而简单选择排序需要进行 n-1 趟排序,因此,总的比较次数为 ,为 O()。
稳定性:
假设在数列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面;并且排序之后,a[i]仍然在a[j]前面。
因此是不稳定的。
1.1.4 java代码
public void selectSort(int[] x) {
if(x.length!=0) {
int temp = 0;
for(int i = 0;i<x.length;i++) {
int min = i;
for(int j = i;j<x.length;j++) {
if(x[j] <= x[min]) {
min = j;
}
}
temp = x[min];
x[min] = x[i];
x[i] = temp;
}
}
}
1.2堆排序
1.2.1思路
堆排序,就是以堆的形式去排序。堆是一种特殊的完全二叉树结构。
完全二叉树,其特点在于:
- 从作为第一层的根开始,除了最后一层之外,第N层的元素个数都必须是2的N次方;第一层2个元素,第二层4个,第三层8个,以此类推。
- 而最后一行的元素,都要紧贴在左边,换句话说,每一行的元素都从最左边开始安放,两个元素之间不能有空闲,具备了这两个特点的树,就是一棵完全二叉树。
当根节点的左右子树都是堆时(根节点不满足堆的性质),可以通过一次向下的调整来将其变换成一个堆。
1.将长度为n的待排序的数组进行堆有序化构造成一个大顶堆
2.将根节点与尾节点交换并输出此时的尾节点
3.将剩余的n -1个节点重新进行堆有序化
4.重复步骤2,步骤3直至构造成一个有序序列
完全二叉树: 除了最后一层之外的其他每一层都被完全填充,并且所有结点都保持向左对齐。
满二叉树:除了叶子结点之外的每一个结点都有两个孩子,每一层(当然包含最后一层)都被完全填充。
完满二叉树:除了叶子结点之外的每一个结点都有两个孩子结点。
大根堆:一棵完全二叉树,满足任一节点都比其孩子节点大;
小根堆:一棵完全二叉树,满足任一节点都比其孩子节点小。
1.2.2例子
{5, 2, 6, 0, 3, 9, 1, 7, 4, 8}
在构造有序堆时,我们开始只需要扫描一半的元素(n/2-1 ~ 0)即可,为什么?
因为(n/2-1)~0的节点才有子节点,如图1,n=8,(n/2-1) = 3 即3 2 1 0这个四个节点才有子节点
第一次找到[n/2]处,进行构造:
我们比较父节点,左右孩子结点的大小,将最大的作为堆顶
第二次,我们对上次找到位置-1即可,找到结点0,对其左右孩子比较,构造这三个结点的最大堆
第三次,我们找到结点6,要对其进行构造,结果如下
第四次(重点),我们不止要构造双亲和左右孩子,我们还要比较其孩子结点为根的堆是否正确,不然我们需要进行调整
我们发现将8,7,2三个结点变为了最大堆,但是其中2,3子树不再是一个最大堆,我们需要对其修改
第五次:选取结点9进行构造
发现以结点5为根的子树不是最大堆,我们需要进行调整
完成最大堆的构建
1.2.3 复杂度和稳定性分析
空间上看:
它只需要一个辅助空间temp ,因此其空间复杂度位O(1)。
时间上看:
运行时间主要消耗在构造堆和重建堆时的反复筛选上。
构造堆的时间复杂度为O(n)
重建堆时时间复杂度为O(nlogn)。
所以总体就是O(nlogn)。
不适合排序序列个数较少的情况
稳定性:
假设在数列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面;并且排序之后,a[i]仍然在a[j]前面。
因此是不稳定的。
1.2.4 java代码
//堆排序
public static void heapSort(int[] a) {
buildMaxHeap(a);
for(int i=a.length-1;i>=0;i--) {
int temp=a[0];
a[0]=a[i];
a[i]=temp;
adjustDown(a,0,i); //将剩余len-1调整为大顶堆,循环,所以用i表示
}
}
//建立大顶堆
public static void buildMaxHeap(int[] a) {
for(int i=(a.length/2)-1;i>=0;i--) {
adjustDown(a,i,a.length);
}
}
//向下调整
public static void adjustDown(int[] a,int i,int len) {
int temp,j;
temp=a[i];
for(j=2*i+1;j<len;j=2*j+1) { //j为当前i的子节点,默认为左节点
if(j+1<len&&a[j+1]>a[j]) //如果右节点大,则选右节点
j++;
if(a[j]<=temp) //若子节点都比初始值temp小,说明找到了位置
break;
else {
a[i]=a[j]; //如果没有终止,那么将子节点中数值大的上调至i处
i=j; //同时i下降到j这个位置
}
}
a[i]=temp; //将temp放在最终的位置
}