目录
一、简单选择排序
● 简单选择排序的动画演示详见:https://www.cs.usfca.edu/~galles/visualization/ComparisonSort.html
● 原理:
假设排序表为 L [1...n],第 i 趟排序即从 L[1...n] 中选择关键字最小的元素与 L[i] 交换,每一趟排序可以确定一个元素的最终位置。 |
● 代码:
void SelectSort(ElemType A[], int n)
{
for(int i=0;i<n-1;i++) //一共进行 n-1趟
{
int min = i;
for(int j=i+1;j<n;j++)
if (A[j] < A[min]) min = j;
if (min != i) swap(A[i], A[min]); //封装的swap函数共移动元素3次
}
}
二、堆排序
● 堆排序的动画演示详见:https://www.cs.usfca.edu/~galles/visualization/HeapSort.html
● 原理:
|
【2.1】 将存放 n 个关键字的一维数组建成初始堆(以大根堆为例)。 【2.2】堆顶元素是最大值,输出堆顶元素。 【2.3】将堆底元素送入堆顶,大根堆被破坏。 【2.4】将堆顶元素依次向下调整,直至再次满足大根堆的性质。 【2.5】继续输出堆顶元素,以此往复。。。直至堆中只剩一个元素。 |
🎉🎉🎉 🎈🎈🎈 ✨✨✨
【2.1】将存放 n 个关键字的一维数组建成初始堆(以大根堆为例)。 如何构造初始堆? 首先,我们从不同的数据结构视角去看待一个一维数组,它本身也是一个已经构建好的完全二叉树,只不过目前不满足大根堆的性质。 对于n个结点的完全二叉树,最后一个结点是第 ⌊n/2⌋ 个结点的孩子。对第 ⌊n/2⌋ 个结点为根的子树筛选(对于大根堆,若根结点的关键字小于左右孩子中关键字较大者,则交换),使该子树成为堆。之后向前依次对各结点( ⌊n/2⌋-1 ~ 1)为根的子树进行筛选,看该结点值是否大于其左右子结点的值,若不大于,则将左右子结点中的较大值与之交换,交换后可能会破坏下一级的堆,于是继续采用上述方法构造下一级的堆,直到以该结点为根的子树构成堆为止。反复利用上述调整堆的方法建堆,直到根结点。 ● 初步总结: 从最后一个分支节点 ⌊n/2⌋ 一直到根结点依次调整 ● 图示: |
【2.2】堆顶元素是最大值,输出堆顶元素。 略 |
【2.3】将堆底元素送入堆顶,大根堆被破坏。 |
【2.4】将堆顶元素依次向下调整,直至再次满足大根堆的性质。 怎么重新调整为大根堆? ● 方法:将堆顶元素向下筛选 ● 图示: |
【2.5】继续输出堆顶元素,以此往复。。。直至堆中只剩一个元素。 略 |
● 代码:
//堆排序
void HeadAdjust(ElemType A[], int k, int len)
{
//函数headAdjust只对元素为k的根进行调整
A[0] = A[k]; //A[0]暂存子树的根节点
for (int i = k * 2; i <= len; i *= 2)
{
if (i < len && A[i] < A[i + 1]) //i<len确保有右孩子
i++; //选取key值较大的子结点的下标
if (A[0] > A[i]) break; //不需要调整,筛选结束
else
{
A[k] = A[i]; //将A[i]调整到双亲结点上
k = i; //修改k值,以便继续向下进行筛选
}
}
A[k] = A[0];
}
void BuildMaxHeap(ElemType A[], int len)
{
for (int i = len / 2; i > 0; i--)
HeadAdjust(A, i, len);
}
void HeapSort(ElemType A[], int len)
{
BuildMaxHeap(A, len);
for (int i = len; i > 1; i--)
{
swap(A[i], A[1]); //堆顶元素与堆底元素交换
HeadAdjust(A, 1, i - 1); //调整剩余的i-1个元素
}
}
int main()
{
ElemType A[10] = {9999, 5, 3, 7, 54, 24, 23, 8, 12, 167};
HeapSort(A, 9);
for (int i=1;i<10;i++)
{
cout << A[i] << " ";
}
}
三、归并排序
● 归并排序的动画演示详见:https://www.cs.usfca.edu/~galles/visualization/ComparisonSort.html
● 原理:
2-路归并排序 将排序表中的 n 个记录看成 n 个有序的子表,每个子表长度为1。然后两两归并,得到 个长度为1或2的有序表,继续两两归并......如此重复,直到合并为一个长度为 n 的有序表。 |
● 图示:
● 代码:
ElemType *B = (ElemType *)malloc((10 + 1) * sizeof(ElemType)); //辅助数组B
void Merge(ElemType A[],int low,int mid,int high)
{
int i, j, k;
//表的两段A[low,mid],A[mid+1,high]各自有序,将他们合并成一个有序表
for (k = low; k <= high; k++)
B[k] = A[k]; //全部复制
for(i=low,j=mid+1,k=i;i<=mid&&j<=high;k++)
{
if (B[i] <= B[j]) //比较左右两段的元素
A[k] = B[i++];//将较小的值复制到A中
else
A[k] = B[j++];
}
while (i <= mid) A[k++] = B[i++]; //若第一个表未检测完,复制
while (j <= high) A[k++] = B[j++];//若第二个表未检测完,复制
}
void MergeSort(ElemType A[],int low,int high)
{
if(low<high)
{
int mid = (low + high) / 2;
MergeSort(A, low, mid);
MergeSort(A, mid + 1, high);
Merge(A, low, mid, high); //归并
}
}