目录
一、插入排序
1)算法思想
2)代码
二、希尔排序
1)算法思想
2)代码
三、选择排序
1)算法思想
2)代码
四、堆排序
1)什么是最大堆
2)如何创建最大堆
3)算法思想
4)代码
五、冒泡排序
1)算法思想
2)代码
六、快速排序
1)算法思想
2)代码
七、归并排序
1)算法思想
2)代码
八、基数排序/桶排序(只对整数类型有效)
1)算法思想
2)代码
一、插入排序
1)算法思想
插入排序即将一个无序的元素集合中的元素一个个插入到子集合中合适的位置,直到原集合中的元素全部加入到子集合中。
2)代码
//插入排序
void Insert_Sort(DataType a[], int n)
{
DataType temp;
for (int i = 0; i < n - 1; i++) //0 到 i 的数据已排序好
{
temp = a[i + 1];
int j = i;
while (j >= 0 && a[j] > temp) //如果是小于号则递增排序,如果是大于号则递减排序
{
a[j + 1] = a[j];
j--;
}
a[j+1] = temp;
}
}
二、希尔排序
1)算法思想
希尔排序算是插入排序的进阶版本,先把待排序的元素分为若干个小组,然后对每个小组进行插入排序。
这里的“增量(span)”代表将相邻span的元素分为一组,span的取值一般取n/2。
2)代码
//希尔排序
void Shell_Sort(DataType a[], int n)
{
int span = n / 2;
DataType temp;
while (span > 0)
{
for (int i = 0; i < span; i ++) //将数组分为span个小组
{
//对各组进行插入排序
for (int k = i; k < n - span; k += span)
{
temp = a[k + span];
int j = k;
while (j >= 0 && a[j] > temp)
{
a[j + span] = a[j];
j -= span;
}
a[j + span] = temp;
}
}
span /= 2;
}
}
三、选择排序
1)算法思想
每次从待排序的集合中选择最小(或最大)的元素放到集合的最前面,元素集合不断缩小,当待排序集合为空时代表排序结束。
2)代码
//选择排序
void Select_Sort(DataType a[], int n)
{
DataType temp;
for (int i = 0; i < n; i++)
{
DataType min = a[i];
int k = i;
for (int j = i; j < n; j++)
{
if (a[j] < min)
{
min = a[j];
k = j;
}
}
temp = a[i];
a[i] = a[k];
a[k] = temp;
}
}
四、堆排序
1)什么是最大堆
1、完全二叉树结构;
2、双亲节点的值都比其孩子节点大。
2)如何创建最大堆
对于一个线性表,很容易可以将其转换为完全二叉树,因为线性表元素的下标是一一对应着完全二叉树的节点。我们先来记住两个重要的对应:
-
(n-2)/ 2 :表示最后一个非叶子节点;
-
2 * i + 1 :表示第i个节点的左孩子;
将数组调整为大根堆的过程:
对应代码如下:
//其中n为数组a中的元素个数,h为要调整的元素的下标
static void CreatHeap(DataType a[], int n, int h)
{
int flag = 0,i = h;
DataType temp = a[i];
int j = 2 * i + 1; //先让j指向h左孩子节点的下标
while (j < n && flag != 1)
{
//寻找左右孩子节点中的较大者,j为其下标
if (j < n - 1 && a[j] < a[j + 1]) j++; //第一个判断条件为该节点是否有右孩子,第二个判断条件为右孩子是否比左孩子大,若是,则j代表右节点的下标
if (temp > a[j]) flag = 1;
else {
a[i] = a[j]; //将j位置的值上移,并且j更新为其左孩子节点的值
i = j;
j = 2 * i + 1;
}
}
a[i] = temp;
}
//创建大根堆
static void InitCreatHeap(DataType a[], int n)
{
for (int i = (n - 2) / 2; i >= 0; i--) //(n-2)/2表示左后一个非叶子节点的下标
CreatHeap(a, n, i);
}
3)算法思想
通过第二部分的操作,我们已经成功地把数组转换为了大根堆,接下来只需要不断将根节点元素与数组末尾元素进行交换,再更新大根堆即可。
为什么不从最后面的叶子节点开始拿?因为我们无法保证最后一个元素一定是最小的!
例如:(a)将88和5进行交换,随后为保持为大根堆,5与76交换,再与50交换,得到(b)。
4)代码
void Heap_Sort(DataType a[], int n)
{
DataType temp;
InitCreatHeap(a, n); //先初始化整个数组为大根堆
for (int i = n - 1; i > 0; i--) //不断将根节点元素放与数组末尾元素进行交换
{
temp = a[0];
a[0] = a[i];
a[i] = temp;
CreatHeap(a, i, 0);
}
}
五、冒泡排序
1)算法思想
冒泡排序是从第一个元素开始,将相邻的元素两个两个进行比较,每趟比较都将集合中最大(或最小)的元素放到了集合末尾。
2)代码
//冒泡排序
void Bubble_Sort(DataType a[], int n)
{
DataType temp;
int flag = 1; //判断是否提前排序完成
for (int i = n; i > 0 && flag; i--)
{
flag = 0;
for (int j = 0; j < i-1; j++)
{
if (a[j] > a[j + 1])
{
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
flag = 1; //仍然有元素进行交换,排序未完成
}
}
}
}
六、快速排序
1)算法思想
快速排序定义了一个数组的下界low和上界high,然后通过两端的指针不断与low处的元素进行比较,将比low处的元素小的元素放到左边,将所有比他大的元素放到右边,这样一来,该元素的左边的元素都比他小,右边的元素都比他大,然后递归对左边和右边的集合进行快速排序即可。
2)代码
//快速排序
static void QuickSort(DataType a[], int low,int high)
{
int i = low, j = high;
DataType temp = a[low];
while (i < j) {
while (i < j && temp <= a[j])j--;
if (i < j) {
a[i] = a[j];
i++;
}
while (i < j && temp >= a[i])i++;
if (i < j) {
a[j] = a[i];
j--;
}
}
a[i] = temp;
if(i>low) QuickSort(a, low, i - 1);
if(j<high) QuickSort(a, j + 1, high);
}
诶!🧐这个排序有点不一样,前面的排序参数都是数组和元素个数,但是它居然有三个参数!逼死强迫症!为了使用时方便统一,我们封装一下:
//快速排序
void Quick_Sort(DataType a[], int n)
{
QuickSort(a, 0, n - 1); //注意上界和下界都是可取的
}
七、归并排序
1)算法思想
将具有n个元素的数组a看成n个长度为1的有序子数组,然后从第一个子数组开始,把相邻的两个子数组两两合并,然后再将合并后的数组两两合并,直到只剩下最后一个数组。
2)代码
//一次二路归并算法
static void Merge(DataType a[], int n, DataType swap[], int k)
{
//k为有序子数组的长度,一次二路归并排序后的有序子数列存于swap
int lower1 = 0; //第一个有序子序列的下界
int lower2, upper1, upper2,m = 0,i,j;
while (lower1 + k <= n - 1) {
lower2 = lower1 + k; //第二个有序子数组的下界
upper1 = lower2 - 1; //第一个有序子数组的上界
upper2 = (lower2 + k - 1 <= n - 1) ? lower2 + k - 1 : n - 1;//第二个有序子数组的上界
//合并两个有序子数组
for (i = lower1,j = lower2; i <= upper1 && j <= upper2; m++) {
if (a[i] < a[j]) {
swap[m] = a[i++];
}
else swap[m] = a[j++];
}
//如果子数组2已存完,则把数组1中剩余的元素存入swap
while (i <= upper1) swap[m++] = a[i++];
//如果子数组1已存完,则把数组2中剩余的元素存入swap
while (j <= upper2) swap[m++] = a[j++];
lower1 = upper2 + 1;
}
//如果原数组不能构成两组子数组,则直接存入swap
for (int i = lower1; i < n; i++, m++)
swap[m] = a[i];
}
//二路归并排序
void Merge_Sort(DataType a[], int n)
{
DataType* swap = (DataType*)malloc(sizeof(DataType) * n);
int k = 1;
while (k < n)
{
Merge(a, n, swap, k);
for (int i = 0; i < n; i++)
a[i] = swap[i];
k *= 2;
}
free(swap);
}
八、基数排序/桶排序(只对整数类型有效)
1)算法思想
设待排序的元素是m位的d进制的数,不足m位的在高位补0,设置d个桶(实际上是队列),其编号为0、1、2…d-1。首先按照元素的低位的数组依次把各元素放入相应编号的队列中,然后从小到大的编号依次出队列,放回到原数组,这样就完成了一轮排序。接着按高一位的数值再次放入桶中,再出队列,直到进行m轮排列,数组中的元素就有序了。
2)代码
这里我们使用链式队列。
//桶排序
//针对整数类型
void Radix_Sort(DataType a[], int n, int m, int d) //m位的d进制数
{
int k, power = 1;
QueuePtr* tub = (QueuePtr*)malloc(sizeof(QueuePtr) * d);
if (tub == NULL)
{
printf("内存分配失败!\n");
return;
}
for (int i = 0; i < d; i++) {
LinkQueue_Init(&tub[i]);
}
//进行m次存放
for (int i = 0; i < m; i++) {
if (i == 0)power = 1;
else power *= d;
//将元素按关键字第k位的数值放入相应队列
for (int j = 0; j < n; j++) {
k = a[j] / power - (a[j] / (power * d)) * d;
LinkQueue_Add(&tub[k], a[j]);
}
//顺序回收各队列中的元素至数值a中
k = 0;
for (int j = 0; j < n; j++) {
while (!LinkQueue_Empty(tub[j])) {
a[k++] = LinkQueue_Pop(&tub[j]);
}
}
}
}
以上就是今天分享的全部内容,最后感谢你观看完我的文章,如果文章对你有帮助,可以点赞收藏评论,这是对作者最好的鼓励!不胜感激🥰