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 }
  • 相关阅读:
    Hander
    N皇后问题--递归回溯
    NYOJ-571 整数划分(三)
    递归--递推之组合数
    深搜最基础题---全排列And组合数
    递归---NYOJ-176 整数划分(二)和NYOJ-279队花的烦恼二
    递归---NYOJ-90整数划分(一)
    线段树---HDU2795Billboard
    输入输出外挂
    线段树---HDU1394Minimum Inversion Number
  • 原文地址:https://www.cnblogs.com/lancelee98/p/11663379.html
Copyright © 2011-2022 走看看