✨✨✨学习的道路很枯燥,希望我们能并肩走下来!
目录
目录
前言
本篇详细介绍了排序的原理及其实现,让使用者对排序的原理及其实现有进一步认识,而不是仅仅停留在表面,更好的模拟,为了更好的使用. 文章可能出现错误,如有请在评论区指正,让我们一起交流,共同进步!
一、冒泡排序
1.1 冒泡排序的原理
冒泡排序的原理是:从左到右,相邻元素进行比较。每次比较一轮,就会找到序列中最大的一个或最小的一个。这个数就会从序列的最右边冒出来。
以从小到大排序为例,第一轮比较后,所有数中最大的那个数就会浮到最右边;第二轮比较后,所有数中第二大的那个数就会浮到倒数第二个位置……就这样一轮一轮地比较,最后实现从小到大排序。
1.2、代码实现
代码如下(C):
void Swap(int* px, int* py)
{
int tmp = *px;
*px = *py;
*py = tmp;
}
void BubbleSort(int* a, int n)
{
for (int j = 0; j < n-1; j++)
{
for (int i = 0; i < n - 1-j; i++)
{
if (a[i] > a[i + 1])
{
Swap(&a[i], &a[i + 1]);
}
}
}
}
二、插入排序
2.1 基本思想:
直接插入排序是一种简单的插入排序法,其基本思想是:
把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为 止,得到一个新的有序序列 。
2.2 直接插入排序
当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与 array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移
直接插入排序的特性总结:
1. 元素集合越接近有序,直接插入排序算法的时间效率越高
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1),它是一种稳定的排序算法
4. 稳定性:稳定
代码如下 :
void InsertSort(int* a, int n)
{
for (int i = 0; i < n - 1; i++)
{
int end = i;
int temp = a[end + 1];
while (end >= 0)
{
if (a[end] > temp)
{
a[end + 1] = a[end];
end--;
}
else
{
break;
}
}
a[end + 1] = temp;
}
}
三 希尔排序( 缩小增量排序 )
希尔排序法又称缩小增量法。希尔排序法的基本思想是:
先选定一个整数,把待排序文件中所有记录分成个 组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工 作。当到达=1时,所有记录在统一组内排好序。
希尔排序的特性总结:
1. 希尔排序是对直接插入排序的优化。
2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就 会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些树中给出的 希尔排序的时间复杂度都不固定:
《数据结构(C语言版)》--- 严蔚敏:
《数据结构-用面相对象方法与C++描述》--- 殷人昆:
4. 不稳定
代码如下:
void ShellSort(int* a, int n)
{
int gap = n;
while (gap > 1)
{
gap /= 2;
for (int i = 0; i < n - gap; i++)
{
int end = i;
int temp = a[end + gap];
while (end >= 0)
{
if (a[end] > temp)
{
a[end + gap] = a[end];
end-=gap;
}
else
{
break;
}
}
a[end + gap] = temp;
}
}
}
四、归并排序
4.1 归并排序的基本思想
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有 序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
归并排序核心步骤:
动图分析:
4.2、代码实现
1.递归实现
MergeSort_A(int* a, int* temp, int begin, int end)
{
if (begin == end)
return;
int mid = (begin + end) / 2; //取中
MergeSort_A(a, temp, begin, mid); //递归
MergeSort_A(a, temp, mid+1, end);
int begin1 = begin, end1 = mid;
int begin2 = mid + 1, end2 = end;
int i = begin;
while (begin1 <= end1 && begin2 <= end2) //结束条件为其中一方停止
{
if (a[begin1] <= a[begin2]) //依次比较,取小的尾插到temp数组中
{
temp[i++] = a[begin1++];
}
else
{
temp[i++] = a[begin2++];
}
}
while (begin1 <= end1)
{
temp[i++] = a[begin1++];
}
while (begin2 <= end2)
{
temp[i++] = a[begin2++];
}
memcpy(a + begin, temp + begin, sizeof(int) * (end - begin + 1)); //覆盖到原数组
}
void MergeSort(int* a, int n)
{
int temp = (int*)malloc(sizeof(int) * n); //创建存放数组temp,用于交换排序
if (temp == NULL)
{
perror("malloc fail");
return;
}
MergeSort_A(a,temp, 0, n - 1);
free(temp);
temp = NULL;
}
2.非递归实现
void MergeSortNonR(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
if (tmp == NULL)
{
perror("malloc fail");
return;
}
int gap = 1; //begin(包括1,2,后end同)与end之间的元素个数
while (gap < n)
{
for (int j = 0; j < n; j += 2*gap)
{
int begin1 = j, end1 = begin1 + gap - 1;
int begin2 = begin1 + gap, end2 = begin2 + gap - 1;
int i = j;
if (end1 >= n || begin2 >= n) //越界问题处理
break;
if (end2 >= n)
end2 = n - 1;
while(begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] <= a[begin2])
{
tmp[i++] = a[begin1++];
}
else
{
tmp[i++] = a[begin2++];
}
}
while (begin1 <= end1)
{
tmp[i++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[i++] = a[begin2++];
}
memcpy(a + j, tmp + j, sizeof(int) * (end2 - j + 1));
}
gap *= 2;
}
free(tmp);
tmp = NULL;
}
越界问题处理:
4.3、 归并排序的特性总结
1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(N)
4. 稳定性:稳定
五 选择排序
5.1 选择排序是什么
选择排序( Selection sort)是一种简单直观的排序算法。它的工作原理是每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。
5.2 选择排序的原理
1.在元素集合array[i]--array[n-1]中选择关键码最大(小)的数据元素
2.若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换
3.在剩余的array[i]--array[n-2](array[i+1]--array[n-1])集合中,重复上述步骤,直到集合剩余1个元素
5.3、代码实现
void Swap(int* px, int* py) //对数进行交换
{
int tmp = *px;
*px = *py;
*py = tmp;
}
void SelectSort(int* a, int n)
{
int begin = 0;
int end = n - 1; //对从首尾同时开始进行最大最小选择
while (begin < end)
{
int min = begin; //存首尾下表方便后面交换;
int maxi = end;
for (int i = begin+1; i <= end; i++)
{
if (a[i] < a[min]) //若下标为i小于下标为min的数,则i的值赋给min;
{
min = i;
}
if (a[i] > a[maxi]) //同上
{
maxi = i;
}
}
Swap(&a[begin], &a[min]);
if (maxi == begin) //防止位置重叠,进行无效交换
{
maxi = min;
}
Swap(&a[end], &a[maxi]);
begin++;
end--;
}
}
// 解释:
if (maxi == begin) //防止位置重叠,进行无效交换
{
maxi = min;
}
我们假设给一串数据:5 3 9 6 2 4 7 1 8;
对这串数据进行我们刚才写的代码(未加我所解释的这串代码),结果如下:
我们发现 4 和 6 并没有交换成功
原因在哪呢?
我们进入调试看一下:
我们发现,maxi 和 begin ,mini 和 end 的位置重叠;
在我们执行
Swap(&a[begin], &a[min]);
4 就换到maxi 和 begin'所在公用的位置, 6则在mini 和end共用的位置
此时我们再进行
Swap(&a[end], &a[maxi]);
则 4 和 6又回到原来的位置,因此再 maxi位于begin的位置时,再 Swap(&a[begin], &a[min]); 后,最大值则是再min的位置,则将min赋给maxi;
六、计数排序
6.1、计数排序思想
计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。
6.2、实现步骤
1. 统计相同元素出现次数
2. 根据统计的结果将序列回收到原来的序列中
6.3 代码实现
void CountSort(int* a, int n)
{
int min, max = a[0];
for (int i = 0; i < n; i++)
{
if (a[i] < min)
{
min = a[i];
}
if (a[i] > max)
{
max = a[i];
}
}
int range = max - min + 1;
int* tmp = (int*)calloc(range, sizeof(int));
if (tmp == NULL)
{
perror("calloc fail");
return;
}
for (int i = 0; i < n; i++)
{
tmp[a[i] - min]++;
}
int j = 0;
for (int i = 0; i < range; i++)
{
while (tmp[i]--)
{
a[j++] = i + min;
}
}
}
总结
✨✨✨各位读友,本篇分享到内容是否更好的让你理解了,如果对你有帮助给个👍赞鼓励一下吧!!
🎉🎉🎉世上没有绝望的处境,只有对处境绝望的人。
感谢每一位一起走到这的伙伴,我们可以一起交流进步!!!一起加油吧!!!。