堆排序
-
以最大堆为例,如果删除一个最大堆的堆顶(并不是完全删除,而是跟末尾的节点交换位置),经过自我调整,第二大的元素就会被交换上来,称为最大堆新堆的堆顶。由于二叉堆的这个特性,每一次删除旧的堆顶,调整后的新堆顶都是大小仅次于旧堆顶的节点,那么只要反复删除堆顶,反复调整二叉堆,所得到的集合就是一个有序集合。
-
堆排序算法步骤:
- 把无序数组构建成二叉堆,需要从小到大排序,则构建最大堆;如果需要从大到小排序,则构建最小堆。
- 循环删除堆顶元素,替换到二叉堆的末尾,调整堆产生新的堆顶。
- 时间复杂度:
- 第一步,把无序数组构建成二叉堆,这一步的时间复杂度是O(n)。
- 第二步,需要进行n-1次循环,每次循环调用一次downAdjust方法,所以第二步是(n-1)*logn,所以总的时间复杂度是O(nlogn).
- 空间复杂度:
- 因为没有开辟额外的集合空间,所以空间复杂度是O(1).
- 快速排序 VS 堆排序
- 相同点:堆排序和快速排序的平均时间复杂度都是O(nlogn),并且都是不稳定排序。
- 不同点:
- 快速排序的最坏时间复杂度是O(n**2),而堆排序的最坏时间复杂度稳定在O(nlogn)
- 快速排序的空间平均复杂度是O(logn),而堆排序的空间复杂度是O(1).
- 代码
/*下沉调整*/
public static void downAdjust(int[] array, int parentIndex, int length)
{
int temp = array[parentIndex];
int childIndex = 2 * parentIndex + 1;
while(childIndex < length)
{
if(childIndex + 1 < length && array[childIndex + 1] > array[childIndex])
{
childIndex++;
}
if(temp >= array[childIndex])
{
break;
}
array[parentIndex] = array[childIndex];
parentIndex = childIndex;
childIndex = parentIndex * 2 + 1;
}
array[parentIndex] = temp;
}
/*堆排序(升序)*/
public static void heapSort(int[] array)
{
// 1. 把无序数组构成最大堆
for(int i = (array.length - 2) / 2; i >= 0; i--)
// 这个地方部分奇偶,都是一样方法找到父节点。
{
downAdjust(array, i, array.length); // 创建对就是遍历非叶子节点,并下沉调整即可
}
System.out.println(Arrays.toString(array));
// 2. 循环删除堆顶元素,移到集合尾部,调整产生新的堆顶。
for(int i = array.length - 1; i > 0; i--)
{
int temp = array[i];
array[i] = array[0];
array[0] = temp;
downAdjust(array, 0, i); // 最后是i,不是array.length
}
}
public static void main(String[] args)
{
int[] arr = new int[]{
1, 2, 2, 6, 5, 7, 8, 9, 10, 0};+
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
计数排序
- 一个例子:
- 问题:给出一个学生成绩表,要求按照成绩从低到高排序,如果成绩相同,则遵循着原表固有顺序。
- 数据变形:其实就是从统计数组的第二个元素开始,每个元素都加上前面所有元素之和。
- 目的:就是让统计数组存储的元素值,等于相应整数的最终排序位置的序号,例如下标9的元素值为5,代表最终9排在第五位,也就是99排在第五位,因为有个差值:最大值-最小值.
- 过程:第一步遍历成绩表最后一行小绿的成绩,小绿的成绩是95,下标是5的元素,值是4,代表小绿的成绩排名在第四位。同时,4-1=3,该值改为3,下次碰到95分的就是第三位。其他的一次类推。
- 属于稳定排序。
- 复杂度
- 时间复杂度:如果原始数据规模是n,最大和最小整数的差值是m,则为3n+m,为O(n + m).
- 空间复杂度:如果不考虑结束数组,只考虑计数数组大小的话,是O(m),主要看内部new了多少。
- 局限性
- 当数列最大和最小差距过大时,并不适用计数排序。
- 当数列元素不是整数时,也不适合。
- 代码
public static int[] countSort(int[] array)
{
// 1. 得到数列的最大和最小值,并算出差值d
int max = array[0];
int min = array[0];
for(int i=0; i<array.length; i++)
{
if(array[i] > max)
{
max = array[i];
}
if(array[i] < min>)
{
min = array[i]
}
}
int d = max - min;
// 2. 创建统计数组并统计对应元素的个数
int[] countArray = new int[d + 1];
for(int i=0; i<array.length; i++)
{
countArray[array[i] - min]++;
}
// 3. 统计数组做变形,后面的元素等于前面元素之和。
for(int i=1; i<countArray.length; i++)
{
countArray[i] += count[i - 1];
}
// 4. 倒序遍历原始数列,从统计数组找到正确位置,输出到结果数组中
int[] sortedArray = new int[array.length];
for(int i=array.length-1; i>=0; i--) // 这个地方可以从前往后遍历,也可以从后往前遍历,之所从后往前遍历是因为这样是稳定的。
{
sortedArray[countArray[array[i] - min] - 1] = array[i];
countArray[array[i] - min]--;
}
return sortedArray;
}
public static void main(String[] args)
{
int[] array = new int[] {
95, 94, 91, 98, 99, 90, 99, 93, 91, 92};
int[] sortedArray = countSort(array);
System.out.println(Arrays.toString(sortedArray));
}
桶排序
- 步骤
- 创建桶。这里创建的桶数量等于原始数列的元素数量,除最后一个桶只包含数列最大值外,前面各个桶的区间按照比例来确定。
区间跨度 = (最大值 - 最小值) / (桶的数量 - 1)
- 第二步遍历原始数列,把元素对号入座放入各个桶中。
- 第三步对桶中内部元素分别进行排序。
- 遍历所有桶,输出所有元素
- 创建桶。这里创建的桶数量等于原始数列的元素数量,除最后一个桶只包含数列最大值外,前面各个桶的区间按照比例来确定。
- 时间复杂度是O(n), 空间复杂度是O(n)
- 缺点:在元素分布极不均衡,第一个同种有n - 1个元素,最后一个桶中有一个元素。此时时间复杂度是O(nlogn),而且还白白创建了很多空桶。
- 代码
public static double[] bucketSort(double[] array)
{
// 1. 得到数列的最大值和最小值
double max = array[0];
double min = array[0];
for(int i=0; i<array.length; i++)
{
if(array[i] > max)
{
max = array[i];
}
if(array[i] < min>)
{
min = array[i]
}
}
double d = max - min;
// 2. 初始化桶
int bucketNum = array.length;
ArrayList<LinkedList<Double>> bucketList = new ArrayList<LinkedList<Double>>(bucketNum);
for(int i=1; i<bucketNum; i++)
{
bucketList.add(new LinkedList<Double>());
}
// 3. 遍历原始数组,将每个元素放入桶中。
for(int i=1; i<array.length; i++)
{
int num = (int)((array[i] - min) * (bucketNum - 1) / d);
bucketList.get(num).add(array[i]);
}
// 4. 对每个桶内部进行排序
for(int i=1; i<bucketList.size(); i++)
{
// jdk 底层采用归并排序或者优化后的归并排序
Collections.sort(bucketList.get(i));
}
// 5. 输出全部元素
double[] sortedArray = new double[array.length];
int index = 0;
for(LinkedList<Double> list : bucketList){
for(double element : list){
sortedArray[index] = element;
index++;
}
}
return sortedArray;
}
public static void main(String[] args){
double[] array = new double[]{
4.12,6.421,0.0023,3.0,2.123,8.122,4.12, 10.09};
double[] sortedArray = bucketSort(array);
System.out.println(Arrays.toString(sortedArray));
}