概念
选择排序:从排序的记录中选择出关键字最小的记录,顺序放在已排好的子文件的后面
常用的方法
直接选择法 、 堆排序
直接排序的思想:n 个记录的文件的直接选择排序需要经过n-1次直接排序所得出结果
void SelectSort(int *arr, int len) { int i,j; // 为循环做准 int iMin; // 存储每次最小值 int temp; // 作为临时存储值 for (i=0; i<len-1; i++) // 进行len-1趟比较即可 { iMin = i; // 存储每次最小值 for (j=i+1; j<len; j++) // 第i次需要与之比较的数据 { if (arr[iMin]>arr[j]) { iMin = j; // 记录最小值的位置 } } temp = arr[i]; // 交换 arr[i] = arr[iMin]; arr[iMin] = temp; } }
直接排序的核心:确定待排序的的数据 、确定比较的数据、找出最小值、进行交换
直接排序的算法性能分析
直接选择排序和直接插入排序类似,都分为无序区、有序区、 区别:直接插入排序是将无序区的第一个元素直接插入到有序区;直接选择排序 把最小的元素放到最后
例子
void Selectsort(int a[], int n) { int i, j, nMinIndex; for (i = 0; i < n; i++) { nMinIndex = i; //找最小元素的位置 for (j = i + 1; j < n; j++) if (a[j] < a[nMinIndex]) nMinIndex = j; Swap(a[i], a[nMinIndex]); //将这个元素放到无序区的开头 } }
堆排序
堆排序类似于一个二叉树除了最底层之外其它层都是满的
二叉堆 分为 最小堆和最大堆
最大堆:每个父节点的元素大于等于 孩子的节点
实现
void AdjustMaxHeap(int a[], int i, int n) { int j, temp; temp = a[i]; // 保存第i个结点值 j = 2 * i + 1; // 第i个结点的左孩子 while (j < n) { if (j + 1 < n && a[j + 1] > a[j]) // 在左右孩子中找最大的 j++; // 如果右孩子大,j就变成右孩子序号 if (a[j] <= temp) // 如果左右孩子的值都不大于父结点,就终止 break; a[i] = a[j]; // 把较大的子结点往上移动,替换它的父结点 i = j; // 为下次循环做准备 j = 2 * i + 1; } a[i] = temp; // 保存父结点的值,此时i是j的值,表示孩子的值 }
功能:建立初始堆(最大堆)
思路:
1、要想将初始文件R[1..n]调整为一个大根堆,就必须将它
所对应的完全二叉树中以每一结点为根的子树都调整为堆。
2、显然只有一个结点的树是堆,而在完全二叉树中,所有序号i>[n/2]([]向下取整)的
结点都是叶子,因此以这些结点为根的子树均已是堆。
3、我们只需依次将以序号为[n/2],[n/2]-1,...1的结点作为根的子树都调整为堆即可。
4、由于c语言中数组下标从0开始,因此,第3点的序号改为[n/2]-1,[n/2]-2,...0
void MakeMaxHeap(int a[], int n) { for (int i = n / 2 - 1; i >= 0; i--) // 根结点序号 AdjustMaxHeap(a, i, n); // 为每个根结点调整,保存最大堆性质 }
1、建立初始堆 2、每一趟最后一个数都与a[0]交换,并重新调整堆, 使其保持堆的特性。 */ void MaxHeapSort(int a[], int n) { MakeMaxHeap(a,n); // 建立初始堆 for (int i = n - 1; i >= 1; i--) // 对当前无序区a[1..i]进行堆排序,共做n-1趟 { int temp = a[i]; // 将堆顶和堆中最后一个记录交换 a[i] = a[0]; a[0] = temp; AdjustMaxHeap(a, 0, i); // 将a[0..i]重新调整为最大堆,仅有a[0]可能违反堆性质 } }
堆排序的算法性能分析
堆排序是不稳定,但是它的平均时间复杂度和最差情况时间复杂度都是O(nlogn)