目录
一.冒泡排序
1.基本思想
通过对待排序序列从前向后(从下标较小的元素开始),依次对相邻两个元素的值进行两两比较,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就如果水底下的气泡一样逐渐向上冒。
2.步骤
<1>比较相邻的元素。如果第一个比第二个大,就交换他们两个。
<2>每趟从第一对相邻元素开始,对每一对相邻元素作同样的工作,直到最后一对。
<3>针对所有的元素重复以上的步骤,除了已排序过的元素(每趟排序后的最后一个元素),直到没有任何一对数字需要比较。
3.动图过程展示
4.代码示例(效率提高之后的)
此代码效率比较高,设置了flag变量。如果flag不再变化,就代表数组已经有序,后续无须再进行排序了。
#include <stdio.h>
void bubble_sort(int arr[], int sz)
{
for (int i=0;i<sz-1;i++)
{
int flag = 1;//假设这一趟已经有序了。
for (int j=0;j<sz-1-i;j++)
{
if (arr[j] > arr[j + 1])
{
flag = 0;//发生交换就说明,无序。
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
if (flag == 1)//这一趟没交换就说明已经有序,后续无需排序了。
{
break;
}
}
}
int main()
{
//实现一个对整型数组的冒泡排序
//冒泡排序的核心思想:两两相邻的元素进行比较
int arr[] = { 2,45,12,6,24,6,9,13 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
二.选择排序
1.基本思想
它的工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,继续放在起始位置知道未排序元素个数为0。
2.步骤
<1>首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
<2>再从剩余未排序元素中继续寻找最小(大)元素,然后放到未排序序列的起始位置。
<3>重复第二步,直到所有元素均排序完毕。
3.动图过程展示
4.代码示例(效率提高之后的)
实际上,我们可以一趟选出两个值,一个最大值,一个最小值,然后将其放在序列开头和末尾,这样可以使选择排序的效率快一倍。
#include <stdio.h>
void swap(int* a, int* b)//形参是指针变量
{
int temp = *a;
*a = *b;
*b = temp;
}
void Select_sort(int arr[], int sz)
{
int left = 0;
int right = sz - 1;
while (left < right)
{
int maxi = left;//保存最大值的下标
int mini = left;//保存最小值的下标
for (int i = left; i <= right; i++)
{
if (arr[i] < arr[mini])
{
mini = i;
}
if (arr[i] > arr[maxi])
{
maxi = i;
}
}
if (mini == right && maxi == left) {
swap(&arr[left], &arr[right]);
}
else if (mini == right) {
swap(&arr[mini], &arr[left]);//将最小值放在开头
swap(&arr[maxi], &arr[right]);//将最大值放在序列尾部
}
else if (maxi == left) {
swap(&arr[maxi], &arr[right]);//将最大值放在序列尾部
swap(&arr[mini], &arr[left]);//将最小值放在开头
}
else {
swap(&arr[maxi], &arr[right]);//将最大值放在序列尾部
swap(&arr[mini], &arr[left]);//将最小值放在开头
}
left= left+1;
right= right-1;
}
}
int main()
{
int arr[] = { 47,2,45,12,6,24,6,9,13,33 };
int sz = sizeof(arr) / sizeof(arr[0]);
Select_sort(arr, sz);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
易错点:
代码段的3对if else是必不可少的,否则结果会出错下面我来解释一下。分为四种情况。
情况一:假设每一趟排序中找到的最大值和最小值的下标既不在序列开头,也不在序列末尾,而是处于两者之间,这个时候我们只需要分别把最小值,最大值和序列开头,序列结尾交换就OK。
情况二:假如在一趟排序中找到的最大值下标正好在序列开头,而最小值的下标介于开头和结尾之间,此时我们就不能简单按照情况一那样交换。比如我们代码中的第一趟排序,{47,45,12,6,24,6,9,13,33 };最大值下标在开头,假设按照情况一,我们先把6(最小值)和47(最大值)交换了,然而最大值下标还是转向开头位置,此时开头位置是6(最小值)而不是47(最大值),此时顺序就不会按照我们所要实现的那样进行。所以,我们不妨这样做:先交换最大值,后交换最小值。
情况三:假如在一趟排序中找到的最小值下标正好在序列结尾,而最小值的下标介于开头和结尾之间。此时和情况二大体情况类似,类比,我们可以先交换最小值,再交换最大值。
情况四:假设在一趟排序中找到的最大值恰好在开头位置,找到的最小值恰好在末尾位置,此时我们只需要交换最小值或者最大值一次就好。
这三对if else可以进一步代码优化,如下:
swap(&arr[mini], &arr[left]);//将最小值放在开头
if (left == maxi)//防止最大的数在开头位置被换走
{
maxi = mini;
}
swap(&arr[maxi], &arr[right]);//将最大值放在序列尾部
三.插入排序
1.基本思想
将整个数组a分为有序和无序的两个部分。前者在左边,后者在右边。开始有序的部分只有a[0] , 其余都属于无序的部分。每次取出无序部分的第一个(最左边)元素,把它加入有序部分。假设插入合适的位置p,则原p位置及其后面的有序部分元素都向右移动一个位置,有序的部分即增加了一个元素。一直做下去,直到无序的部分没有元素。
2.动图过程展示
3.代码示例
#include <stdio.h>
void Insert_sort(int arr[], int sz)
{
for (int i = 0; i < sz-1; i++)
{
int end = i;//记录有序序列最后一个元素的下标
int temp = arr[end + 1];//待插入的元素
while (end >= 0)//单趟排序
{
if (temp < arr[end])//有序表元素比插入的数大
{
arr[end + 1] = arr[end];//使大数往后移
end--;//从后往前检查比较有序表元素与待插入元素值的大小
}
else//比插入的数小,跳出循环
break;
}
//两种情况:(1)待插入元素比当前有序序列中的所有元素都小
//(2)待插入元素找到插入位置(break跳出循环至此)
arr[end + 1] = temp;
}
}
int main()
{
int arr[] = { 47,2,45,12,6,24,6,9,13,57,33 };
int sz = sizeof(arr) / sizeof(arr[0]);
Insert_sort(arr, sz);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
四.快速排序
1.基本思想
选取一个基准,一趟排序确定两个区间,一个区间全部比基准值小,另一个区间全部比基准值大,接着再选取一个基准值来进行排序,以此类推,最后得到一个有序的数列。
2.步骤
<1>首先设定一个分界值,通过该分界值将数组分成左右两部分。
<2>将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。
<3>然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
<4>重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。
3.动图过程展示
4.代码示例
#include <stdio.h>
void swap(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
void Quick_sort(int arr[],int left,int right)
{
if (left >= right)
{
return;
}
int i=left, j=right, base=arr[left], temp;
while (i < j)
{
while (arr[j] >= base && i < j)
{
j--;
}
while (arr[i] <= base && i < j)
{
i++;
}
if (i < j)
{
swap(&arr[i], &arr[j]);
}
}
arr[left] = arr[i];//基准数回归
arr[i] = base;
Quick_sort(arr,left,i-1);//递归左边
Quick_sort(arr,j+1 ,right );//递归右边
}
int main()
{
int arr[] = { 47,2,45,12,6,24,6,9,13,57,33 };
int sz = sizeof(arr) / sizeof(arr[0]);
Quick_sort(arr, 0,sz-1);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
五.时间复杂度和空间复杂度对比
完结撒花