mirror of
https://github.com/Didnelpsun/CS408.git
synced 2026-02-10 22:25:48 +08:00
更新排序
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -27,3 +27,17 @@ $C.$冒泡排序
|
||||
$D.$快速排序
|
||||
|
||||
解:$A$。希尔排序是组间有序组内无序。
|
||||
|
||||
## 交换排序
|
||||
|
||||
**例题** 采用递归方式对顺序表进行快速排序。下列关于递归次数的叙述中,正确的是()。
|
||||
|
||||
$A.$递归次数与初始数据的排列次序无关
|
||||
|
||||
$B.$每次划分后,先处理较长的分区可以减少递归次数
|
||||
|
||||
$C.$每次划分后,先处理较短的分区可以减少递归次数
|
||||
|
||||
$D.$递归次数与每次划分后得到的分区的处理顺序无关
|
||||
|
||||
解:$D$。递归次数与各元素的初始排列有关。若每次划分后分区比较平衡,则递归次数少;若分区才平衡,递归次数多。递归次数与处理顺序是没有关系的。
|
||||
|
||||
@@ -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$趟处理后得到有序序列。基于大根堆的堆排序会得到递增序列,而基于小根堆的堆排序会得到递减序列。
|
||||
|
||||
#### 堆排序的性能
|
||||
|
||||
|
||||
Reference in New Issue
Block a user