八大排序

八大排序:冒泡排序,选择排序法,直接插入法,shell 排序法,快速排序,堆
排,归并排序,基数排序
冒泡排序
每次比较相邻的两个数,如果第一个比第二个大,就交换,一直比较到最后完
成一趟冒泡,每次冒泡最后的数变的有序,如果无序会进行 n-1 次的排序,直
到所有数都变得有序


时间复杂度:O(n*2) 优化后 O(n) 空间复杂度:O(1) 稳定性:稳定
void bublesort(int *arr, int len)
{
int tmp = 0;
for(int i=0;i<len-1;i++)
{
bool swap =false;
for (int j = 0; j < len- 1-i; j++)
{
if (arr[j] > arr[j+1])
{
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
swap = true;
}
}
if (!swap)
{
return;
}
}
}
选择排序法
选择法排序指每次选择所要排序的数组中的最大值(由小到大排序则选择最小值)
的数组元素,将这个数组元素的值与最前面没有进行排序的数组元素的值互换


时间复杂度:O(N*2) 空间复杂度:O(1) 稳定性:不稳定
void selectsort(int *arr,int len)
{
int tmp;
int i ;
int j ;
int min=0;
for ( i = 0; i < len - 1; i++)
{
min = i;
for ( j = i+ 1; j < len; j++)
{
if (arr[j] < arr[min])
{
tmp = arr[j];
arr[j] = arr[min];
arr[min] = tmp;
}
}
}
}
直接插入法
插入排序是在一个已经有序的小序列的基础上,一次插入一个元素。比较是从有
序序列的末尾开始,也就是想要插入的元素和已经有序的最大者开始比起,如果
比它大则直接插入在其后面,否则一直往前找直到找到它该插入的位置。如果碰
见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。
所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的
顺序,所以插入排序是稳定的。


时间复杂度:O(n^2) 好的情况下 O(1) 空间复杂度:O(1) 稳定性:稳定
void insertsort(int *arr,int len)
{
int i,j,tmp;
for ( i = 1; i < len; i++)
{
tmp = arr[i];
for (j = i - 1; i >= 0;j--)
{
if (tmp < arr[j])
{
arr[j + 1] = arr[j];
}
else
break;
}
arr[j + 1] = tmp;
}
}
shell  排序
Shell排序是直接插入排序的一种优化采用分组的思想,将一组数分成 n 各小组,
对每个小组进行插入排序,然后减小 n 值,重新分组,直到 n=1,最后 n 必须等
于1进行插入排序完成最终排序


时间复杂度:O(n^1.3~~n^1.5) 空间复杂度 O(1) 稳定性:不稳定
void Shell(int *arr, int len, int gap)
{
int tmp = 0;
int i, j;
for (i = gap; i < len; i++)
{
tmp = arr[i];
for (j = i - gap; j >= 0; j-=gap)
{
if (arr[j] > tmp)
{
arr[j + gap] = arr[j];
}
else
break;
}
arr[j + gap] = tmp;
}
}
void shellsort(int *arr,int len)
{
int drr[] ={ 5,3,1 };
int lend = sizeof(drr) / sizeof(drr[0]);
for (int i = 0; i < lend; i++)
{
Shell(arr, len, drr[i]);
}
}
快速排序
时间复杂度:好的情况O(nlog2n)坏的情况(有序)O(n^2)
空间复杂度:O(logn) 稳定性:不稳定
1、先从数列中取出一个数作为基准数
2、分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它
的左边
3、再对左右区间重复第二步,直到各区间只有一个数
以第一个数为基准将其放进 TMP 中从 high 开始比较如果 high<tmp 则把 high 放
入 low,然后 low++,若 arr[low]>tmp,则把 low 放入high,然后 high--,若
high<tmp,则把high给low,然后Low++,进行循环



int Partation(int*arr,int low, int hign)
{
int tmp;
tmp = arr[low];
while (low < hign)
{
while ((low< hign) && arr[hign] >=tmp)
{
hign--;
}
if (low >= hign)
{
break;
}
else
{
arr[low] = arr[hign];
}
while ((low< hign) && arr[low] <= tmp)
{
low++;
}
if (low >= hign)
{
break;
}
else
{
arr[hign] =arr[low];
}
}
arr[low] = tmp;
return low;
//查找 par 的位置,找到后刚好完成第一次快排
}
一,//递归
void quick(int *arr, int low, int hign)
{
int par = Partation(arr, low, hign);
if(par>low+1)
{
quick(arr, low, par - 1);
}
//从第一次快速排列后的左边开始重新定义新的hign为par-1,并对左边进行快速排列,查找新的par

if (par < hign - 1)
{
quick(arr, par + 1, hign);
}
//从第一次快速排列后的右边开始重新查找新的 low 为 0,并对右边进行快速排列,查找新的 par
}
void quicksort(int *arr,int len)
{
quick(arr, 0, len - 1);
//整个数列从头到尾查找 par 进行第一次快速排列
}
二//非递归 进行入栈操作进行排列
#include<math.h>
void quicksort1(int*arr, int len)
{
int tmpsize= (int)ceil(log((double)len));
int *stack = (int*)malloc(sizeof(int)*tmpsize*2);
//开辟栈的内存大小( O(nlog2n) )
assert(stack != NULL);
int low = 0;
int hign = len - 1;
int par = Partation(arr, 0, len - 1);
//第一次排序后为 9,5,13,2,6,20,32,25,76,查找第一个 par


int top = 0;
if (par > low + 1)
{
stack[top++] = low;
stack[top++] = par - 1;
}
//对 par 的左边进行入栈操作,入栈 0,4
if (par < hign-1)
{
stack[top++] = par + 1;
stack[top++] = hign;
}


//对 par 的右边进行入栈操作,入栈 6,8
while (top > 0)
{
hign = stack[--top];
low = stack[--top];
par = Partation(arr, low, hign);
//(1.a)赋予 hign,low,par 新的位置,hign 位置为 8,low 位置为 6,par 位置为 7,先进行右侧
的排序


if (par > low + 1)
{
stack[top++] = low;
stack[top++] = par - 1;
}
//(2),执行完(1)操作后,栈内所含数据为 0,4 对初始 par 的左边操作赋予 hign,low,par 新的
位置,此时 hign 位置为 4,low 位置为 0,par 位置为 3


//此时 par 的右侧有序不需执行下面的的操作,接下来再次执行 par 左侧的入栈操作,新栈
进栈数据 0,2


//再次重新 赋予 hign,low,par 新的位置,hign 位置为 2,low 位置为 0,par 位置为 1,此时数据排
序完成


if (par < hign - 1)
{
stack[top++] = par + 1;
stack[top++] = hign;
}
//(1.b)对新 par 的右边进行入栈操作,此时(1.a)操作后右侧数据有序
}
}
快速排序的三种方法:
(1 ) 随机选取基准法

思想:取待排序列中任意一个元素作为基准
void Swap(int *arr, intlow, int hign)
{
int tmp = arr[low];
arr[low] = arr[hign];
arr[hign] =tmp;
}
//交换 low,hign 所在位置的值
void quick2(int *arr, int low, int hign)
{
Swap(arr, low, rand() %(hign - low) + low);
int par = Partation(arr, low, hign);
//交换 low 与随机产生 hign 的值,确定新的 hign,low,par 位置
if (par>low+ 1)
{
quick2(arr,low, par - 1);
}
//对 par 左边进行排序
if (par < hign - 1)
{
quick2(arr,par + 1, hign);
}
//对 par 右边进行排序
}
void quicksort2(int *arr, int len)
{
quick(arr, 0, len - 1);
}
(2 ) 三数取中法
void Median_of_three(int *arr, int low, int mid, int hign)
{
//取三个数的中间值 arr[mid]<=arr[low]<=arr[hign]
if (arr[mid] > arr[low])
{
Swap(arr, mid,low);
}
if (arr[mid] > arr[hign])
{
Swap(arr, mid, hign);
}
if (arr[low] > arr[hign])
{
Swap(arr, low, hign);
}
void quick3(int *arr, int low, int hign)
{
Median_of_three(arr, low, (hign - low) / 2, hign);
//取待排列的序列中的 low,mid,hign 值,选取中间值为驱轴


int par = Partation(arr, low, hign);
if (par < hign - 1)
{
quick3(arr,par + 1, hign);
}


if (par>low+ 1)
{
quick3(arr,low, par - 1);
}


}
void quicksort3(int *arr, int len)
{
quick3(arr,0, len - 1);
}
(3 ) 基准聚拢法
待排序序列1467667686三数取中选取枢轴:下标为4的数6
转换后,待分割序列:6 467167686
枢轴key:6
第一步,在划分过程中,把与key相等元素放入数组的两端
结果为:6416(枢轴)787666
此时,与6相等的元素全放入在两端了
第二步,划分结束后,把与key相等的元素移到枢轴周围
结果为:1466(枢轴) 666787
此时,与6相等的元素全移到枢轴周围了
之后,在14和 787两个子序列进行快排
void Focus_Num(int *arr, int low, int par,int hign,int*left,int*right)
{
int i;
int Pleft =par - 1;
//聚拢左边相同数
for (i = par - 1; i >= low; i--)
{
if (arr[i] == arr[par])
{
if (i == Pleft)
{
Pleft--;
}
//在 arr[i]==arr[par]的前提下,如果 i=Pleft,Pleft 向左移动,再次查找与 par 代表值相同的数
if (i != Pleft)
{
Swap(arr, i, Pleft);
Pleft--;
}
//在 arr[i]==arr[par]的前提下,如果 i!=Pleft,先交换 i 与 Pleft 所代表的值,再使 Pleft 向左
移动,再次查找与 par 代表值相同的数

}
}
int Pright = par + 1;
//聚拢右边相同数
for (i = par + 1; i <= hign; i++)
{
if (arr[i] == arr[par])
{
if (i == Pright)
{
Pright++;
}
//在 arr[i]==arr[par]的前提下,如果 i=Pright,Pright 向右移动,再次查找与 par 代表值相同的

if (i != Pright)
{
Swap(arr, i, Pright);
Pright++;
}
//在 arr[i]==arr[par]的前提下,如果 i!=Pright,先交换 i 与 Pright 所代表的值,再使 Pright 向
右移动,再次查找与 par 代表值相同的数

}
}
*left = Pleft;
*right = Pright;
}
void quick4(int *arr, int low, int hign)
{
int par = Partation(arr, low, hign);
int left=par-1;
int right=par+1;
Focus_Num(arr, low, par, hign,&left,&right);
if (par>low+ 1)
{
quick4(arr,low,left);
}
if (par < hign - 1)
{
quick4(arr,right, hign);
}
}
void quicksort4(int *arr, int len)
{
quick4(arr,0, len - 1);
}
堆排
时间复杂度:O(nlog2n)最好最坏情况
空间复杂度:O(1) 稳定性:不稳定
堆排序是一种树形选择排序,在排序过程中可以把元素看成是一颗完全二叉树,
每个节点都大(小)于它的两个子节点,当每个节点都大于等于它的两个子节点
时,就称为大顶堆,也叫堆有序; 当每个节点都小于等于它的两个子节点时,
就称为小根堆。
思路(大根堆):
(1)将长度为n的待排序的数组进行堆有序化构成一个大顶堆;
(2)将根节点与尾节点交换并输出此时的尾节点;
(3)将剩余的n-1个节点重新进行堆有序化;
(4)重复进行(2)(3)步骤直至构成一个有序序列

void adjust(int*arr, int start, intend)
{
//将原数列调整为大根堆
int tmp;
tmp = arr[start];
//由子代(n)推出父代 ——————> (n-1)/2
//由父代(n)推出子代 ——————> 左子叶(2n+2)右子叶(2n+2)

for (int i = 2 * start + 1; i <= end; i = 2 * i+ 1)
{
//i 为最后一个子叶的父代所在的位置
if (i < end&& arr[i] <arr[i + 1])
{
i++;//i 当前的下标肯定是左右孩子里最大的值
}
if (arr[i] > tmp)
{
arr[start] = arr[i];
start = i;
}
else
{
break;
}
}
arr[start] = tmp;
//将 start 所代表的值赋值给 tmp,并将其与子代值进行比较,值小的置于 start 值所在的位置,再
进行下一个 start 操作,逐步将较大的值置于底部

}
void heapsort(int*arr, int len)
{
for (int i = (len - 1 -1) / 2; i >= 0; i--)
{
adjust(arr,i, len - 1);
}
//第一次构造成大根堆


int tmp = 0;
for (int i = 0; i < len- 1; i++)
{
tmp = arr[0];
arr[0] = arr[len - 1 - i];
arr[len - 1- i] = tmp;
//根与最后的子叶交换,此时最大值位置确定
adjust(arr,0, len-1-i-1);
//再次将剩余数据构造大顶推树并进行根与最后子叶的交换,每交换一次确定出剩余数据最大值的位

}
}

归并排序
时间复杂度:O(nlog2n) 空间复杂度:O(n) 稳定性:稳定
即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序
表,称为二路归并。
对以下数据5 10 8 14 19 1 35 25,单个数据有序,再进行排序使两个
两个数据有序,如下图示:

void Merge(int*arr, intlen, int gap)
{
//申请一个数组
int *brr = (int*)malloc(sizeof(int)*len);
assert(brr != NULL);
//判断 start1,end1,start2,end2 的位置
int i = 0;
int s1 = 0;
int e1 = s1+gap - 1;;
int s2 = e1+ 1;
int e2 = s2+gap-1<len-1?s2+gap-1:len-1;
//当有两个归并段时
while (s2 <len)
{
//两个归并段都有数据
while ((s1 <= e1) && (s2 <= e2))
{
if (arr[s1]<= arr[s2])
{
brr[i++] = arr[s1++];
}
else
{
brr[i++] = arr[s2++];
}
}


//退出循环的两种方式
while (s1 <= e1)
{
brr[i++]=arr[s1++];
}
while (s2 <= e2)
{
brr[i++] = arr[s2++];
}
s1 = e2 + 1;
e1 = s1 + gap - 1;;
s2 = e1 + 1;
e2 = s2 + gap - 1<len - 1 ? s2 + gap - 1 : len- 1;
}
//只有一个归并段时
while (s1 <len)
{
brr[i++] = arr[s1++];
}
for (int i = 0; i < len; i++)
{
arr[i]=brr[i];
}
free(brr);
brr = NULL;
}
void Mergesort(int *arr, int len)
{
for (int i = 1; i < len; i*=2)
{
Merge(arr, len, i);
}
}
基数排序
又称桶子法,根据当前待排序的每一位上的数字进行入桶排序,桶的数量跟当前
单个数字的取值范围有关等到最低位排完得到一个子序列,再将这个序列按照次
低位的大小进入相应的桶中,一直排到最高位为止,数组排序完成。

//每个桶内存储数据时相当于进行单链表的操作,创建单链表
typedef struct Node
{
int data;
struct Node*next;
}Node, *List;
//单链表的初始化
void InitList(List plist)
{
assert(plist != NULL);
plist->next= NULL;
}
//新建一个节点
static Node*GetNode(int val)
{
Node *pGet = (Node *)malloc(sizeof(Node));
assert(pGet!= NULL);
pGet->data = val;
pGet->next = NULL;
return pGet;
}
//单链表的插入操作,尾插。入桶
bool Insert(List plist,int val)
{
Node *p = plist;
while (p->next != NULL)
{
p = p->next;
}
Node *pGet = GetNode(val);
p->next = pGet;
return true;
}
//删除并保存值,出桶操作
bool DelFirst(List plist, int *rtv)
{
Node *p = plist->next;
if (p == NULL)
{
return false;
}
*rtv = p->data;
plist->next= p->next;
free(p);
p = NULL;
return true;
}
//找出最大数并确定最大数的位数
int GetBitMax(int *arr,int len)
{
int max = arr[0];
int count =0;
for (int i = 1; i < len;i++)
{
if (arr[i] > max)
{
max = arr[i];
}
}
while (max != 0)
{
count++;
max /= 10;
}
return count;
}
//得到 figure 位上的数值
int Getnum(int num, intfigure)
{
for (int j = 0; j < figure; j++)
{
num = num /10;
}
return num % 10;
}
void Base(int *arr, intlen, int figure)
{
assert(arr != NULL);
//创建桶
Node head[10];
for (int j = 0; j < 10;j++)
{
InitList(&head[j]);//初始化
}
int tmp;
for (int j = 0; j < len; j++)
{
tmp = Getnum(arr[j], figure);//得到 j 号下表 figure 位的数值
Insert(&head[tmp], arr[j]);
//入桶,遍历数组 figure 位相应的数据进入相应的桶里
}
int n = 0;
for (int j = 0; j < 10;)
{
if (DelFirst(&head[j], &arr[n]))
{


n++;//j 号桶不一定只有一个数据,遍历整个数组用 n 代表一个桶里存放数据的下表,
避免出现多个数据使数据存放出现问题,覆盖掉原先值或赋值后不改变

}
else
{
j++;//下一个桶
}
}
}
void Basesort(int *arr,int len)
{
int count =GetBitMax(arr, len);
for (int i = 0; i < count; i++)
{
Base(arr, len, i);//i 表示当前数字从右往左数的位数
}
}

 

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值