选择排序(这里实现升序)
实现思想:遍历数组找出最大或最小的数,交换到数组末尾(最大)或者数组起始位置(最小),直到数组完成有序。
动图演示(演示的是找最小的)

?void Swap(int* a, int* b)//传址,才能完成交换,否则形参不会改变实参。 { ?? ?int tmp = *a; ?? ?*a = *b; ?? ?*b = tmp;//交换函数 } void SelectSort(int* a, int n) { ?? ?int begin = 0; ?? ?int i = 0; ?? ?while (begin < n) ?? ?{ ?? ??? ?int min = begin;//记录最小值下标,min从begin位置开始找数组未完成排序的最小值。 ?? ??? ?for (i = begin; i < n; i++) ?? ??? ?{ ?? ??? ??? ?if (a[min] > a[i]) ?? ??? ??? ?{ ?? ??? ??? ??? ?min = i;//更新min位置 ?? ??? ??? ?} ?? ??? ?} ?? ??? ?Swap(&a[begin], &a[min]); ?? ??? ?begin++;//第一轮排序后,数组最小值已经完成排序,begin++,排序区间往后移。 ?? ?} }
这里可以优化一点,循环找最小位置的下标同时把最大位置的下标也找出来,最小的往前交换,最大的往后交换,(begin++,end--)区间改变,这样效率快一倍,但是有特殊情况要处理。
void SelectSort(int* a, int n) { ?? ?int begin = 0;//需要排序数组开始位置 ?? ?int end = n - 1;//需要排序数组最后位置 ?? ?int i = 0; ?? ?while (begin < end)//begin=end,一个元素不需要处理 ?? ?{ ?? ??? ?int min = begin;//记录最小值下标 ?? ??? ?int max = begin;//记录最大值下标 ?? ??? ?for (i = begin; i <= end; i++) ?? ??? ?{ ?? ??? ??? ?if (a[min] > a[i]) ?? ??? ??? ?{ ?? ??? ??? ??? ?min = i;//更新min位置 ?? ??? ??? ?} ?? ??? ??? ?if (a[max] < a[i]) ?? ??? ??? ?{ ?? ??? ??? ??? ?max = i;//更新mxa位置 ?? ??? ??? ?} ?? ??? ?} ?? ??? ?Swap(&a[begin], &a[min]);//这里先交换最小值位置与begin位置元素 ?? ??? ?if (max == begin)//判断一下最大值是否在begin位置上 ?? ??? ?{ ?? ??? ??? ?max = min;//如果是,交换后,最大值就在min下标位置 ?? ??? ?} ?? ??? ?Swap(&a[end], &a[max]); ?? ??? ?begin++; ?? ??? ?end--; ?? ?} } ?
时间复杂度最好最坏都是O(N^2)? 空间复杂度O(1)。?
堆排序?
实现思想,如果排升序,得先建一个大堆,在通过向下调节完成排序。
建堆
向下调节算法 若想将其调整为小堆,那么根结点的左右子树必须都为小堆。 若想将其调整为大堆,那么根结点的左右子树必须都为大堆。
所以我们得最后一个非叶子节点的左右孩子节点开始调堆。

思想实现(建大堆)
向下调整算法思想
1.从根结点处开始,找出左右孩子大的那个节点 2.大的节点与父亲节点比较 ? ? 若大的孩子比父亲节点还要大,则与父亲节点位置交换,并让这个节点为新的父亲节点,继续向下调整直到叶子节点为止, ?若大的孩子比父亲小,则不需处理了,调整完成,整个树已经是大堆了。

?void AdjustHeapDown(int* a, int size, int parent) { ?? ?int child = parent * 2 + 1;? //左孩子下标 ?? ?while (child < size)//child>=size时,已经不在堆中了 ?? ?{ ?? ??? ?if (child + 1 < size && a[child + 1] > a[child])//child+1<size表示右孩子存在 ?? ??? ?{ ?? ??? ??? ?child++;//表示右孩子的值大于左孩子的值,与父亲节点比较的值是右孩子 ?? ??? ?} ?? ??? ?if (a[child] > a[parent]) ?? ??? ?{ ?? ??? ??? ?Swap(&a[child], &a[parent]);//交换父亲孩子节点位置 ?? ??? ??? ?parent = child;//父亲迭代到该孩子节点 ?? ??? ??? ?child = parent * 2 + 1;//下一个需要比较的孩子节点位置 ?? ??? ?} ?? ??? ?else ?? ??? ?{ ?? ??? ??? ?break;//已经满足堆的条件,跳出本次循环 ?? ??? ?} ?? ?} }
有了向下调整思想,怎样将一个任意的二叉树调成堆呢??
因为要左右节点都满足堆的性质,才能向下调整,那我们就可以从第一个非叶子节点下标开始调节,直到调到根结束。
//n-1是最后一个节点位置,公式法求该位置的父亲节点位置,parent=(child-1)/2。 ??for (int i = (n - 1 - 1) / 2; i >= 0; i--)// i=0时,左右都满足大堆,最后一次从根节点调 ??{ ?? ???AdjustHeapDown(a, n, i);//n是二叉树的节点个数 ??}
建堆的时间复杂度 (每层节点数*向下调节次数)
最后一层叶子节点不需要向下调节,所以只需要调到倒数第二层节点
 
堆排序
思想实现:
1.堆顶元素与最后一个元素交换
2.从根位置执行向下调整算法,但是此时最后一个位置不属于向下调整算法区间(此时该值已经排好),通过end--,调整区间往前移,重复执行1,2,直到end=0时(区间只有一个节点),完成排序。
void HeapSort(int* a, int n) {
? ? //建堆 ?? ?for (int i = (n - 1 - 1) / 2; i >= 0; i--) ?? ?{ ?? ??? ?AdjustHeapDown(a, n, i); ?? ?}
? ? ?? ?int index = n - 1;//最后一个元素位置 ?? ?while (index > 0) ?? ?{ ?? ??? ?Swap(&a[0], &a[index]); ?? ??? ?AdjustHeapDown(a, index, 0); ?? ??? ?index--; ?? ?} }
时间复杂度为O(N*logN)? ?空间复杂度是O(1)
|