交换类
#冒泡排序
细节:每趟排序完之后都少比较一个元素
//冒泡排序
void BubbleSort(int *B,int len)
{
int temp;
//总趟数
for (int i = 0; i < len - 1; ++i) {
for (int j = len - 1; j > i ; --j) {
if(B[j-1] > B[j])
{
temp = B[j];
B[j] = B[j - 1];
B[j -1] = temp;
}
}
}
}
#快速排序
利用了分治思想,取数组第一个值为分隔值,将比分隔值小的放在左边,比分隔值大的放在右边,最后分割出来两组数组,将前一个数组的尾指针设为分隔值-1,将后一个数组的头指针设为分隔值+1,再次进行递归操作,直至数组中只剩下一个元素的时候递归结束。每次在找分隔值位置的时候,每次先拿一个分隔值变量将第一个元素存储起来,尾指针从最后一个位置开始与分隔值比对,当大于分隔值的时候在位置上不动,当小于分隔值的时候覆盖low指针所在位置值,然后再从尾指针开始比对,大于分隔值覆盖high指针当前位置值,小于分隔值尾指针向后移,最后当头指针与尾指针相遇的时候循环结束。
//具体怎么实现的 挖坑法
int paritition(int *B,int low,int high)
{
int pivot = B[low];
while (low < high)
{
while (low < high && B[high] >= pivot) //细节 一定带上等于 因为如果两个值相等的话放在哪里都无所谓的
{
high--; //如果大于放右边
}
B[low] = B[high]; //如果找到小于分隔值 放左边,覆盖掉low的值
while (low < high && B[low] <= pivot)
{
low ++;
}
B[high] = B[low]; //大于放右边 覆盖掉刚刚小于分隔值的值
}
//相遇之后 将分隔值放到此位置
B[low] = pivot;
return low;
}
//快速排序
void QuickSort(int *B,int low,int high)
{
if(low < high) // 因为递归中的low和high是用分隔值位置表示的,因此当只剩下最后一个元素的时候,递归函数的low就与high相等了
// 如果只剩下一个元素的时候 就不需要再进行递归
{
int PovitPos = paritition(B,low,high); // 记录每次递归存储的分隔值的位置
QuickSort(B,low,PovitPos - 1 ); // 左边一直递归分割
QuickSort(B,PovitPos +1 ,high);
}
}
插入类
#插入排序
原理:将序列分为有序序列以及要插入的数的序列,设置一个InsertVal值来存储要插入的数方便在排序时前面的数覆盖后面的数,外层循环控制要插入的数,内存循环控制有序序列从后往前与插入数进行比较,当有序序列的数大于需要插入的数,该位置的数向后覆盖,然后继续往前找直到找到比插入数小的数或者有序数列到头为止结束内层循环,因为内层循环控制的是有序序列,所以判断有序数列数与插入数大小的条件可以写在内层循环中,这样在找到的小的数的时候,这个位置的数前面的数就不用再去比较了,节省了时间。又因为每次有序数列都是从最后一个数开始比较,这个数每次都在插入数位置后一个位置,所以内层循环条件为 j = i-1;
小细节:因为循环结束之后有序序列位置又向前移一位,最后找到插入的数的位置需要加1。
实现:
//插入排序
void InsertSort(SSTable ST)
{
int j ;
int InsertVal;
for (int i = 1; i < ST.STbaleLen; ++i) { // 外层控制插入序列的数
InsertVal = ST.elem[i];
for ( j = i - 1 ; j >= 0 && ST.elem[j] > InsertVal ; --j) { // 内层控制有序数列 从后往前比较 找到插入值的位置
ST.elem[j +1 ] = ST.elem[j];
}
ST.elem[j + 1] = InsertVal;
}
}
选择类
#选择排序
原理:假设表为【0....n】,通过循环找出最小值排在前面,也就是【i.....n】i初始值为0随着每次找到最小值之后再加1直到到表下标前一位,就可以按升序排序。实际上原理与冒泡排序原理相同,只不过选择排序是在一轮比较后找到最小值下标再进行交换,而冒牌排序是每次比较后都交换。
//
// Created by Administrator on 2023/2/3.
//
#include "stdio.h"
//选择排序
void SelectionSort(int *B,int len)
{
int i,j,min,temp; //定义最小值
for (i = 0; i < len -1 ; ++i) { //与冒泡排序循环次数相同 只不过区别为选择排序内存循环不交换
min = i; // 设开头第一位为最小值
for ( j = i+1; j < len ; ++j) {
if( B[j] < B[min] )
{
min = j;
}
}
if(min != i)
{
temp = B[i];
B[i] = B[min];
B[min] = temp;
}
}
}
void PrintArr3(int *B,int len)
{
for (int i = 0; i < len; ++i) {
printf("%3d",B[i]);
}
}
int main()
{
int B[10] = {62,94,25,79,69,84,22,22,12,78};
int len = 10;
SelectionSort(B,len);
PrintArr3(B,len);
return 0;
}
#堆排序
而利用堆排序将数组升序排序其实就是利用了大根堆的特性,堆排序其实就分为两步,第一步将数组对应的树化为大根堆,第二步其实就是给无序数列排序,因为根据大根堆的特性,当你将该数组转换成大根堆之后,他的根节点一定是无序数列中的最大数,并且所有子树都为大根堆,这个时候就可以将数组中最后一个值与化成大根堆的根节点互换位置,这样每次化成大根堆的无序数列中的最大值都会放在无序数列的最后一位,并且在换完位置之后的数列组成的树,只有根节点这颗子树不是大根堆,这样我们只需每次讲根节点这个子树转换为大根堆那么他的整体也会是一个大根堆,在无序数列中依次排到最后两个元素,这样最后排出来的数组就是一个升序的数组。
//
// Created by Administrator on 2023/2/3.
//
#include "stdio.h"
void swap(int &a,int &b)
{
int temp;
temp = a;
a = b;
b = temp;
}
void AdjustDown(int r,int *A,int len)
{
int temp;
int dad = r;
int son = (dad*2)+1; //左子树为 dad*2+1
while ( son < len)//当为根叶子结点的时候就说明所有的子树都为大根树了
{
if(son+1 < len &&A[son+1] > A[son]) //找到左孩子 如果左孩子比右孩子小 那么直接用右孩子来代替父节点 (用两个中间大的代替父节点那么这颗子树一定是大根树
{
son = son+1;
}
if(A[son] > A[dad]) //查看是否为大根树
{
swap(A[son],A[dad]);//交换子结点与父结点
dad = son; //如果交换后另一颗子树变成非大根堆了
那要对这颗改变的子树再转换成大根堆
son = (2*dad)+1;
} else{
break;
}
}
}
void HeapSort(int *A,int len)
{
int i;
//先把堆转为大根堆
for (i = (len/2)-1 ; i >= 0 ; --i) {//最后一棵子树的根节点为 n/2 - 1
AdjustDown(i,A,len);//把每一颗子树转换为大根堆
}
//转换为大根堆后,每次都将大根堆的根节点取出,因为此时大根堆的根节点一定是最大值。
swap(A[len-1],A[0]);
for (int j = len - 1; j > 1; --j) { // 此时的j为剩下无序数组的长度
j>1:无序数列中剩最后两个元素的时候是最后一次排列
AdjustDown(0,A,j);//此时只有第一颗子树不为大根树,因此只需要调整第一颗子树就可以让剩下的无序元素变成大根堆
swap(A[j - 1],A[0]);
}
}
void PrintArr4(int *A,int len)
{
for (int i = 0; i < len; ++i) {
printf("%3d",A[i]);
}
}
int main()
{
int A[10] = {3,87,2,93,78,56,61,38,12,40};
int len = 10;
HeapSort(A,len);
PrintArr4(A,len - 1);
return 0;
}
归并类
#归并排序
操作与快速排序相似,都是分治法思想通过递归切割元素,只不过归并排序是前半段与后半段没有分割元素在中间。当通过递归将元素分割为以自身为单位的时候,就可以开始进行合并了,这里的合并操作就是前面的线性表合并,创建一个相同大小的表,将分割后的两个表存入这个表中,这个时候这两个表都是有序的,因此可以创建两个游标分别指向分割后进行比对的两个表的第一个元素,开始比对,小的值放入原数组然后游标向后移,再与另一个表的元素继续进行比对,重复此操作。
//
// Created by Administrator on 2023/2/4.
//
#include <stdio.h>
#define N 7
void Merge(int *A, int low, int mid, int high) {
static int B[N];
int i,j,k;
for (i = low; i <= high; ++i) {
B[i] = A[i];
}
k = low;
for (i = low,j = mid +1; i <= mid &&j<=high ;) {
if(B[i] < B[j])
{
A[k] = B[i];
i++;
k++;
} else{
A[k] = B[j];
j++;
k++;
}
}
while (i <= mid)
{
A[k] = B[i];
i++;
k++;
}
while (j<=high)
{
A[k] = B[j];
j++;
k++;
}
}
void MergeSort(int *A, int low, int high)
{
if(low < high)
{
int mid = (high+low)/2;
MergeSort(A,low,mid); // 左边递归分割成单个元素
MergeSort(A,mid+1,high); // 右边递归分割
Merge(A,low,mid,high);
}
}
void PrintArr6(int *B,int len)
{
for (int i = 0; i < len; ++i) {
printf("%3d",B[i]);
}
}
int main()
{
int A[7] = {49,38,65,97,76,13,27};
int len = 7;
int low = 0;
int high = len-1;
MergeSort(A,low,high);
PrintArr6(A,len);
return 0;
}