目录
一、冒泡排序
规则:
n 个数据进行冒泡排序,首先将第一个数据和第二个数据进行比较,如果为逆序就交 换两个数据的值,然后比较第二个和第三个数据,依此类推,直到第最后一个和倒数第二个比较完了为 止。上述过程为冒泡排序的第一趟冒泡排序,其结果是最大或者最小的数据被放置在末尾的位置。然后 进行第二趟排序,把第二大或者第二小的数放置在倒数第二的位置,之后每一趟排序都会使一个数据有 序,直到此序列的全部数据都有序为止。注意:
每一次比较都把最大的放到最后面。
代码:
void bubble_sort(int x[], int n)
{
for (int i = 0; i < n-1; i++)
{
for (int j = 0; j < n - 1-i; j++) //每一轮比较都把最大的数放到最后,然后下一轮一直排到最大的数之前(这时,已经排列出来的每轮最大的数的个数为i)。
{
if (x[j] > x[j + 1])
{
swap(x[j], x[j + 1]);
}
}
}
}
二、选择排序
规则:
对一个序列进行选择排序,首先通过一轮循环比较,从 n 个数据中找出最大或者最小的那个数据的位 置,然后按照递增或者递减的顺序,将此数据与第一个或最后一个数据进行交换。然后再找第二大或者 第二小的数据进行交换,以此类推,直到序列全部有序为止。
代码:
void select_sort(int x[], int n)
{
int min;
for (int i = 0; i < n; i++)
{
min = i;
for (int j = i; j < n; j++)
{
if (x[min] > x[j])
min = j;
}
swap(x[i], x[min]);
}
}
三、插入排序
规则:
- 基本思想:将一个记录插入到已经排好序的有序表中,从而产生一个新的、记录数增1的有序表。
- 实现过程:双循环;
- 外层循环对除了第一个元素以外的所有元素;
- 内层循环对当前元素前面有序表进行待插入位置查找,并进行移动。
一共n个元素,需要插入的元素n-1个
代码:
void insert_sort(int arr[], int len)
{
int temp;
int j;
for (int i = 1; i < len; i++)
{
temp = arr[i];//保存待插入的值,
j = i - 1;//将要插入的数与它前面的数分别比较
while (j >= 0 && temp < arr[j])
{
arr[j + 1] = arr[j];//取出(待插入)的那个数位置空,它前面的数顶替进来,前面所有大于它的数挨个往后面挪,直到第一个小于取出来的数为止。
--j;
}
arr[j + 1] = temp;
}
}
四、希尔排序
规则:
把记录按下标的一定增量分组,对每组使用直接插入排序算法排序:随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
每一组同一个位置相比较,相交换。靠前组的数据比后面的数据小。
插入排序的优化。
插入排序是所有数跟它前面的数比较,希尔排序是每个数跟它前面分组中的相应同一位置的数相比较。
代码:
void shell_sort(int x[], int n)
{
int temp;
int j;
int jump = n >> 1;//n/2
while (jump != 0)
{
for (int i = jump; i < n; i++)
{
temp = x[i];
j = i - jump;
while (j >= 0 && temp < x[j])
{
x[j + jump] = x[j];//每个数组同一位置的数相比较。取出(待插入)的那个数位置空,它前面每隔jump的数顶替进来,前面所有大于待插数的数挨个往后面挪,直到第一个小于待插数的数为止。
j = j - jump;
}
x[j + jump] = temp;
}
jump >>= 1; //jump=jump/2
}
}
五、快速排序
规则:
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
代码:
void quick_sort(int arr[], int low, int hight)
{
int t = arr[low];//标杆。
int f = low + 1;
int b = hight;
if (low >= hight) return;
while (f <= b)
{
while (f <= b && arr[f] <= t) f++;//遇到大于t的数停止移动。
while (f <= b && arr[b] >= t) b--;//遇到小于t的数停止移动。
//都停止移动,交换标记数。
if (f < b)
{
swap(arr[f], arr[b]); //交换arr[f],arr[b]使arr[f]<t.arr[b]>t
f++;//f继续向右找小于t的数。
b--;//b继续向左找大于t的数。
}
}
//f>b的时候跳出循环,这时arr[b]<arr[f]
swap(arr[low], arr[b]);
//对两部分进行递归操作。
quick_sort(arr, low, b - 1);
quick_sort(arr, b + 1, hight);
}
六、基数排序(桶排序):
规则:
基数排序又称为"桶子法”,从低位开始将待排序的数按照这一位的值放到相应的编号为0~9的桶中。等到低位排完得到一个子序列,再将这个序列按照次低位的大小进入相应的桶中,一直排到最高位为止,数组排序完成。
基数排序法是属于稳定性的排序。
代码:
void radix_sort(int arr[], size_t len)
{
int temp[10][10] = {}; //第二个位置的数字取决于数组大小。
for (int n = 1; n < 10000; n *= 10)//这个10000由最高位数决定
{
memset(temp, -1, sizeof(temp));//每次用完把桶子洗干净,初始化。(初始化为桶里不可能含有的数,0有可能存在于桶里。)
for (size_t i = 0; i < len; ++i)
{
int tempIndex = (arr[i] / n) % 10;//从个位开始每一位的数。
temp[tempIndex][i] = arr[i];//元素入桶。
}
int k = 0;
//元素出桶。
for (int x = 0; x < 10; ++x)
{
for (int y = 0; y < 10; ++y)
{
if (temp[x][y] != -1)
arr[k++] = temp[x][y];
}
}
}
}
七、计数排序
规则:
开一个初始化为0的内存较大的数组,把要排序的数组里 所有数对应到大数组的相应下标,大数组对应位置的数+1。然后再遍历的大数组。
代码:
int cnt[100000];
void count_sort(int x[], int len)
{
memset(cnt, 0, sizeof(cnt));//把大数组初始化为0
for (int i = 0; i < len; i++)
++cnt[x[i]];//把要排序的数组里 所有数对应到大数组的相应下标,大数组对应位置的数+1
int top = 0;
for (int i = 0; i < 100000; i++)//遍历大数组
{
while (cnt[i])
{
x[top++] = i;
--cnt[i];
}
if (top == len)
break;
}
}
八、归并排序
规则:
归并排序是利用归并的思想实现的排序方法,该算法采用经典的分治策略。(分治法将问题分成一些 小的问题然后递归求解,而治的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之。)将已有序的子序列合并,得到完全有序的序列; 即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
归并排序是一种稳定的排序方法。
图源:麦克老师讲算法
代码:
void _merge(int x[], int left, int right);//递归的声明
void _unite(int x[], int left, int mid, int right);//合并的声明
void merge_sort(int x[], int n)//算法入口函数
{
//先递归。
_merge(x, 0,n-1 );
}
void _merge(int x[], int left, int right)//递归操作。
{
//递归结束条件 有穷性。 元素只有一个了。
if (left >= right)
return;
int mid = ((right - left) >> 1)+left;//确定中间下标。 >>1的意思是除以2
_merge(x, left, mid);//递归左区间
_merge(x, mid + 1, right);//递归右区间
_unite(x, left, mid, right);
}
void _unite(int x[], int left,int mid, int right)
{
//每次合并数组长度要求不一致。
int length=right-left+1;//数组长度
int* pData = new int[length];//辅助数组
memset(pData, 0, sizeof(int)*length);//初始化数组
//游标。分别指向两个需要合并的数组以及辅助数组
int low = left;//左区间
int hig = mid + 1;//右区间
int index = 0;
//合并
while (low<=mid &&hig<=right)//两边区间还存在元素
{ //*****实现排序。
//左区间存在元素并且当前元素比右区间小。
while (low <= mid && x[low] <= x[hig]) //允许相等,稳定性。当左边等于右边的时候,左边的还在左边。
pData[index++] = x[low++]; //while里low<=mid,因为这个循环里面的low值是变化的,不能只在外层while循环里设定low<=mid条件。
//右区间存在元素并且当前元素比左区间大。
while (hig<=right && x[low] > x[hig])
pData[index++] = x[hig++];
}
//出循环了,至少有一个区间出完了。
//剩下区间的元素直接拷贝。
if (low <= mid)//说明左区间还没有出完
memmove(&pData[index],&x[low] ,sizeof(int)*(mid-low+1));//memmove(去的地方,来的地方,取出的长度)
if (hig <= right)//说明右区间没有出完
memmove(&pData[index], &x[hig], sizeof(int)*(right-hig+ 1));
memmove(&x[left], &pData[0],sizeof(int)*length);//最后一步 把辅助数组里面的数据覆盖给原数组 还回去 清空辅助数组
delete[] pData;
}