排序算法
插入排序
直接插入排序
思路: 将一个记录插入到一个已经排序好的有序表中,找到合适的位置插入。一般将第一个记录当做有序表,从第二个记录还是逐个插入
代码:
1 |
|
希尔排序
希尔排序是插入排序的一种改进版本,不需要每个元素挨个比较,而是初期选用大跨步(增量较大)间隔比较,使记录跳跃式接近它的排序位置;然后增量缩小;最后增量为 1 ,这样记录移动次数大大减少,提高了排序效率。
思路
- 先取一个正整数 d1(d1 < n),把全部记录分成 d1 个组,所有距离为 d1 的倍数的记录看成一组,然后在各组内进行插入排序
- 然后取 d2(d2 < d1)
- 重复上述分组和排序操作;直到取 di = 1(i >= 1) 位置,即所有记录成为一个组,最后对这个组进行插入排序。一般选 d1 约为 n/2,d2 为 d1 /2, d3 为 d2/2 ,…, di = 1
代码实现
1 |
|
选择排序
简单选择排序
思路
比较length躺,每躺将最大(小)的找出来,赋值给第一个
代码实现
1 |
|
堆排序
什么是堆
堆数据结构是一种数组对象,它可以被视为一科完全二叉树结构。它的特点是父节点的值大于(小于)两个子节点的值(分别称为大顶堆和小顶堆)。
堆和数组的互相关系:
堆的基本操作
- A表示堆的数组,下标从1开始,一直到n
- parent(i): 节点i的父节点= floor(i/2)
- left(i): 节点i的左节点 = 2*i
- right(i): 节点i的右节点 = 2*i+1
- heap_size(A): 堆A当前元素的个数
但是在堆排序中一般是基于0开始的,也就是第一个元素是0并不是1(数组的第一个元素是0),所以公式也要做相应的调整:
- Parent(i) = floor((i-1)/2),i 的父节点下标
- Left(i) = 2i + 1,i 的左子节点下标
- Right(i) = 2(i + 1),i 的右子节点下标
类似下图:
堆排序的原理
几个操作:
- 最大堆调整(Max-Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点使得i节点之后的下标都满足最大堆的性质
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public static void maxHeapfiy(int[] array, int index, int heapSize) {
int iMax = index;
int iLeft = 2 * index + 1;
int iRight = 2 * (index + 1);
if(iLeft < heapSize && array[index] < array[iLeft]) {
iMax = iLeft;
}
if(iRight < heapSize && array[index] < array[iRight]) {
iMax = iRight;
}
if(iMax != index) {
swap(array, iMax, index);
// 递归调整
maxHeapfiy(array, iMax, heapSize);
}
} - 创建最大堆(Build-Max-Heap):将堆所有数据重新排序,使其成为最大堆
1
2
3
4
5
6
7
8public static void buildMaxHeadp(int[] array, int heapSize) {
int i;
// (heapSize -1)/2以后的节点都是叶子节点,没必要调整
int iParent = (int)Math.floor((heapSize - 1) / 2);
for(i = iParent; i >= 0; i--) {
maxHeapfiy(array, i, heapSize);
}
} - 堆排序(Heap-Sort):移除位在第一个数据的根节点,并做最大堆调整的递归运算
1
2
3
4
5
6
7public static void heapSort1(int[] array, int heapSize) {
buildMaxHeadp(array, heapSize);
for (int i = heapSize - 1; i > 0; i--) {
swap(array, 0, i);
maxHeapfiy(array, 0, i);
}
}交换排序
冒泡排序
思路
临近的数字两两进行比较,按照从小到大或者从大到小的顺序进行交换,
这样一趟过去后,最大或最小的数字被交换到了最后一位,
然后再从头开始进行两两比较交换,直到倒数第二位时结束,
代码实现
1 |
|
快速排序
这里使用分治实现快速排序
分治法
将原问题分解为若干个规模更小但结构与原问题相似的子问题。递归地解这些子问题,然后将这些子问题的解组合为原问题的解
思路
- 数据集中间选一个元素作为基准(pivot)
- 所有小于基准的元素移到基准的左边,所有大于基准的元素移到基准的右边,这个操作称为分区(partition)
- 对基准左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止
代码实现
1 |
|
归并排序
归并排序也是用分治法,将两个已经排序的序列合并成一个序列的操作。
算法思路
- 把 n 个记录看成 n 个长度为 l 的有序子表
- 进行两两归并使记录关键字有序,得到 n/2 个长度为2的有序子表
- 重复第 2 步直到所有记录归并成一个长度为 n 的有序表为止
分割归并:
代码实现
1 |
|
桶排序(基数排序)
思路
桶排序的思想近乎彻底的分治思想。假设现在需要对一亿个数进行排序。我们可以将其等长地分到10000个虚拟的“桶”里面,这样,平均每个桶只有10000个数。如果每个桶都有序了,则只需要依次输出为有序序列即可。
- 将待排数据按一个映射函数f(x)分为连续的若干段。理论上最佳的分段方法应该使数据平均分布;实际上,通常采用的方法都做不到这一点。显然,对于一个已知输入范围在【0,10000】的数组,最简单的分段方法莫过于x/m这种方法,例如,f(x)=x/100。
“连续的”这个条件非常重要,它是后面数据按顺序输出的理论保证 - 分配足够的桶,按照f(x)从数组起始处向后扫描,并把数据放到合适的桶中。对于上面的例子,如果数据有10000个,则我们需要分配101个桶(因为要考虑边界条件:f(x)=x/100会产生【0,100】共101种情况),理想情况下,每个桶有大约100个数据
- 对每个桶进行内部排序,例如,使用快速排序。注意,如果数据足够大,这里可以继续递归使用桶排序,直到数据大小降到合适的范围
- 按顺序从每个桶输出数据。例如,1号桶【112,123,145,189】,2号桶【234,235,250,250】,3号桶【361】,则输出序列为【112,123,145,189,234,235,250,250,361】
排序算法
https://haobin.work/2017/12/24/算法/八大排序算法总结上/