简单选择排序
算法思想:假设是一个有 n 个元素的数组,假设升序
- 第一步,从 1 到 n 中,依次比较选择一个最小值,放在第一个位置。
- 第二步,由于第一个元素已经是最小值,所以这时候我们从 2 到 n 中,选择一个次小值,放在第二个位置。
- 依次类推,直到在会剩下两个元素为止,进行最后一次判断后,就可以得到一个有序的数组了。
代码如下:
// 两个参数:待排序数组,和数组元素个数
// 假设我们要升序
void SelectSort(int arr[], int n){
// 定义两个用于循环的变量
int i, j;
// 外层循环控制比较的轮数,最后一个元素不需要比较,所以只需要n-1次比较
for (i=1; i<=n-1; i++){
// 从i到n-1进行比较,因为前面i-1轮已经找出了i-1个最小值
// 因为是相邻的比较,所以第n-1次比较的时候,比较的第n-1个元素和第n个元素的大小
for (j=i; j<=n-1; j++){
// 如果当前元素比它的后一个元素大,那么就交换位置,让大的往后走
// 如果当前元素不必后一个元素大,就不用交换,也实现了大的元素在后面
if (arr[j]>arr[j+1]){
arr[0] = arr[j];
arr[j] = arr[j+1];
arr[j+1] = arr[0];
}
}
}
}
堆排序
【大顶堆=大根堆,小顶堆=小根堆】
堆排序思想:
- 把数组想象成一颗完全二叉树,让这颗树满足根节点大于(小于)左右孩子,然后左右子树也满足这个条件。
- 最终建立而成的完全二叉树就是一个大顶堆(小顶堆)。这时候在最上面的根部的元素一定是这棵树的最大值(最小值)。
- 而且由于这是一个用数组模拟的完全二叉树,所以最上面的根部元素一定是数组的第一个元素,堆底元素一定是最下面一排的最右边的元素。
- 那么我们让根元素(数组第一个元素)和堆底元素(数组最后一个元素)交换,就可以把筛选出来的最值放在后面。
- 这时候由于根部元素是从堆底上去的,就不满足大顶堆(小顶堆)的要求,然后我们再重新对除了最后一个元素的剩下元素进行建堆操作。
- 再次建堆后,根部元素就是剩下元素的最大值,然后再让根部元素后剩下元素的最后一个元素交换,我们就得到了两个最值的有序序列。
- 然后不断重复以上的建堆然后交换的操作,直到最后只剩下一个元素,这个数组整个就是一个有序序列了。
堆排序代码思路:
- 首先,我们得知道,对于一个完全二叉树,有孩子的节点是前 n/2 个元素。后面的都是叶子节点,叶子节点是自然服从顶堆规则的,因为只有一个嘛。(关于这点大家可以自己画图验证一下)
- 先写一个建堆的函数。
- 然后进行堆顶元素和堆底元素的交换。
- 然后再对剩下的元素进行建堆操作。
- 重复 2 3 直到只剩下一个元素为止。
// 1. 调整为堆函数:假设建立大顶堆
// 三个参数,arr是待建堆的数组
// 假设 arr[k+1 ... n]已经是堆,将arr[k ... n]调整为以s为根的大顶堆。
void HeapAdjust(int arr[], int k, int n){
// 用于循环的变量
int i;
// 把当准备建堆的第k个元素放入0的位置暂时存着
arr[0] = arr[k];
// i=2*k就是k节点的左孩子,取右孩子只需要左孩子+1
for (i=2*k; i<=n; i*=2){
// 在i还在n这个节点范围内时,如果左孩子比右孩子小,说明右孩子是较大值
if (i<n && arr[i]<arr[i+1]){
// 让i+1后,这时候就是指向了右孩子(较大值)
i=i+1;
}
// 如果当前k节点的值小于他的孩子结点
if(arr[0]<arr[i]){
// 就把他的孩子结点的值赋值给k节点
// 这里不用担心覆盖,因为在arr[0]中存了一份,而且上面判断也是用的arr[0]
arr[k] = arr[i];
// 然后让k节点赋值为i,也就是间接的让k节点的值走到了i节点
// 下次循环就是判断的i节点的左右孩子,依次反复
k = i;
}else{
// 如果上面的结论没有成立,说明k节点比他的左右孩子都大
// 并且他的左右孩子都是之前已经建堆完成的,所以就没必要再进行循环了,直接退出循环
break;
}
}
// 整个循环结束后,由于k通过k=i进行了移动,所以我们把最开始存好的arr[0]值赋值给新的k的位置
arr[k] = arr[0];
}
// 2. 初始化堆
void BuildHeap(int arr[], int n){
// 循环变量
int i;
// 因为前n/2个元素才是分支节点,后面的都是叶子,所以这里从n/2到1依次建立堆
// 从下往上有个方便的地方就是下面已经符合堆,就只需要慢慢往上面解决就行
for (i=n/2; i<=1; i++){
// 通过堆调整,对每个分支节点进行依次调整
HeapAdjust(arr, i, n);
}
}
// 3. 堆排序
void HeapSort(int arr[], int n){
// 循环变量
int i;
// 先把数组初始化一个堆
BuildHeap(arr, n);
// 然后通过for循环依次调换堆底和堆顶的元素,让最值放在最后
for (i=n; i>=2; i--){
// 交换堆顶和堆底的元素
arr[0] = arr[i];
arr[i] = arr[1];
arr[1] = arr[0];
// 交换完了后,就对剩下的元素进行堆调整,便于下次交换
HeapAdjust(arr,1, i-1);
}
}
主函数
void main(void){
int i;
int arr[] = {0,135,4,315,3,15,43,54};
int n = 7;
HeapSort(arr, n);
for (i=1; i<=n; i++){
printf("%d,", arr[i]);
}
printf("\n");
}
以上的两个排序,堆排序较难,如果暂时理解不了堆排序的代码,可以先理解堆排序的思想。
大家如果有什么特别的见解,欢迎评论留言。 ~. ~