1、冒泡排序
冒泡排序(Bubble Sort)是一种简单的排序算法,其基本思想是通过交换相邻两个数的位置,将当前序列中的最大值逐步“冒泡”到末尾。
冒泡排序的基本操作:依次比较两两相邻的元素的值,若为逆序,则交换它们的位置,直到数据序列排完为止;
代码实现:
void bubble(int A[], int len)
{
for (int i = 1; i < len; i++) //循环次数
{
int j = 0;
for (j = 0; j < len-i; j++) //数据之间比较的次数
{
if (A[j] < A[j+1]) //数据交换
{
int temp = A[j];
A[j] = A[j+1];
A[j+1] = temp;
}
}
}
}
int main()
{
int A[10] = { 14,6,7,23,75,12,98,43,64,57 };
bubble(A, 10);
for (int i = 0; i < 10; i++)
printf("%d ", A[i]);
return 0;
}
2、插入排序
概念:每次将一个待排序的数据按照其大小插入到已排好序的子序列中,直到全部数据插入成功;
算法思想:首先,使用temp保存第i个元素,然后,对数据进行比较,当temp小于i-1个元素时,第i-1个元素复制到第i个位置,temp继续与往左边的元素对比,直到temp大于第n个元素时,temp的数据放在第n+1个位置;(若temp是序列中最小的元素,则放在最开头位置)
代码实现:
void insert(int A[], int len)
{
int i, j, temp;
for (i = 1; i < len; i++) // A[0]为已排好序的序列
{
if (A[i] < A[i - 1]) //若A[i]小于前驱值
{
temp = A[i]; //用temp保存A[i]
for (j = i - 1; j >= 0 && A[j] > temp; j--) //检查排好序的序列
A[j + 1] = A[j]; // 若temp < A[j],则j元素往后移动
A[j + 1] = temp; //循环结束,temp插入序列中
}
}
}
int main()
{
int A[10] = { 6,75,8,32,65,99,43,16,45,28 };
insert(A, 10);
for (int i = 0; i < 10; i++)
printf("%d ", A[i]);
return 0;
}
优化思路:可以在查找插入位置时,使用折半查找来寻找插入位置;在查找到插入位置时,将插入位置后的序列往前进一位,空出位置给新数据插入;
折半查找代码:
void insert(int A[], int len ) //查找插入位置使用折半查找
{
for (int i = 1; i < len; i++)
{
if (A[i] < A[i - 1])
{
int temp = A[i];
int per = 0;
int last = i - 1;
int mid = (per + last) / 2;
while ( per <= last) //终止条件
{
mid = (per + last) / 2;
if (A[mid] > temp)
last = mid - 1;
else
per = mid + 1;
}
while (per <= (i - 1)) //插入位置找到后,per到(i-1)的元素往后移动
{
A[i] = A[i - 1];
i--;
}
A[per] = temp; //插入数据
}
}
}
3、希尔排序
希尔排序(Shell Sort)是一种插入排序的改进算法,也称为缩小增量排序。它通过将待排元素分成若干个小组,对每个小组进行插入排序,不断缩小组的大小,最终完成排序。希尔排序的核心在于定义增量序列,即分组的方式。在效率和稳定性之间取得了一个平衡,适用于面对大规模数据的排序。
希尔排序特征:先追求数据序列中元素的部分有序,再逐渐逼近全局有序;
算法思想:设置一个增量d,用来分隔数据序列,将相隔距离为d的元素看作一个子表,对各个子表分别进行直接插入排序,接着缩小增量d(最好每次缩小一半),重复上述过程,直到d=1为止;
注意:希尔排序只适用于顺序表,不适用于链表;
代码实现:
void shellsort(int A[], int n)
{
int temp, step, i, j;
for (step = n / 2; step > 0; step = step / 2) //步长变化
{
for (i = step; i < n; i++)
{
if (A[i] < A[i - step]) //比较
{
temp = A[i]; //将小值暂存在temp
for (j = i - step; j >= 0 && temp < A[j]; j -= step)
A[j + step] = A[j]; //记录后移,查找插入的位置
A[j + step] = temp; //插入
}
}
}
}
int main()
{
int A[10] = { 76,34,65,18,45,99,32,11,21,88 };
shellsort(A, 10);
for (int i = 0; i < 10; i++)
printf("%d ", A[i]);
return 0;
}
4、快速排序
快速排序是一种常用的排序算法,基本思路是选择一个元素作为pivot(枢轴),然后通过一趟排序将待排序列分为两部分,一部分比pivot小,一部分比pivot大。然后再对这两部分分别递归地进行快速排序,直到整个序列有序。
操作步骤:使用一个temp来存放pivot,使用双指针从序列的两端开始往中间扫描,当右端元素小于pivot,与左部分进行交换,左指针往右移动,反之,操作相反,直到左右指针同时指向同一个位置为止;
递归用来改变边界指针,对不同的序列进行划分;
代码实现:
int quicksort2(int A[], int low,int high)
{
int point = A[low]; //确定基准元素
while (low < high) // 退出循环条件
{
while (low < high && A[high] >= point) //从右边开始寻找比point值小的元素
high--;
A[low] = A[high]; //换到左边
while (low < high && A[low] <= point)//从左边开始寻找比point值大的元素
low++;
A[high] = A[low]; //换到右边
}
A[low] = point; //插入point值最终的位置
return low; //返回low坐标
}
void quicksort1(int A[], int low, int high)
{
if (low < high) //递归退出条件
{
int lowpoint = quicksort2(A, low, high); //对序列进行划分
quicksort1(A, low, lowpoint - 1); //左子表
quicksort1(A, lowpoint + 1, high); //右子表
}
}
int main()
{
int A[10] = { 44,23,75,34,98,21,58,65,91,11 };
int low = 0; int high = 9;
quicksort1(A, low, high);
for (int i = 0; i < 10; i++)
printf("%d ", A[i]);
return 0;
}
5、选择排序
选择排序(Selection Sort)是一种简单直观的排序算法。其基本思想是在未排序的数列中,每次选择最小(或最大)的数,放置到数列的起始位置,然后再在剩余未排序的数列中选择最小(或最大)的数,放置到已排序数列的末尾。重复这样的操作,直到整个数列有序。选择排序算法的时间复杂度稳定在 O(n^2),因为每个元素都要比较多次。尽管如此,选择排序算法也是一种简单易懂、易于实现的排序算法。
5.1、简单选择排序
代码实现:
void choosesort(int A[], int n)
{
int i, j;
for ( i = 0; i < n - 1; i++) //遍历次数
{
int min = i;
for ( j = i+1; j < n; j++) //遍历元素个数
{
if (A[j] < A[min]) //寻找最小值
min = j;
}
if (min != i) //下标不同则交换
{
int temp = A[min];
A[min] = A[i];
A[i] = temp;
}
}
}
int main()
{
int A[10] = { 23,45,76,43,23,11,98,87,66,34 };
choosesort(A, 10);
for (int i = 0; i < 10; i++)
printf("%d ", A[i]);
return 0;
}
5.2、堆排序
堆排序本身是一种完全二叉树,可以理解为一棵顺序存储的完全二叉树;
分为两种关键字序列:
1、根节点都大于或等于其左右孩子节点的值的树——大根堆;
2、根节点都小于或等于其左右孩子节点的值的树——小根堆;
堆排序的重要参数(i为节点的数组下标)
i的左孩子——2i;
i的右孩子——2i+1;
判断i是否为分支节点/叶节点——i>(n/2)+1;
构建大根堆思路:把所有根节点检查一遍,是否满足大根堆的要求,若不满足,则将当前节点与其左右孩子节点的最大值交换位置,若元素互换破环了下一级堆,则采用相同的方法继续往下调整;
排序思路:首先将待排序的数组构造成一个大根堆,整个数组的最大值为数组首位数,接着将顶端的数与数组末尾的数交换,此时,末尾的数变为最大值,待排序的数组个数减一,继续将剩余的数构建成大根堆,然后交换,重复上述操作,便能得到有序数组;
注意:升序用大根堆,降序用小根堆;
该代码从数组下标为1处开始,下标为0的空间被弃用;
代码实现:
void headAdjust(int A[], int k, int len) //以k为根节点的树调整为大根堆
{
int temp = A[k]; //存放根节点
for (int i = 2 * k; i < len; i = i * 2)
{
if (i < len && A[i] < A[i + 1]) //寻找根节点的左右孩的最大值
i++;
if (temp >= A[i]) //根节点大于左右孩,则表示是大根堆
break;
else //否则—>进行替换
{
A[k] = A[i];
k = i; //修改k值,继续向下的树检查
}
for (int j = 1; j <= 10; j++)
printf("%d ", A[j]);
printf("\n");
}
A[k] = temp; //检查完毕,把值放入最终位置
}
void Buildhead(int A[], int len)
{
for (int i = len / 2; i > 0; i--) //从后往前调整每个根节点
{
headAdjust(A, i, len);
}
}
void headsort(int A[], int len)
{
Buildhead(A, len); //初始化大根堆
for (int i = len; i > 1; i--)
{
int temp = A[i];
A[i] = A[1];
A[1] = temp; //堆顶元素与堆底元素交换
for (int j = 1; j <= 10; j++)
printf("%d ", A[j]);
printf("\n");
headAdjust(A, 1, i - 1); //把剩余的元素整理为大根堆
}
}
int main()
{
int n = 10;
int i;
int a[11];
for (i = 1; i <= n; i++)
scanf_s("%d", &a[i]);
headsort(a, n);
for (int j = 1; j <= n; j++)
printf("%d ", a[j]);
return 0;
}
6、归并排序
归并排序是一种基于分治法的排序算法。它的基本思想是将待排序的元素序列分成若干个子序列,每个子序列都是有序的,然后将这些子序列合并成一个有序的序列。具体实现时,采用递归的方式将序列分成两半,然后将两个有序子序列合并成一个有序序列,最后得到完整的有序序列。
代码实现:
int arr[10]; //创建额外空间来存储
void Merge(int A[], int low, int mid, int high)
{
int i, j, k;
for (k = low; k <= high; k++) //将数据序列复制到arr数组
arr[k] = A[k];
for (i = low, j = mid + 1, k = i; i <= mid && j <= high; k++) //i,j指向arr数组的两个子数组的开头,k指向A数组
{
if (arr[i] <= arr[j]) //数据比较
A[k] = arr[i++];
else
A[k] = arr[j++];
} //某个子数组为空,另一个子数组不为空,将剩余数据填入A数组;
while (i <= mid)
A[k++] = arr[i++];
while (j <= high)
A[k++] = arr[j++];
}
void Mergesort(int 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); //进行归并
}
}
int main()
{
int A[10] = { 23,12,43,30,99,84,76,66,54,4 };
Mergesort(A, 0, 9);
for (int i = 0; i < 10; i++)
printf("%d ", A[i]);
return 0;
}