数据操作-Sort(排序)

复制代码
插入排序(基于比较的排序算法)
 1.直接插入(性能要比冒泡和简单选择排序好些)
2.折半插入 (直接插入改进版)
3.希尔排序
交换排序
1.冒泡排序
2.快速排序
选择排序
1.简单选择
2.堆排序--------插入删除
归并排序
1.归并排序
复制代码
小总结:
(一)经过一趟排序,能够保证一个关键字达到最终位置,这样的排序是交换类:(冒泡,快速)和选择类:(简单选择,堆)
(二)排序算法关键字比较次数和原始序列无关---简单选择排序和折半插入排序
(三)排序算法的排序趟数和原始序列有关---交换类的排序(冒泡,快速)
(四)借助于“比较”进行排序的算法,在最坏情况下的时间复杂度至少为O(nlog2n)
(五)快些选队(快希选堆):不稳定; 快速排序,希尔排序,简单选择排序,堆排序都是不稳定排序,其他都是稳定排序
(六)
快些排队(快希排堆):
平均情况下时间复杂度都是O(nlog2n),其他都是O(n2);最快情况下快速排序的时间复杂度为O(n2),其他的和平均情况下相同

时间复杂度表

 

数据结构和交换方法

复制代码
#define max 10
typedef struct
{
    int r[max+1];
    int length;
}sqList;

void swap(sqList *l,int i,int j)
{
    int temp=l->r[i];
    l->r[i]=l->r[j];
    L->r[j] = temp;
}
void swaparr(int k[],int i,int j)
{
    int temp=k[i];
    k[i]=k[j];
    k[j]=temp;
}
复制代码

一:插入排序

1:直接插入

(每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子序列中的适当位置,直到全部记录插入完成为止。)

复制代码
void InsertSort(SqList *L)
{
    int i, j, loop, count2;
   loop = count = 0;
    for (i = 2; i <= L->length;i++)
    {
        if (L->r[i]<L->r[i-1])    //若是前面第一个都不满足顺序,那么我们就要去循环
        {
            L->r[0] = L->r[i];
            for (j = i - 1; L->r[j]>L->r[0]; j--)    //将大的数据全部向后移动,从后向前防止数据覆盖
            {
                loop++;
                L->r[j + 1] = L->r[j];    //记录后移
            }
            L->r[j + 1] = L->r[0];    //插入到正确位置
            count++;
        }
    }
    printf("loop move count:%d,  swap insert count:%d\n", loop, count);
}
复制代码

2:折半插入

(通过不断地将数据元素插入到合适的位置进行排序,在寻找插入点时采用了折半查找。)

1)使用key存储插入值

复制代码
void BinSort(int k[],int n)
{
    int i,j,//数据位置
    low,high,mid,key;//已排序的数列中最小中间最大数据位置
    for(i=1;i<n;i++)
    {
        key=k[i];//将待插入记录存在key
        low=1,high=i-1;
        while(low<=high)//在已经有序的表中折半查找插入位置
        {
            mid=(low+high)/2;
            if(key<k[mid])high=mid-1;
            else low=mid+1;
        }
        for(j=i;j>low;j--)//将low之后的数据整体后移
        {
            k[j]=k[j-1];
        }
        k[low]=key;//插入操作
    }
}
复制代码

2)使用k[0]存储插入值

复制代码
void BinSort(int k[],int n)
{
    int i,j,//数据位置
    low,high,mid;//已排序的数列中最小中间最大数据位置
    for(i=2;i<=n;i++)
    {
        k[0]=k[i];//将待插入记录存在k【0】
        low=1,high=i-1;
        while(low<=high)//在已经有序的表中折半查找插入位置
        {
            mid=(low+high)/2;
            if(k[0]<k[mid])high=mid-1;
            else low=mid+1;
        }
        for(j=i-1;j>=high+1;j--)//将low之后的数据整体后移
        {
            k[j+1]=k[j];
        }
        k[high+1]=k[0];//插入操作
/*或者
        for(j=i-1;j>=low;j--)//将low之后的数据整体后移
        {
            k[j+1]=k[j];
        }
        k[low]=k[0];
*/
    }
}
复制代码

3:希尔插入

(把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。图片演示看这个https://www.cnblogs.com/ssyfj/p/9510832.html

复制代码
void shellSort(sqList *l)
{
    int i,j;
    int delta=l->length;
    do
    {
        delta=delta/3+1;//自定义增量变化公式
        for(i=delta+1;i<=l->length;i++)//用delta分组的数据进行直接插入排序
        {
            if(l->r[i]<l->r[i-delta])
            {
                l->r[0]=l->r[i];
                for(j=i-delta;j>0&&l->r[j]>l->r[0];j-=delta)
                {
                    l->r[j+delta]=l->r[j];
                }
                l->r[j+delta]=l->r[0];
            }
        }
    }while(delta>1)

}
复制代码

二:交换排序

1:冒泡排序

(两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止)

复制代码
void bubbleSort(sqList *l)
{
    int i,j;
    int flag=1;
    /**
    for(i=1;i<l->length;i++)
    {
        for(j=l->length-1;j>=i;j--)//从后往前两两比较
        {
            if(l->r[j]>l->r[j+1)
            {
                swap(l,j,j+1);
            }
        }
    }
    **/
    for(i=1;i<l->length;i++)
    {
        flag=0;
        for(j=l->length-1;j>=i;j--)//从后往前两两比较
        {
            if(l->r[j]>l->r[j+1)
            {
                swap(l,j,j+1);
                flag=1;
            }
        }
        if(flag==0)break;//上次排序中没有发生序列变化,说明已经完成排序
    }
}
}
复制代码

2:快速排序

(快排----冒泡排序的改进版:【关于快排优化和快排演示看这个https://www.cnblogs.com/ssyfj/p/9515744.html】

1.在待排序的元素任取一个元素作为基准(通常选第一个元素,但最的选择方法是从待排序元素中随机选取一个作为基准),称为基准元素;

2.将待排序的元素进行分区,比基准元素大的元素放在它的右边,比其小的放在它的左边;

3.对左右两个分区重复以上步骤直到所有元素都是有序的。)

1)递归快排

复制代码
void quickSort(int k[],int low,int high)
{
    int pivot;
    if(low<high)
    {
        pivot=Partition(k,low,high)
        quickSort(k,low,pivot-1);
        quickSort(k,pivot+1,high);

    }
}
int Partition(int k[],int low,int high)
{
    int pivotkey;
    pivotkey=k[low];//用子表第一个记录左枢轴记录
    while(low<high) //从表的两端交替向中间扫描
    {
        while(low<high&&k[high]>=pivotkey)high--;
        swap(k, high, low); //将比枢轴记录小的记录交换到低端
        while(low<high&&k[low]<=pivotkey)low++;
        swap(k, low, high);//将比枢轴记录大的记录交换到高端
    }
    return low;    //返回枢轴所在位置
}
复制代码

2)非递归快排

复制代码
void Push(linkStack &stack,int left,int right)
{
    push(left);
    push(right);
}
int Partition(int k[],int low,int high)
{
    int pivotkey;
    pivotkey=k[low];//用子表第一个记录左枢轴记录
    while(low<high)//从表的两端交替向中间扫描
    {
        while(low<high&&k[high]>=pivotkey)high--;
        swap(k, high, low); //将比枢轴记录小的记录交换到低端
        while(low<high&&k[low]<=pivotkey)low++;
        swap(k, low, high);//将比枢轴记录大的记录交换到高端
    }
    return low;    //返回枢轴所在位置
}
void norecQsort(int k[],int begin,int end)
{
//递归的算法主要是在划分子区间,如果要非递归实现快排,只要使用一个栈来保存区间就可以了。
    linkStack sta;
    Push(sta,begin,end);
    while(!isEmptyStack(sta))
    {
        int right,left;
        top(sta,right);pop(sta);
        top(sta,left);pop(sta);
        if(l>=r)continue;
        int mid=Partition(k,left,right);
        Push(sta,left,mid-1); 
        Push(sta,mid+1,right);
    }
}
复制代码

三:选择排序

1:简单选择

(通过n-i次关键字间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i(1<=i<=n)个记录交换之)

复制代码
void SelectSort(SqList *L)
{
    int i, j, min,count1,count2;
    count1 = count2 = 0;
    for (i = 1; i < L->length;i++)
    {
        min = i;    //将当前下标定义为最小值下标
        for (j = i + 1; j <= L->length;j++)    //循环之后的数据
        {
            count1++;
            if (L->r[min]>L->r[j])    //如果由小于当前最小值的关键字
                min = j;    //更新最小值
        }
        if (i != min)    //若是我们在上面循环中找到最小值,则min会改变,与i不同,就需要进行交换
        {
            swap(L, i, min);
            count2++;
        }
    }
    printf("loop count:%d,  swap count:%d\n", count1, count2);
}
复制代码

2:堆排序(不怎么懂,知道原理无法独自实现)

具体步骤图解和实现算法参考链接https://www.cnblogs.com/ssyfj/p/9512451.html

1:简介

堆排序是一种树形选择排序,在排序过程中可以把元素看成是一颗完全二叉树,每个节点都大(小)于它的两个子节点
1)当每个节点都大于等于它的两个子节点时,就称为大顶堆
2)当每个节点都小于等于它的两个子节点时,就称为小顶堆

2:步骤

1.将长度为n的待排序的数组进行堆有序化(层序遍历)构造成一个大顶堆
 
2.将根节点与尾节点交换并输出此时的尾节点
 
3.将剩余的n -1个节点重新进行堆有序化
 
4.重复步骤2,步骤3直至构造成一个有序序列
复制代码
void swap(int K[], int i, int j)
{
    int temp = K[i];
    K[i] = K[j];
    K[j] = temp;
}

//大顶堆的构造,传入的i是父节点
void HeapAdjust(int k[],int p,int n)
{
    int i,temp;
    temp = k[p];
    for (i = 2 * p; i <= n;i*=2)    //逐渐去找左右孩子结点
    {
        //找到两个孩子结点中最大的
        if (i < n&&k[i] < k[i + 1])
            i++;
        //父节点和孩子最大的进行判断,调整,变为最大堆
        if (temp >= k[i])
            break;
        //将父节点数据变为最大的,将原来的数据还是放在temp中,
        k[p] = k[i];    //若是孩子结点的数据更大,我们会将数据上移,为他插入的点提供位置
        p = i;
    }
    //当我们在for循环中找到了p子树中,满足条件的点,我们就加入数据到该点p,注意:p点原来数据已经被上移动了
    //若没有找到,就是相当于对其值不变
    //插入
    k[p] = temp;
}

//大顶堆排序
void HeapSort(int k[], int n)
{
    int i;
    //首先将无序数列转换为大顶堆
    for (i = n / 2; i > 0;i--)    //注意由于是完全二叉树,所以我们从一半向前构造,传入父节点
        HeapAdjust(k, i, n);

    //上面大顶堆已经构造完成,我们现在需要排序,每次将最大的元素放入最后
    //然后将剩余元素重新构造大顶堆,将最大元素放在剩余最后
    for (i = n; i >1;i--)
    {
        swap(k, 1, i);
        HeapAdjust(k, 1, i - 1);
    }
}


int main()
{
    int i;
    int a[11] = {-1, 5, 2, 6, 0, 3, 9, 1, 7, 4, 8 };
    HeapSort(a, 10);

    for (i = 1; i <= 10; i++)
        printf("%d ", a[i]);

    system("pause");
    return 0;
}
复制代码

 

四:归并排序

1:归并排序

(将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。)

具体参考:https://www.cnblogs.com/linzeliang1222/p/13779914.html

复制代码
void merge(int a[],int left,int mid,int right){
        int m=right - left + 1;
        int arr[m];
        int l = left;
        int r = mid + 1;
        int k = 0;

        while (l <= mid && r <= right) {
            if (a[l] < a[r]) {
                arr[k++] = a[l++];
            } else {
                arr[k++] = a[r++];
            }
        }
        while (l <= mid) {
            arr[k++] = a[l++];
        }
        while (r <= right) {
            arr[k++] = a[r++];
        }

        k = 0;
        while (k < arr.length()) {
            a[left++] = arr[k++];
        }
    }
}
复制代码

 

1)递归归并

复制代码
void 归并排序(int k[],int start,int end){//先排序再合并
   int mid;
   if(start<end){
       mid=(start+end)/2;
       归并排序(k,start,mid);
       归并排序(k,mid+1,end);
       merge(k,start,mid,end);
   }
}
复制代码

2)非递归归并

复制代码
void 归并排序(int a[],int len){
    int i;
    //子数组大小分别为1、2、4、8···,刚开始是1然后2(自底向上,
    //即先按照一个数组两个个元素排序然后四个)
    //i为1时是两个元素一个数组,2时四个元素一个数组,进行排序
    for ( i = 1; i < len; i += i) {
        //left为0从最左端开始
        int left = 0;
        //mid和right是固定的
        int mid = left + i - 1;
        int right = mid + i;
        //进行合并,将数组两两有序合并
        while (right < len) {
            //merge方法和递归用的一样
            merge(a, left, mid, right);
            //移到下一组继续合并
            left = right + 1;
            mid = left + i - 1;
            right = mid + i;
        }
        //由于并不是所有的数组大小都刚好一样大,最后一组不一定满了,
        //所以对最后一组再来合并
        if (left < len && mid < len) {
            merge(a, left, mid, len - 1);
        }
    
}
复制代码

 

 

posted on   Y-flower  阅读(245)  评论(0编辑  收藏  举报

编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示