1
0
mirror of https://github.com/Didnelpsun/CS408.git synced 2026-02-10 22:25:48 +08:00

更新排序

This commit is contained in:
Didnelpsun
2021-10-02 23:33:16 +08:00
parent 4d3dad32d5
commit de44228084
3 changed files with 148 additions and 14 deletions

View File

@@ -47,6 +47,7 @@ bool BinaryInsertSort(LinearTable table) {
}
table.data[j + 1] = temp;
}
return true;
}
// 希尔排序
@@ -66,19 +67,130 @@ bool ShellSort(LinearTable table) {
}
// 冒泡排序
bool BubbleSort(LinearTable table){
bool BubbleSort(LinearTable table) {
elem_type temp;
// 外循环为排序趟数一共需要length-1趟
for(int i=0;i<table.length-1;i++){
for (int i = 0; i < table.length - 1; i++) {
// 内循环为排序趟数i趟比较次数为length-i
// 排序完成的序列在后面
for(int j=0;j<table.length-1-i;j++){
if(table.data[j]>table.data[j+1]){
for (int j = 0; j < table.length - 1 - i; j++) {
if (table.data[j] > table.data[j + 1]) {
temp = table.data[j];
table.data[j] = table.data[j+1];
table.data[j+1] = temp;
table.data[j] = table.data[j + 1];
table.data[j + 1] = temp;
}
}
}
return true;
}
// 快速排序一趟划分
int PartQuickSort(LinearTable table, int low, int high) {
// 假设表中第一个元素为枢纽对表进行划分
elem_type pivot = table.data[low];
// 跳出循环条件
while (low < high) {
// 若元素大于枢轴则一直左移
while (low < high && table.data[high] >= pivot)
--high;
// 将比枢轴小的元素移动到左端
table.data[low] = table.data[high];
while (low < high && table.data[low] <= pivot)
++low;
// 比枢轴大的元素移动到右端
table.data[high] = table.data[low];
}
// 枢轴元素放到最终位置
table.data[low] = pivot;
return low;
}
// 快速排序
bool QuickSort(LinearTable table, int low, int high) {
// 递归跳出条件
if (low < high) {
// 划分
int pivot = PartQuickSort(table, low, high);
// 递归
PartQuickSort(table, low, pivot - 1);
PartQuickSort(table, pivot + 1, high);
}
return true;
}
// 简单选择排序
bool SimpleSelectSort(LinearTable table) {
int min;
for (int i = 0; i < table.length - 1; i++) {
// 记录最小元素
min = i;
// 选择最小元素
for (int j = i + 1; j < table.length; j++) {
// 更新最小元素位置
if (table.data[j] < table.data[min])
min = j;
// 交换元素
if (min != i) {
elem_type temp = table.data[min];
table.data[min] = table.data[i];
table.data[i] = temp;
}
}
}
return true;
}
// 建立根堆
bool BuildHeap(LinearTable table, bool HeadAdjust(LinearTable table, int index)) {
// 从i=[n/2]~1开始反复调整堆
for (int i = table.length / 2; i > 0; i--) {
bool result = HeadAdjust(table, i);
if (!result)
return false;
}
return true;
}
// 调整大根堆
bool HeadMaxAdjust(LinearTable table, int index) {
// 将根元素为索引值index的子树进行调整之后的元素已经有序
// 0索引处暂存子树根结点
table.data[0] = table.data[index];
// 沿着索引值较大的子结点向下筛选
for (int i = 2 * index; i <= table.length; i *= 2) {
// 继续寻找较大的子结点的下标
if (i < table.length && table.data[i] < table.data[i + 1])
i++;
// 筛选结束,因为要找的元素必须比根结点大
if (table.data[0] >= table.data[i])
break;
else {
// 将index的元素调整到双亲结点上
table.data[index] = table.data[i];
// 修改index值以便继续向下筛选
index = i;
}
}
// 将被筛选结点放入最终位置
table.data[index] = table.data[0];
return true;
}
// 堆排序
bool HeapSort(LinearTable table, bool mode){
bool result;
// mode为true建立大根堆false建立小根堆
if(mode)
result = BuildHeap(table, HeadMaxAdjust);
if(!result)
return false;
for(int i=table.length;i>1;i--){
// 栈顶元素和栈底元素交换
elem_type temp = table.data[0];
table.data[0] = table.data[i];
table.data[i] = temp;
if(mode)
HeadMaxAdjust(table,i);
}
return true;
}

View File

@@ -27,3 +27,17 @@ $C.$冒泡排序
$D.$快速排序
解:$A$。希尔排序是组间有序组内无序。
## 交换排序
**例题** 采用递归方式对顺序表进行快速排序。下列关于递归次数的叙述中,正确的是()。
$A.$递归次数与初始数据的排列次序无关
$B.$每次划分后,先处理较长的分区可以减少递归次数
$C.$每次划分后,先处理较短的分区可以减少递归次数
$D.$递归次数与每次划分后得到的分区的处理顺序无关
解:$D$。递归次数与各元素的初始排列有关。若每次划分后分区比较平衡,则递归次数少;若分区才平衡,递归次数多。递归次数与处理顺序是没有关系的。

View File

@@ -122,20 +122,26 @@
9. 当$low=high$时表示$low$和$high$之前的元素都比基准小,$low$和$high$之后的元素都比基准大,完成了一次划分。然后把基准元素放入$low$和$high$指向的位置。
10. 不断交替使用$low$和$high$指针进行对比。对左右子序列进行同样的递归操作即可,从步骤三开始。若左右两个子序列的元素数量等于一,则无需再划分。
即对序列进行比较,有头尾两个指针,尾指针开始比较向前移动,若指向值比对比值小则要交换,交替让头指针开始移动,否则不改变指针则尾指针继续向前;同理头指针向后移动,若指向值比对比值大则交换,交替让尾指针移动,否则不改变指针则头指针继续向后。最后头尾指针指向一个位置,将对比值插入到当前值,此时一趟完成。
在快速排序算法中,并不产生有序子序列,但每趟排序后会将枢轴(基准)元素放到其最终的位置上。且每一趟下来就会出现一个枢轴元素,其中左边小于枢轴,右边大于枢轴,求快速排序趟数就是找到符合这种性质的元素个数。
#### 快速排序的性能
由于快速排序使用了递归,所以需要递归工作栈,空间复杂度与递归层数相关,所以为$O(递归层数)$。
由于快速排序使用了递归,所以需要递归工作栈,空间复杂度与递归层数相关,所以为$O($递归层数$)$。
每一层划分只需要处理剩余的待排序元素,时间复杂度不超过$O(n)$,所以时间复杂度为$O(n\times\text{递归层数})$。
每一层划分只需要处理剩余的待排序元素,时间复杂度不超过$O(n)$,所以时间复杂度为$O(n\times$归层数$)$。
而快速排序会将所有元素组织成为二叉树,二叉树的层数就是递归调用的层数。所以对于$n$个结点的二叉树,最小高度为$\lfloor\log_2n\rfloor+1$,最大高度为$n$。
从而最好时间复杂度为$O(n\log_2n)$,最坏时间复杂度为$O(n^2)$,平均时间复杂度为$O(n\log_2n)$;最好空间复杂度为$O(\log_2n)$,最空间复杂度为$O(n)$。
从而最好时间复杂度为$O(n\log_2n)$,最坏时间复杂度为$O(n^2)$,平均时间复杂度为$O(n\log_2n)$;最好空间复杂度为$O(\log_2n)$,最坏空间复杂度为$O(n)$,平均空间复杂度为$O(\log_2n)$。
所以如果初始序列是有序的或逆序的,则快速排序的性能最差。若每一次选中的基准能均匀划分,则效率最高。
所以如果初始序列是有序的或逆序的,则快速排序的性能最差(速度最慢)。若每一次选中的基准能均匀划分,则效率最高(速度最快)
所以对于快速排序的性能优化是选择尽可能能中分的基准元素,入选头中尾三个位置的元素,选择中间值作为基准元素,或随机选择一个元素作为基准元素。
快速排序是所有内部排序算法中乎均性能最优的排序算法。
快速排序算法是不稳定的。
## 选择排序
@@ -146,7 +152,7 @@
#### 简单选择排序的过程
即每一趟在待排序元素中选取关键字最小的元素加入有序序列。
即每一趟在待排序元素中选取关键字最小的元素加入有序序列。交换发生在选出最值后,在每趟的尾部。经过$n-1$趟就可以完成。
#### 简单选择排序的性能
@@ -178,7 +184,7 @@
所以建立根堆的过程是:
1. 从$t\lfloor\dfrac{n}{2}\rfloor$的结点开始往前遍历。
1. 从$t<\lfloor\dfrac{n}{2}\rfloor$的结点开始往前遍历。
2. 检查当前结点$i$与左孩子和右孩子是否满足根堆条件,若不满足则交换。
+ 若是建立大根堆,检查是否满足根大于等于左、右结点,若不满足,则当前结点与更大的一个孩子互换。
+ 若是建立小根堆,检查是否满足根小于等于左、右结点,若不满足,则当前结点与更小的一个孩子互换。
@@ -186,12 +192,14 @@
+ 若是建立大根堆,则小的元素不断下坠。
+ 若是建立小根堆,则大的元素不断下坠。
调整堆的时间与树高相关$O(\log_2n)$,建立堆的时间复杂度为$O(n)$。
#### 堆排序的过程
1. 每一趟将堆顶元素加入子序列(堆顶元素与待排序序列中的最后一个元素交换)。此时后面的这个元素就排序好了。
2. 此时待排序序列已经不是堆了,需要将其再次调整为堆(小元素或大元素不断下坠)。
2. 此时待排序序列已经不是堆了(堆顶被换成最小或最大的元素),需要将其再次调整为堆(小元素或大元素不断下坠)。
3. 重复步骤一二。
4. 直到n-1趟处理后得到有序序列。基于大根堆的堆排序会得到递增序列而基于小根堆的堆排序会得到递减序列。
4. 直到$n-1$趟处理后得到有序序列。基于大根堆的堆排序会得到递增序列,而基于小根堆的堆排序会得到递减序列。
#### 堆排序的性能