基础排序算法:
前言
今天复习一下常用的排序算法
以下内容仅供参考
冒泡排序:
原理:每一次循环都挑出最大值放在末尾,循环此步骤直到完成排序。
挑选方式:从数组开头依次比较当前位和后一位的大小,发现更大值就交换位置。
普通版本为两个for循环依次比较。
改进版本为设置循环条件,每一次循环都找出最大值和最小值,然后缩小循环条件,重复此步骤。
以下是改进代码
void Bubble_Sort(int Arry[], int len)//冒泡排序
{
//小的数冒到前面,大的数排后面
//改进一下,前面往后冒,后面往前冒
int low=0;
int high=len-1;
int tmp=0;
while(low<high)
{
for(int i=low;i<high;i++)
{
if(Arry[i]>Arry[i+1])
{
tmp=Arry[i];
Arry[i]=Arry[i+1];
Arry[i+1]=tmp;
}
}
for(int j=high;j>low;j--)
{
if(Arry[j]<Arry[j-1])
{
tmp=Arry[j];
Arry[j]=Arry[j-1];
Arry[j-1]=tmp;
}
}
low++;
high--;
}
}
时空复杂度及稳定性
时间复杂度:O(N^2)
空间复杂度: O(1)
稳定性:稳定
直接插入排序:
原理:从数组第二位开始往前找位置插入。
插入方式:找到比自己小的前一位就可以插在那一位的后面。因为前面是有序的。
void Insert_Sort(int Arry[], int len) //插入排序
{
for(int i=1;i<len;i++)
{
int cur = Arry[i];//这一步必须取值,不能只复制下标,否则会出错。
int j=i;
for(;j>0;j--)
{
if(Arry[j-1]>cur)
{
Arry[j]=Arry[j-1];
}
else
break;
}
Arry[j] = cur;
}
}
时间复杂度:O(N^2)
空间复杂度:O(1)
稳定性:稳定
简单选择排序
原理:每趟从未排序序列中找出最小(最大)元素,插入到已排序序列末尾,直到全部变为有序序列为止。
//选择排序
void Select_Sort(int Arry[], int len)
{
int min=0;
int tmp=0;
//从第一个位置开始循环,往后找到最小值,与当前位置交换
for(int i=0;i<len;i++)
{
min = i;//重新初始化min=i,便于后面判断min是否改变
for(int j=i; j<len;j++)
if(Arry[j] < Arry[min])//发现比自己小的元素,把下标改成它
min=j;
//如果发现min位置没变,那就continue
if(min != i)
{
tmp = Arry[i];
Arry[i]=Arry[min];
Arry[min] = tmp;
}
}
}
时空复杂度和稳定性
时间复杂度:O(N^2)
空间复杂度:O(1)
稳定性:不确定。 基于交换的选择排序是不稳定的;基于插入的选择排序是稳定的。如果没有说明默认是基于交换的选择排序即不稳定。
希尔排序:
原理:插入排序的优化,将待排序列根据增量分成多个数组,分别进行插入排序。
//待排数组、数组长度、希尔数组、数组长度
void Shell_Sort(int Arry[], int len, int Shell[], int slen)
{
//1、每次相隔一定距离执行插入排序
//2、其实就是插入排序的变种
int cur = 0;//当前数
int delta = 0;
for (int i = 0; i < slen; i++)//遍历增量数组
{
delta = Shell[i];//增量
for (int j = delta; j < len; j++)//从第一个增量往后
{
cur = Arry[j];
int k = j;
for (; k >= delta; k -= delta)//每个值和它-增量的值相比
{
if (Arry[k - delta] > cur)
Arry[k] = Arry[k - delta];
else
break;
}
Arry[k] = cur;
}
}
}
稳定性
不稳定
快速排序
快速排序是对冒泡排序的改进
原理:每次选定一个基准数,将比它小的排在左边,比它大的排在右边,如此依次,使得序列有序。
void Quick_Sort(int Arry[], int low, int high)//快速排序
{
//1、找第一个元素为基准点,记位standardnum
//2、有一个值i指向low,一个j指向high。
//3、j往前找到比基准小的,然后i往后找到比基准大的。
//4、交换i和j。
if (low >= high)//递归结束条件
return;
int standardnum = low;
int tmp = 0;
int i = low;
int j = high;
while (i < j)
{
while (Arry[j] >= Arry[standardnum] && i < j)
j--;
while (Arry[i] <= Arry[standardnum] && i < j)
i++;
if (i < j)
{
tmp = Arry[i];
Arry[i] = Arry[j];
Arry[j] = tmp;
}
}
//i=j时,把基准和i或者j调换
if (i == j)
{
tmp = Arry[standardnum];
Arry[standardnum] = Arry[j];
Arry[j] = tmp;
}
Quick_Sort(Arry, low, i - 1);
Quick_Sort(Arry, i + 1, high);
}
时空复杂度和稳定性
时间复杂度:O(nlog2n)
空间复杂度:O(nlog2n)
稳定性:不稳定
归并排序
原理:先拆分,后合并,将序列拆分成多个数组直至最小,最小数组为2个元素,然后将两个数组合并排序。
void Merge(int Arry[], int low, int mid, int high)//合,两个数组,所以要low mid high才有两个
{
//先创建一个第三方数组来存放合成好的元素,第三方数组长度为high - low + 1
int* thirdArry = (int*)malloc(sizeof(int) * (high - low + 1));
memset(thirdArry, 0, sizeof(int) * (high - low + 1));//初始化为0
int low1 = low, high1 = mid;//第一个数组
int low2 = mid + 1, high2 = high;//第二个数组
int k = 0;//遍历第三方数组,存储数字
while (low1 <= high1 && low2 <= high2)
{//两个数组都没超
if (Arry[low1] <= Arry[low2])
{
thirdArry[k++] = Arry[low1++];
}
else
thirdArry[k++] = Arry[low2++];
}
//如果一个超了一个没超,比如A数组长度是2,B长度是5,那排序完把B后面的3个元素加上
while (low1 <= high1)
{
thirdArry[k++] = Arry[low1++];
}
while (low2 <= high2)
{
thirdArry[k++] = Arry[low2++];
}
//排序完把Arry值修改一下
if(k!=0)
for (int i = low, k = 0; i <= high; i++, k++)
{
Arry[i] = thirdArry[k];
}
free(thirdArry);
}
void Merge_Sort(int Arry[],int low,int high)//分
{
//先递归分成多个数组
if (low >= high)
return;
int mid = (low + high) / 2;
Merge_Sort(Arry, low, mid);
Merge_Sort(Arry, mid + 1, high);
Merge(Arry, low, mid, high);
}
时空复杂度和稳定性
时间复杂度:O(nlog2n)
空间复杂度:O(n)
稳定性:稳定
堆排序
原理:将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆; 将堆顶元素与末尾元素交换,将最大(小)元素"沉"到数组末端; 接着循环构造堆,重复此动作。
void Heap_Sort(int Arry[], int len)//堆排序
{
if (len <= 0)
return;
//父i 子2*i+1 2*i+2
//1、构造大根堆,如何构造大根堆?
//每个父节点都比两个子节点大的堆,从后往前构造。
int tmp = 0;
for (int i = len; i > 0; i--) {
if (Arry[(i - 1) / 2] < Arry[i])
{
tmp = Arry[(i - 1) / 2];
Arry[(i - 1) / 2] = Arry[i];
Arry[i] = tmp;
}
if (Arry[(i - 2) / 2] < Arry[i])
{
tmp = Arry[(i - 2) / 2];
Arry[(i - 2) / 2] = Arry[i];
Arry[i] = tmp;
}
}
//大根堆构造完成,然后把堆顶元素和数组末尾元素交换顺序,然后len-1
tmp = Arry[0];
Arry[0] = Arry[len];
Arry[len] = tmp;
len--;
Heap_Sort(Arry, len);
}
时空复杂度和稳定性
时间复杂度:O(nlog2n)
空间复杂度:O(1)
稳定性:不稳定
基数排序
原理:基数排序不需要进行记录关键字间的比较。
先求得最大数字的长度,然后循环此值,从个位往前将每个元素分别排入队列,随后取出再重新排入。
void Radix_Sort(int Arry[], int len)//基数排序
{
//找出最大值计算他的位数
int max = Arry[0];
for (int i = 0; i < len; i++) {
Arry[i] > max ? max = Arry[i] : max = max;
}
//转成字符串计算位数
char maxnum[20];
sprintf_s(maxnum, "%d", max);
max = strlen(maxnum);//得到位数
queue<int> queuelist[10];//10个队列存放元素
int order = 1;
for (int i = 0; i < max; i++)
{
//循环位数那么多次
int k = 0;//重新放回数组
for (int j = 0; j < len; j++)
{
//1、判断位值插入队列。
//如何判断某一位的数呢?先转字符串然后判断当前位的位数
int listnum = (Arry[j] / order) %((int)pow(10.0, i + 1));
queuelist[listnum].push(Arry[j]);
}
order *= 10;
//2、从队列重新排入数组。
for (int o = 0; o < 10; o++)
{
while (!queuelist[o].empty())//队列不为空
{//不为空就往外取值
Arry[k++] = queuelist[o].front();
queuelist[o].pop();
}
}
}
}
时空复杂度和稳定性
时间复杂度:O(nk)
空间复杂度:O(nk)
稳定性:稳定
参考:
1、值得收藏的十大经典排序算法
2、八大排序算法
3、【数据结构、算法】八大排序算法概述(算法复杂度、稳定性)
4、算法,当下