#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 }