冒泡排序,从前往后就是把最大的冒泡到最后面,缩小区间,或者从最后面开始,每次把最小的冒泡到最前面
打印数组的值
#include
#include
void PrintAr(int* ar, int n)
{
assert(ar != nullptr);
for (int i = 0; i < n; ++i)
{
printf("%5d", ar[i]);
}
printf("\n");
}
交换函数swap
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
这个是从后向前遍历,每次把最小的放到最前面
注意越界条件,j的取值,j+1 时候是否会越界
void bubblesort1(int* ar, int n) {
assert(ar != nullptr);
if (n <= 1)return;
int i, j;
for (int i = n - 1; i >= 0; i--) {
for (int j = n - 2; j >= n - i - 1; j--) {
if (ar[j] > ar[j + 1]) {
swap(&ar[j], &ar[j + 1]);
}
}
}
}
这个是从前往后遍历,每次把最大的放到最后面
注意越界条件,j的取值,j+1 时候会越界
void bubblesort2(int* ar, int n) {
assert(ar != nullptr);
if (n <= 1)return;
int i, j;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n - i-1; j++) {
if (ar[j] > ar[j + 1]) {
swap(&ar[j], &ar[j + 1]);
}
}
}
}
这个是从后向前遍历,每次把最小的放到最前面,注意越界条件,j的取值,j+1 时候是否会越界,假如排了几次,发现,已经有序了,就不用再继续排了,直接跳出就行
void bubblesort1plus(int* ar, int n) {
assert(ar != nullptr);
if (n <= 1)return;
int i, j;
for (int i = n - 1; i >= 0; i--) {
bool flag = false;
for (int j = n - 2; j >= n - i - 1; j--) {
if (ar[j] > ar[j + 1]) {
swap(&ar[j], &ar[j + 1]);
flag = true;
}
}
if (flag == false)break;
}
}
选择排序,就是每次在指定的区间选择最小的元素,放到最前面
先判断指针是否为空,然后判断如果n<=1 则说明一个数据或者没有数据
则不用排序,直接返回,
外层循环只用从0开始遍历到n-2位置,因为最后一个数不用比较,一定是最大的,
如果i本身就是minpos,则不用交换
void SelectSort(int* ar, int n)
{
assert(ar != nullptr);
if (n <= 1) return;
for (int i = 0; i < n - 1; ++i)
{
int minpos = i;
for (int j = i + 1; j < n; ++j)
{
if (ar[minpos] > ar[j])
{
minpos = j;
}
}
if (i != minpos)
{
Swap(&ar[minpos], &ar[i]);
}
}
}
插入排序,就是遍历每一个数据,将其插入到合适的位置,类似最大堆搜索函数的实现
先判断指针是否为空,然后判断如果n<=1 则说明一个数据或者没有数据
//则不用排序,直接返回
//从第二个数据开始,看是否需要向前插入,
//如果大圩前面的数,则直接遍历到第三个数据
//如果小于前面一个数,则需要向前插入,
//插入步骤是,现将该数提取到temp,前面的数下标为j,将前面的数移动到该位置,即ar[j+1]=ar[j] 然后j–;
// 看temp是否能插入到前面的一个位置,如果该temp大于签的的数
// 则可以插入到j+1位置,否则,继续将j位置到j+1位置
//循环的结束条件是temp>ar[j] 和j==-1 则将temp放在j+1位置
void InsertSort(int* ar, int n)
{
assert(ar != nullptr);
if (n <= 1) return;
for (int i = 1; i < n; ++i)
{
PrintAr(ar, n);
if (ar[i - 1] > ar[i])
{
int j = i - 1;
int tmp = ar[j + 1];
do
{
ar[j + 1] = ar[j];
j = j - 1;
} while (j >= 0 && ar[j] > tmp);
ar[j + 1] = tmp;
}
}
}
堆排序的思想就是先将待排序的序列建成大根堆,使得每个父节点的元素大于等于它的子节点。此时整个序列最大值即为堆顶元素,我们将其与末尾元素交换,使末尾元素为最大值,然后再调整堆顶元素使得剩下的 n−1 个元素仍为大根堆,再重复执行以上操作我们即能得到一个有序的序列
归并排序利用了分治的思想来对序列进行排序。对一个长为 n 的待排序的序列,我们将其分解成两个长度为n/2的子序列。每次先递归调用函数使两个子序列有序,然后我们再线性合并两个有序的子序列使整个序列有序
void PrintAr(int* ar, int n)
{
assert(ar != nullptr);
for (int i = 0; i < n; ++i)
{
printf("%5d", ar[i]);
}
printf("\n");
}
//类似将两个有序的数组合并成一个有序的数组,
//一个数组,pos左边的有序,pos右边的也有序
void MergeAr(int* dest, int* src, int left, int pos, int right) {
assert(dest != nullptr && src != nullptr);
int i = left, j = pos + 1;
int k = left;
while (i <= pos && j <= right) {
dest[k++] = src[i] < src[j] ? src[i++] : src[j++];
}
while (i <= pos) {
dest[k++] = src[i++];
}
while (j<= right) {
dest[k++] = src[j++];
}
}
void CopyAr(int* dest, int* src, int left, int right) {
assert(dest != nullptr && src != nullptr);
for (int i = left; i <= right; i++) {
dest[i] = src[i];
}
}
//int m = left + (right - left) / 2; 不能是left + (right - left+1) / 2;
//每次到(0,1) passsort(ar, br, 0, 1); 会陷入死递归,出不来栈溢出
//55 45 33 22 11 0
//0 1 2 3 4 5
//
//m=2
//55 45 33 | 22 11 0
//0 1 2 | 3 4 5
//m=1
//55 45 | 33 | 22 11 0
//0 1 | 2 | 3 4 5
//m=0
//55 | 45 | 33 | 22 11 0
//0 | 1 | 2 | 3 4 5
//合并 使其有序
//55 | 45
//0 | 1
//45 | 55
//0 | 1
//下一次合并
//45 | 55 | 33
//0 | 1 | 2
//33 | 45 | 55
//0 | 1 | 2
//if (left < right) 不要写成 while(left
//递归到只有一个数据的时候,退出,合并这两个数据,每一个数据自己是有序的,‘
//所以每次递归的底部是,一个数据,即left==right
void passsort(int* br, int* ar, int left, int right) {
assert(ar != nullptr && br != nullptr);
if (left < right) {
int m = left + (right - left) / 2;
passsort(br, ar, left, m);
passsort(br, ar, m + 1, right);
MergeAr(br, ar, left, m, right);
CopyAr(ar, br, left, right);
PrintAr(ar, 10);
}
}
//归并排序
//开辟br存储归并后的ar
void mergesort(int* ar, int n) {
assert (ar != nullptr);
if (n <= 1)return;
int* br = (int*)malloc(sizeof(int) * n);
if (br == nullptr)exit(EXIT_FAILURE);
passsort(br, ar, 0, n - 1);
free(br);
br = nullptr;
}
int main()
{
int ar[] = { 78,12,23,90,100,34,89,45,56,67 };
int n = sizeof(ar) / sizeof(ar[0]);
PrintAr(ar, n);
mergesort(ar, n);
PrintAr(ar, n);
return 0;
}
快速排序的主要思想是通过划分将待排序的序列分成前后两部分,其中前一部分的数据都比后一部分的数据要小,然后再递归调用函数对两部分的序列分别进行快速排序,以此使整个序列达到有序
核心是分治算法的体现
partitiaon函数算法要点
//将ar数组的数分成两部分,左边的部分是都是<=ar[i],右边的都是>ar[i]
//大致思路是,在右边开始遍历,找比temp小的数,找到后直接赋值给i下标
// 从左边遍历,找比temp大的数,找到后直接赋值给j下标
// while (i
int Parition(int* ar, int left, int right) {
assert(ar != nullptr);
int i = left, j = right;
int temp = ar[i];
while (i < j) {
while (i<j && ar[j]>temp)j--;
if(i<j)ar[i] = ar[j];
while (i < j && ar[i] <= temp)i++;
if(i<j)ar[j] = ar[i];
}
ar[i] = temp;
return i;
}
//不断地缩小区间,每次看pos,pos左边都小于等于ar[pos]
//右边都大于ar[pos] 就算是两个数,也要排序,所以,当left==right
//说明这一边只有一个数了,就不用再递归排序了
void Qsort(int* ar, int left, int right) {
assert(ar != nullptr);
if (left < right) {
int pos = Parition(ar, left, right);
Qsort(ar, left, pos - 1);
Qsort(ar, pos + 1, right);
}
}
void quicksort1(int* ar, int n) {
assert(ar != nullptr);
Qsort(ar, 0, n - 1);
}
//非递归的排序算法 快排 需要用到队列,即链式队列,先进先出
//队列为空时说明排序完成,退出
//队列不为空时,就出队,将其当做一个区间,算出中间的pos,然后再将两个区间入队
//如果区间为pos + 1 < right) 即右边只有一个数据,就不要继续排序,不用入队,//pos - 1 > left) pos位置是,它左边都小于等于他自己,如果pos - 1 = left,左边只有一个数,不用入队
void quicksort2(int* ar, int n) {
assert(ar != nullptr);
if (n <= 1)return;
LinkQueue myQ;
InitQueue(&myQ);
Push(&myQ, 0);
Push(&myQ, n - 1);
int left, right;
while (1) {
if (IsEmpty(&myQ))break;
Pop(&myQ, &left);
if (IsEmpty(&myQ))break;
Pop(&myQ, &right);
int pos = Parition(ar, left, right);
if (pos - 1 > left) {
Push(&myQ, left);
Push(&myQ, pos);
}
if (pos + 1 < right) {
Push(&myQ, pos + 1);
Push(&myQ, right);
}
}
DestroyQueue(&myQ);
}