zoukankan      html  css  js  c++  java
  • 排序算法

    #define LENGTH(array) ( (sizeof(array)) / (sizeof(array[0])) )
    #define swap(a,b) do{a=a+b;b=a-b;a=a-b;}while(0) //两个数相同时 会导致结果为0

    图片来源:https://blog.csdn.net/dyl_love98/article/details/8809000

    图片来源 https://www.zhihu.com/question/280279208/answer/829134919

    一、插入排序

    1、直接插入排序

     1 /**
     2 **  直接插入排序(插入到准确的位置)  不利于二分查找 直接遍历
     3 **  时间复杂度:比较和移动和平均时间复杂度为O(n^2)  适合基本有序的序列,此时复杂度接近O(n)
     4 **  空间复杂度:O(1)
     5 **  稳定性:稳定
     6 **/
     7 void InsertSort(int a[], int n)
     8 {
     9     int i, j;
    10     for (i = 2; i <=n; i++)//假设第一项已经排好,所以从第二项开始
    11     {
    12         if (a[i] < a[i - 1])//若此项小于 前面已经排好的最后一项(已排好的最大项)
    13         {
    14             a[0] = a[i];//0号位置为哨兵节点
    15             for (j = i - 1; a[0] < a[j]; j--)//从后 向前查找待插入位置
    16             {
    17                 a[j+1] = a[j];//记录后移
    18             }
    19             a[j+1] = a[0];//复制到插入的位置 此时a[j]已经是小于a[0]的了
    20         }
    21     }
    22 }

    2、利用二分查找的直接插入排序

     1 /**
     2 **  插入排序(插入到准确的位置) 利用二分查找
     3 **  比较复杂度:O(nlogn)
     4 **  移动复杂度:O(n^2) 
     5 **  时间复杂度为:O(n^2) 
     6 **  稳定性:稳定
     7 **/
     8 void BInsertSort(int a[], int n)
     9 {
    10     int i, j, low, high, mid;
    11     for (i = 2; i <= n; i++)
    12     {
    13         a[0] = a[i];//哨兵元素
    14         low = 1; high = i - 1;
    15         while (low <= high)
    16         {
    17             mid = (low + high) / 2;
    18             if (a[mid] > a[0])
    19                 high = mid - 1;
    20             else
    21                 low = mid + 1;
    22         }
    23         for (j = i - 1; j >=high+1; j--)
    24             a[j + 1] = a[j];
    25         a[high + 1] = a[0];
    26     }
    27 }

    3、希尔排序

     1 /**
     2 **  希尔排序 (缩小增量的排序)
     3 **  最坏时间复杂度:O(n^2)
     4 **  平均时间复杂度:O(n^1.3)
     5 **  空间复杂度:O(1)
     6 **  稳定性:不稳定 当相同关键字被划分到不同子表时 可能会造成不稳定
     7 **/
     8 void ShellSort(int a[], int n)
     9 {
    10     //a[0] 是暂存单元 不是哨兵
    11     //进行插入排序的步长是dk 而不是1
    12     int i, j, dk;
    13     for (dk = n / 2; dk >= 1; dk = dk / 2)//步长变化
    14     {
    15         for (i = dk + 1; i <= n; i++)//对已经分好组的进行插入 排序 每一组都交替进行
    16         {
    17             if (a[i] < a[i - dk])//若当前项小于本组前一项
    18             {
    19                 a[0] = a[i];//暂存在a[0]
    20                 for (j = i - dk; j > 0 && a[0] < a[j]; j-=dk)//从本组已经排好序的序列中找到合适的插入位置
    21                     a[j + dk] = a[j];//记录后移
    22                 a[j + dk] = a[0];//插入
    23             }
    24         }
    25     }
    26 }

    二、交换排序

    1、冒泡排序

     1 /**
     2 **  冒泡排序(每次交换,将相邻中大的一个放在后面)
     3 **  最坏时间复杂度:O(n^2)
     4 **  平均时间复杂度:O(n^2)
     5 **  最好情况下的复杂度:O(n) 初始序列有序时
     6 **  空间复杂度:O(1)
     7 **  稳定性:稳定
     8 **/
     9 //最大的上浮
    10 void BubbleSort(int a[], int n)
    11 {
    12     for (int i = 0; i < n - 1; i++)
    13     {
    14         int flag = 0;
    15         for (int j = 0; j < n - 1 - i; j++)//最大的向上浮
    16         {
    17             if (a[j] > a[j + 1])//此处若写等号 则变成不稳定的排序
    18             {
    19                 swap(a[j], a[j + 1]);
    20                 flag = 1;
    21             }
    22         }
    23         if (flag == 0) return;//一整趟都未交换一次 则已排序好 退出
    24     }
    25 }
     1 //最小的下浮
     2 void BubbleSort_m(int a[], int n)
     3 {
     4     for (int i = 0; i < n - 1; i++)
     5     {
     6         int flag = 0;
     7         for (int j = n-1; j >i; j--)//最小的向下浮
     8         {
     9             if (a[j-1] > a[j])//此处若写等号 则变成不稳定的排序
    10             {
    11                 swap(a[j], a[j-1]);
    12                 flag = 1;
    13             }
    14         }
    15         if (flag == 0) return;//一整趟都未交换一次 则已排序好 退出
    16     }
    17 }

    2、快速排序

     1 /**
     2 **  快速排序(任取枢轴pivot,将其分为小于pivot,和大于pivot的两部分,此时pivot放在了最终的位置上)
     3 **  最坏空间复杂度O(n)
     4 **  平均空间复杂度O(logn)
     5 **  最坏时间复杂度:O(n^2)
     6 **  平均时间复杂度:O(nlogn)
     7 **  稳定性:不稳定 如若3为枢轴{3,2,2'} 经过一趟排序后 {2',2,3}
     8 **/
     9 int Partition(int a[], int low, int high)//根据枢轴划分两个区间
    10 {
    11     int pivot = a[low];//将枢轴的值存起来
    12     while (low < high)
    13     {
    14         while (low < high && a[high] >= pivot) --high;//若高位有比枢轴小的 结束循环
    15         a[low] = a[high];//将高位比枢轴小的值放到低位上 
    16         while (low < high && a[low] <= pivot) ++low;//若低位有比枢轴大的值 结束循环 
    17         a[high] = a[low];//将低位比枢轴大的值放在高位上
    18     }
    19     a[low] = pivot;//将枢轴的值放到正确的位置上
    20     return low;//返回枢轴的位置
    21 }
    22 void QuickSort(int a[], int low, int high)
    23 {
    24     if (low < high)
    25     {
    26         int pivotloc = Partition(a, low, high);
    27         QuickSort(a, low, pivotloc - 1);//依次对两个子表进行递归排序
    28         QuickSort(a, pivotloc + 1, high);
    29     }
    30 }

    三、选择排序

    1、选择排序

     1 /**
     2 **  选择排序(每次选最小的)
     3 **  空间复杂度O(1)
     4 **  时间复杂度:O(n^2)
     5 **  稳定性:不稳定 如若{2,2',1} 经过一趟排序后 {1,2',2}
     6 **/
     7 void SelectSort(int a[], int n)
     8 {
     9     for (int i = 0; i < n - 1; i++)
    10     {
    11         int min = i;
    12         for (int j = i + 1; j < n; j++)
    13         {
    14                 if (a[j] < a[min])
    15                     min = j;
    16         }
    17         if(min!=i)
    18             swap(a[i], a[min]);
    19     }
    20 }

    2、堆排序

     1 /**
     2 **  堆排序(每次选最小的)
     3 **  空间复杂度O(1)
     4 **  时间复杂度:O(nlogn) 建堆时间O(n) 向下平均调整时间O(nlogn)
     5 **  稳定性:不稳定 如若{1,2,2'} 构造初始堆时将2交换到堆顶 {2,1,2'} 最终排序为{1,2',2}
     6 **/
     7 
     8 //将堆中小的节点调整至下方
     9 void AdjustDown(int a[], int k, int len)
    10 {
    11     a[0] = a[k];//用a[0]暂存根节点
    12     int i;
    13     for (i = 2 * k; i <= len; i *= 2)
    14     {
    15         if (i < len && a[i] < a[i + 1])//找出子数较大的一个
    16             i++;
    17         if (a[0] >= a[i])break;//如果根节点本来就大 无须调整
    18         else//叶子节点比较大
    19         {
    20             a[k] = a[i];//将较大的叶子节点调整到根节点
    21             k = i;//以原先未调整的较大的叶子节点的位置为根再进行调整 
    22         }
    23     }//for
    24     a[k] = a[0];//将最初暂存的根节点的值 放到最后一个做调整的叶子节点上去
    25 }
    26 //建大根堆
    27 void BuildMaxHeap(int a[], int len)
    28 {
    29     for (int i = len / 2; i > 0; i--)//从i=n/2到1 反复调整堆
    30         AdjustDown(a, i, len);
    31 }
    32 //堆排序
    33 void HeapSort(int a[], int len)
    34 {
    35     BuildMaxHeap(a, len);//建立大根堆
    36     show(a, 1,len, "after_build_max_heap:");
    37     for (int i = len; i > 1; --i)//将最后一个记录与大根堆的根节点对换
    38     {
    39         swap(a[1], a[i]);
    40         AdjustDown(a, 1, i - 1);//对根节点向下调整,序列长度为i-1 第i项为已经排列好的最大项
    41     }
    42 
    43 }

    四、归并排序

    1、一个无序数组归并

     1 /**
     2 **  归并排序(分治法 每次分成左右两个子序列 且左右两个子序列有序)
     3 **  空间复杂度O(n) 需要辅助数组b[]
     4 **  时间复杂度:O(nlogn)
     5 **  稳定性:稳定
     6 **/
     7 void Merge(int a[], int b[], int low, int mid, int high)
     8 {
     9     //a是原数组 b是辅助数组 low-mid;mid+1-high 各自有序 将他们合并成一个有序表存放在a中
    10     int i, j, count;//count代表当前排序好的数据
    11     for (int k = low; k <= high; k++)//将a中所有的数据放到b中
    12         b[k] = a[k];
    13     for (i = low, j = mid + 1, count = low; i <= mid && j <= high; count++)//选择两个有序组中更小的一个放在a中
    14     {
    15         if (b[i] < b[j])
    16             a[count] = b[i++];
    17         else
    18             a[count] = b[j++];
    19     }
    20     //如下两个循环只会执行一个
    21     while (i <= mid) a[count++] = b[i++];//若第一个表未检测完 复制
    22     while (j <= high) a[count++] = b[j++];//若第二个表未检测完 复制
    23 }
    24 void MergeSort(int a[],int b[], int low, int high)
    25 {
    26     if (low < high)
    27     {
    28         int mid = (low + high) / 2;//划分子序列
    29         MergeSort(a,b ,low, mid);//对左侧递归
    30         MergeSort(a,b, mid + 1, high);//右侧递归
    31         Merge(a, b, low, mid, high);//排序
    32         //show(b, 0, high+1, "b:");
    33     }
    34 }

     2、两个有序数组归并

    最差情况下:比较次数为 m+n-1

    此时,将数组 arr1 与数组 arr2 中的元素两两比较,将值小的放进数组 arr3, 直到数组 arr3 填满为止。

    因为 arr3 有 m+n 个空位,每次两两比较就放进去一个数,而最后一个剩下的元素可以不用比较直接放进去,所以一共两两比较了 m+n-1 次。

     

    最好情况下:比较次数为 min{m, n}

    有个疑问: 若一个数组为 1, 2,3 ; 另一个数组为 4, 5, 6, 7; 则直接将后一个数组最小的与前一个数组最大的比较,仅需一次比较就行了

    一定要注意,这是简单归并排序,必须从第一个元素比。

    因此,最好情况下,至少需要两两比较一个数组的长度,比较次数为 min{m,n}
    ————————————————
    版权声明:本文为CSDN博主「心态与做事习惯决定人生高度」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/robert_chen1988/article/details/78718395

     1 //双指针法 temp为辅助数组
     2 class Solution {
     3 public:
     4     void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
     5         vector<int> temp(m);
     6         for(int i=0;i<m;i++)//将nums1的值复制到temp中
     7             temp[i]=nums1[i];
     8         nums1.clear();//clear之后size变为0 但容量不变
     9         int p=0,q=0;
    10         while(p<temp.size()&&q<nums2.size())
    11         {
    12             if(temp[p]<nums2[q])nums1.push_back(temp[p++]);//push_back 会使nums1的值++
    13             else nums1.push_back(nums2[q++]);
    14         }
    15         while(p<temp.size())
    16             nums1.push_back(temp[p++]);
    17         while(q<nums2.size())
    18             nums1.push_back(nums2[q++]);
    19         temp.swap(temp);//释放temp
    20     }
    21 };

    五、基数排序

     1 /**
     2 **  基数排序 基于关键字各位的大小进行排序
     3 **  r代表基数 10进制时r=10
     4 **  d代表最大数据的位数 
     5 **  空间复杂度O(r)
     6 **  时间复杂度:O(d(n+r)) 需要d趟分配和收集 一趟分配是O(n) 一趟收集是O(r)
     7 **  稳定性:稳定
     8 **  适用于k不是很大 序列较为集中 排序速度快于任何比较排序
     9 **/
    10 int maxbit(vector<int>& data) //辅助函数,求数据的最大位数
    11 {
    12     int d = 1; //保存最大的位数
    13     int p = 10;
    14     int len = data.size();
    15     for (int i = 0; i < len; i++)
    16     {
    17         while (data[i] >= p)
    18         {
    19             p *= 10;
    20             ++d;
    21         }
    22     }
    23     return d;
    24 }
    25 void RadixSort(vector<int> &data) //基数排序
    26 {
    27     int n = data.size();
    28     int r = 10;//基数r 此处为10
    29     vector<queue<int>> bucket(r);//r个队列 辅助存储空间
    30     int d = maxbit(data);
    31     int radix=1;//用来计算放在那个桶
    32     for (int k = 0; k < d; k++)//进行d趟分配和收集
    33     {
    34         //分配 
    35         for (int i = 0; i < n; i++)
    36             bucket[(data[i] / radix) % 10].push(data[i]);//(data[i] / radix) % 10指的是某数的某一位
    37         //收集
    38         int index = 0;//初始化数组大小
    39         for (int i = 0; i < r; i++)
    40         {
    41             while (!bucket[i].empty())
    42             {
    43                 data[index++] = bucket[i].front();
    44                 bucket[i].pop();
    45             }
    46         }
    47         radix *= 10;
    48     }
    49 }
  • 相关阅读:
    spring cloud 和 阿里微服务spring cloud Alibaba
    为WPF中的ContentControl设置背景色
    java RSA 解密
    java OA系统 自定义表单 流程审批 电子印章 手写文字识别 电子签名 即时通讯
    Hystrix 配置参数全解析
    spring cloud 2020 gateway 报错503
    Spring Boot 配置 Quartz 定时任务
    Mybatis 整合 ehcache缓存
    Springboot 整合阿里数据库连接池 druid
    java OA系统 自定义表单 流程审批 电子印章 手写文字识别 电子签名 即时通讯
  • 原文地址:https://www.cnblogs.com/lancelee98/p/11663379.html
Copyright © 2011-2022 走看看