#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
//1.插入排序 复杂度为n^2
int ch[10000];
void charusort(int* ch,int n)//排的升序
{
for (int i = 1; i < n; i++)
{
int tmp = ch[i];
int end = i - 1;
for (; end >= 0;)//将i前面所有比ch[i]大的数据向后移动一个单位,然后将ch[i]放到最后一个移动的1空格处
{
if (tmp < ch[end])
{
ch[end + 1] = ch[end];
end--;
}
else
break;
}
ch[end + 1] = tmp;
}
}
//2.希尔排序 复杂度为n^1.3
//预排序可以减少后面直接插入排序的复杂度
void shellsort(int* ch, int n)
{
for (int gap = n; gap > 1;)//gap是步长。逐步减少为1,前面步长大的是在预排序,可以有效地减少后面排序的交换次数
{
gap = gap / 3 + 1;
for (int i = 0; i < n - gap; i++)//下面就是以步长为gap来进行的插入排序
{
int end = i;
int tmp = ch[end + gap];
while (end >= 0)
{
if (ch[end] < tmp)
break;
else
{
ch[end + gap] = ch[end];
end -= gap;
}
}
ch[end + gap] = tmp;
}
}
}
//3.选择排序:思想就是找到每一组数据中的最小值,排在前面,依次这样下去
void xuanzesort(int* ch,int n)
{
for (int i = 0; i < n - 1;)
{
int posmin = i;
int posmax = i;
for (int k = i+1; k < n; k++)
{
if (ch[k] < ch[posmin])//找到最小的
posmin = k;
if (ch[k] > ch[posmax])//找到最大的
posmax = k;
}
int tmp = ch[posmin];
ch[posmin] = ch[i];
ch[i] = tmp;
if (posmax == i)//防止第一个数据就是最大的数据导致小的交换时改变最大数据的位置
posmax = posmin;
tmp = ch[n - 1];
ch[n - 1] = ch[posmax];
ch[posmax] = tmp;
n--;//这里最后一个数据是最大的,已经排成了有序的
i++;//i++是因为前面的第一个数据已经排成了有序
}
}
//4.霍尔快速排序
void quicksort(int* ch, int left, int right)
{
//用left和right来找基准值
//左子序列就是left到mid-1;
//右子序列就是mid+1到right
if (left < right)
{
int basepos = left;//基准值最初的位置,这个位置可以随便取,相应的后面的代码需要修改
int base = ch[basepos];//基准值
int posleft = left;//记住原始的左右边界
int posright = right;
left++;
while (left <= right)
{
while (ch[left] < base && left <= right)
left++;
while (left<=right&&ch[right] > base)
right--;
if (right >= left)//这个地方必须有=因为要让right减少
{
int tmp = ch[left];
ch[left] = ch[right];
ch[right] = tmp;
right--;
left++;//这个left可以不变化,但是按逻辑来说变化更好
}
}//到这right中的数据是小于基准的
ch[basepos] = ch[right];
ch[right] = base;
basepos = right;
quicksort(ch, posleft, basepos-1);
quicksort(ch, basepos+1, posright);//!!!注意这里是利用二叉树的形式进行递归,空间复杂度为logN,时间复杂度时N*logN
//注意堆排序的复杂度也是基于二叉树计算的,N*logN
}
else
return;
}
//5.挖坑法快速排序,快排的思想
void quickhole(int* ch, int left, int right)
{
if (left < right)
{
int holepos = left;
int key = ch[left];
int leftpos = left;
int rightpos = right;
left++;
while (left <= right)//每次必须先填坑小的,再填坑大的
{
while (left <= right)//注意这个是有顺序的,先从右向左找小的数,再从左向右找大的数。找不到小的数就不会去填坑
{
if (ch[right] <= key)
{
ch[holepos] = ch[right];
holepos = right;
right--;
break;//找到一个小的值,然后取填前面的坑。如果没有找到小的值,那么right会走到left的左边,
//因为left左边包括left都是小于等于基准值的,所以可以实现交换后左数组小,右数组大的效果
}
right--;
}
while (left <= right)
{
if (ch[left] >= key)
{
ch[holepos] = ch[left];
holepos = left;
left++;
break;
}
left++;
}
}
ch[holepos] = key;
quickhole(ch, leftpos, holepos - 1);
quickhole(ch, holepos + 1, rightpos);
}
else
return;
}
//6.lomuto前后指针,快排的的思想
void lomutosort(int* ch, int left,int right)
{
if (left < right)
{
int prev = left;
int cur = left + 1;
int key = ch[left];
//这里解释一下:就是这种算法会使(除去基准值的位置)prev跟cur之间的数字都是大于等于基准值,而prev之前的数据都是小于基准值的,
//cur从左到右遍历数组,从而实现了prev(包括prev)左边都是小于等于基准值的,而右边都是大于等于基准值的,所以只要将基准值的
//位置数据跟prev交换就可以实现二叉树的结构
while (cur <= right)
{
if (ch[cur] < key&&++prev!=cur)//如果小了,prev先加1再交换,注意这里&&的短路效果
//这个地方的 < 或者 <= 结果是一样的,所以用来处理相同数据的排序效率不高
//所以这个lomuto方法相比于前面的快速排序缺点就是相同数据的处理效率低
{
int tmp = ch[cur];
ch[cur] = ch[prev];
ch[prev] = tmp;
}
cur++;
}
ch[left] = ch[prev];
ch[prev] = key;
//然后再次递归左右子树
lomutosort(ch, left, prev - 1);
lomutosort(ch, prev + 1, right);
}
else
return;
}
//7.非递归版本的快排(借助数据结构栈)//这必须是栈,因为栈的先进后出的性质可以模拟递归,
// 在递归的时候是将一个数据一直划分成左右子树,每当你划分好就将右左子树入栈,依次下去就是递归的形式
// 队列的先进先出的特性不符合快速排序的分治思想。快速排序的分治需要先处理较小的子数组,然后再处理较大的子数组。
// 这种顺序通常不能由队列完成,因为队列的处理顺序与递归调用的顺序是不同的
//注意!!!!!!所以可以用栈模拟递归,而使用其他的数据结构会导致递归的顺序出错
//写一个栈
typedef struct stacknode
{
int val;
struct zhannode* next;
}node;
typedef struct stack
{
node* top;
int size;
}stack;
//初始化栈
void initstack(stack* zhan)
{
zhan->size = 0;
zhan->top = NULL;
}
//入栈
void pushstack(stack* zhan, int x)
{
node* newnode = (node*)malloc(sizeof(node));
newnode->next = NULL;
newnode->val = x;
if (zhan->size == 0)
{
zhan->top = newnode;
zhan->size++;
}
else
{
zhan->size++;
newnode->next = zhan->top;
zhan->top = newnode;
}
}
//出栈
void deletestack(stack* zhan)
{
zhan->size--;
node* ret = zhan->top;
zhan->top = zhan->top->next;
free(ret);
ret = NULL;
}
void quicksortnor(int* ch, int Left, int Right)
{
stack zhan;
initstack(&zhan);
pushstack(&zhan, Right);
pushstack(&zhan, Left);
while (zhan.size)
{
int left = zhan.top->val;//找到左右坐标
int leftpos = left;
deletestack(&zhan);
int right = zhan.top->val;
int rightpos = right;
deletestack(&zhan);
int base = ch[left];
int basepos = left;
left++;//这里的left一定要++,使left从基准点后面的位置开始遍历,不然left从基准点开始遍历,会将基准点跟后面的值交换
while (left <= right)
{
while (left <= right && ch[right] > base)
right--;
while (left <= right && ch[left] < base)
left++;
if (left <= right)
{
int tmp = ch[right];
ch[right] = ch[left];
ch[left] = tmp;
left++;
right--;
}
}
//下面是将基准点跟找到的最右边的小于基准点的值交换,结果就是基准点右边的都大于基准点,基准点左边的都小于基准点
ch[basepos] = ch[right];
ch[right] = base;
basepos = right;
if (leftpos < basepos - 1)//这里判断是否有入栈的必要
{
pushstack(&zhan, basepos - 1);
pushstack(&zhan, leftpos);
}
if (basepos + 1 < rightpos)
{
pushstack(&zhan, rightpos);
pushstack(&zhan, basepos + 1);
}
}
}
//8.归并排序
//归并排序的算法思想是分治,先分再治:在递归的过程中,设置边界条件使得递归结束后实现分的效果,然后再进行治的操作,解决一个问题的子问题
//但是再解决子问题时的方法应该是普适的方法,使得再递归的时候也能利用此方法,进行不断地操作
//
//解释归并:首先对于两个有序的数组的合并是简单的;思考一下
//然后将一个待排序数组分成两个数组,分别排序后再进行合并
//然后再进行分组,直到数组中只有一个数,然后这就变成了一个简单的问题
//然后在递归回去的时候都是对两个有序数组的排序,问题就解决了
void mergesort(int* a,int left,int right,int* tmp)
{
if (left >= right)
return;
int mid = (left + right) / 2;//归并就是先递归出去,在递归出去的的基础上来进行操作,然后回归的时候就可以达到效果
mergesort(a, left, mid, tmp);//这个过程就是在不断地进行二叉树的分割,左右子树一直在向深处走,直到左右子树只剩下一个元素
mergesort(a, mid + 1, right,tmp);
//然后开始进行操作,一次次的将递归出去的的返回去
int begin1 = left, end1 = mid;
int begin2 = mid + 1, end2 = right;
int index = begin1;
//合并两个有序数组为⼀个数组
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] < a[begin2])
tmp[index++] = a[begin1++];
else
tmp[index++] = a[begin2++];
}
while (begin1 <= end1)
tmp[index++] = a[begin1++];
while (begin2 <= end2)
tmp[index++] = a[begin2++];
for (int i = left; i <= right; i++)
a[i] = tmp[i];
}
void Mergesort(int* ch, int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
mergesort(ch, 0, n - 1, tmp);
free(tmp);
tmp = NULL;
}
//9.计数排序(非比较排序)
//思想:首先数组的下标是有序的
//所以可以统计数据中所有数字出现的个数,然后将每个数出现的次数存到对应下标的数组中
void printfsort(int* ch, int n)
{
for (int i = 0; i < n; i++)
printf("%d ", ch[i]);
putchar('\n');
}
int main()
{
int n = 0;
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d", &ch[i]);
//quicksortnor(ch, 0, n-1);
lomutosort(ch, 0,n-1);
printfsort(ch, n);
return 0;
}
c语言排序大全
于 2024-08-14 20:26:44 首次发布