测试排序的题目:
912. 排序数组 - 力扣(LeetCode)
堕落的做法:
class Solution
{
public:vector sortArray(vector& nums) {sort(nums.begin(),nums.end());return nums;}
};
视频推荐:
91.基数排序的原理_哔哩哔哩_bilibili
代码推荐:
【C++】十大排序(冒泡、选择、插入、希尔、归并、快排、堆、计数、桶、基数) - 排序数组 - 力扣(LeetCode)
1
;1
~2
,直到排序完成。class Solution
{
public:vector sortArray(vector& nums) {int n = nums.size();for (int i = 0; i < n - 1; ++i) {bool flag = false;for (int j = 0; j < n - 1 - i; ++j) {if (nums[j] > nums[j + 1]) {swap(nums[j], nums[j + 1]);flag = true;} }if (flag == false) break; //优化点:无交换代表已经排好序了}return nums;}
};
注意:本题中使用冒泡排序过不去,会超时
class Solution
{
public:vector sortArray(vector& nums){int n = nums.size();for (int i = 0; i < n; i++){int minindex = i;for (int j = i + 1; j < n; j++){if (nums[j] < nums[minindex]) minindex = j;}swap(nums[i], nums[minindex]);}return nums;}
};
注意:本题中使用选择排序过不去,会超时
3
,直到找到已排序的元素小于或者等于新元素的位置;2
~5
。普通版本:
class Solution
{
public:vector sortArray(vector& nums){int n = nums.size();for (int i = 1; i < n; i++){int currnumber = nums[i];int j = i - 1;for (; j >= 0; j--){if (nums[j] >= currnumber) swap(nums[j], nums[j + 1]);else break;}swap(currnumber, nums[j + 1]);}return nums;}
};
时间复杂度:最好O(n)最坏O(n^2)
空间复杂度:O(1)
class Solution
{
public:vector sortArray(vector& nums) {int n = nums.size();// 分组,最开始时,间隔T为数组的一半for (int T = n / 2; T >= 1; T /= 2) {//对分组进行插入排序for (int i = T; i < n; ++i){shellSort(nums, T, i);}}return nums;}void shellSort(vector& nums, int T, int i) {int j, tmp = nums[i];for (j = i - T; j >= 0 && tmp < nums[j]; j -= T) {nums[j + T] = nums[j];}nums[j + T] = tmp;}
};
第一个通过的排序
class Solution
{
public:vector sortArray(vector& nums){res.resize(nums.size(), 0);MerageSort(nums, 0, nums.size() - 1);return nums;}void MerageSort(vector& nums, int left, int right){if (left >= right) return;int mid = (left + right) / 2;MerageSort(nums, left, mid);MerageSort(nums, mid + 1, right);int l = left, r = mid + 1, i = left;while (l <= mid && r <= right){if (nums[l] < nums[r]) res[i++] = nums[l++];else res[i++] = nums[r++];}while (l <= mid) res[i++] = nums[l++];while (r <= right) res[i++] = nums[r++];for (int i = left; i <= right; i++) nums[i] = res[i];}
private:vector res;
};
归并排序最好情况下:O(nlogn),最坏情况下 O(nlogn),归并排序的空间复杂度 O(n),如果使用的是堆上空间,则操作时间可能更长(分配销毁时间太长)
算法步骤:
基准pivot
;partition
操作:比基准值小的元素放在左边,比基准值大的元素放在右边;recursive
:把小于基准值元素的子数列和大于基准值元素的子数列分别递归排序。普通的快排(选取中间点):
class Solution
{
public:vector sortArray(vector& nums){QuickSort(nums, 0, nums.size() - 1);return nums;}void QuickSort(vector& nums, int left, int right){if (left >= right) return;int x = nums[(left + right) / 2], l = left - 1, r = right + 1;while (l < r){do l++; while (nums[l] < x);do r--; while (nums[r] > x);if (l < r) swap(nums[l], nums[r]);}QuickSort(nums, left, r);QuickSort(nums, r + 1, right);}
};
优化后的快排(随机选点)
class Solution
{
public:vector sortArray(vector& nums){QuickSort(nums, 0, nums.size() - 1);return nums;}void QuickSort(vector& nums, int left, int right){if (left >= right) return;int x = nums[rand() % (right - left + 1) + left], l = left - 1, r = right + 1;while (l < r){do l++; while (nums[l] < x);do r--; while (nums[r] > x);if (l < r) swap(nums[l], nums[r]);}QuickSort(nums, left, r);QuickSort(nums, r + 1, right);}
};
**rand() % (b-a+1)+ a ** 就表示 a~b 之间的一个随机整数。
快速排序最好情况下:O(nlogn) ,最坏情况下退化为选择排序(例如每次选择的都是最大值,之后遍历左边的 n -1 序列):O(n^2)
(R1,R2….Rn)
构建成大顶堆,此堆为初始的无序区;R[1]
与最后一个元素R[n]
交换,此时得到新的无序区(R1,R2,……Rn-1)
和新的有序区(Rn)
,且满足R[1,2…n-1]<=R[n]
;R[1]
可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)
调整为新堆,然后再次将R[1]
与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)
和新的有序区(Rn-1,Rn)
。不断重复此过程直到有序区的元素个数为n-1
,则整个排序过程完成。class Solution
{
public:vector sortArray(vector& nums) {// heapSort 堆排序int n = nums.size();// 将数组整理成大根堆buildMaxHeap(nums);for (int i = n - 1; i >= 1; --i) {// 依次弹出最大元素,放到数组最后,当前排序对应数组大小 - 1swap(nums[0], nums[i]);--n;maxHeapify(nums, 0, n);}return nums;}void buildMaxHeap(vector& nums){int n = nums.size();for (int i = (n - 1) / 2; i >= 0; --i)//从最后一个节点的父节点开始进行堆排序{maxHeapify(nums, i, n);}}void maxHeapify(vector& nums, int i, int n){while (i * 2 + 1 < n){// 代表当前 i 节点的左右儿子;// 超出数组大小则代表当前 i 节点为叶子节点,不需要移位int lSon = 2 * i + 1;int rSon = 2 * i + 2;int large = i;if (lSon < n && nums[lSon] > nums[i]) large = lSon;if (rSon < n && nums[rSon] > nums[large]) large = rSon;if (large != i){swap(nums[i], nums[large]);// 迭代判断对应子节点及其儿子节点的大小关系i = large;}else//若当前根节点和左右节点和维持大根堆则跳出循环{break;}}}
};
什么是堆?堆有哪些特征?
堆是一个可以被看做一棵树的数组对象, 它的形状特征是完全二叉树且是顺序存储,数值特征:根总是优于左孩子和右孩子。
堆中的父结点和孩子结点的下标有什么特征
若 pos 是父亲节点下标(其节点编号是 pos + 1),左孩子的下标就是 2pos +1,右孩子是 2pos + 2
简述堆排序的流程:
1、对N个无序元素的数组简历大根堆
从最后一个父亲节点开始,向前循环
先比较左右孩子,再用胜者与父亲比较,如果孩子胜利,交换父亲和孩子,再重复检查交换后原父亲的稳定性。
2、交换堆顶和末尾元素,缩小堆的规模,从新的根节点出发,重建堆。
3、循环步骤2,堆的规模从n到2.
i
的元素出现的次数,存入数组C
的第i
项;C
中的第一个元素开始,每一项和前一项相加);i
放在新数组的第C(i)
项,每放一个元素就将C(i)
减去1
。class Solution {
public:vector sortArray(vector& nums) {// CountSort 计数排序int n = nums.size();int minNum = INT_MAX, maxNum = INT_MIN;// 找到数组中的最小和最大元素 这里面最小的那个可能为负数for (int i = 0; i < n; ++i) {if (nums[i] < minNum) minNum = nums[i];if (nums[i] > maxNum) maxNum = nums[i];}// 构造计数数组vector counts(maxNum - minNum + 1, 0);for (int i = 0; i < n; ++i) {++counts[nums[i] - minNum];}// 计数排序int index = 0;for (int i = 0; i < counts.size(); ++i) {while (counts[i] != 0) {nums[index++] = i + minNum;counts[i]--;}}return nums;}
};
class Solution
{
public:vector sortArray(vector& nums) {// BucketSort 桶排序int n = nums.size();// 获取数组的最小值和最大值int maxNum = nums[0], minNum = nums[0];for (int i = 1; i < n; ++i) {if (nums[i] > maxNum) maxNum = nums[i];if (nums[i] < minNum) minNum = nums[i];}// 初始化桶int bucketNum = 5, bucketSize = (maxNum - minNum) / bucketNum + 1;vector> buckets(bucketNum, vector(0));// 小至大分桶for (int num : nums) {int bucketIndex = (num - minNum) / bucketSize;buckets[bucketIndex].emplace_back(num);}// 桶内排序for (int i = 0; i < buckets.size(); ++i) {sort(buckets[i].begin(), buckets[i].end());}// 从桶中依次取数int index = 0;for (auto& bucket : buckets) {for (int num : bucket) {nums[index++] = num;}}return nums;}
};
arr
为原始数组,从最低位开始取每个位组成radix
数组;radix
进行计数排序(利用计数排序适用于小范围数的特点);class Solution {public:vector sortArray(vector& nums) {// RadixSort 基数排序int n = nums.size();// 预处理,让所有的数都大于等于0for (int i = 0; i < n; ++i) {nums[i] += 50000; // 50000为最小可能的数组大小}// 找出最大的数字,并获得其最大位数int maxNum = nums[0];for (int i = 0; i < n; ++i) {if (nums[i] > maxNum) maxNum = nums[i];}int num = maxNum, maxLen = 0;while (num) {++maxLen;num /= 10;}// 基数排序,低位优先int divisor = 1;for (int i = 0; i < maxLen; ++i) {radixSort(nums, divisor);divisor *= 10;}// 减去预处理量for (int i = 0; i < n; ++i) {nums[i] -= 50000;}return nums;}void radixSort(vector& nums, int divisor)//divisor 目标位{vector> bucket(10, vector(0, 0));//桶for (int i = 0; i < nums.size(); i++)//根据目标位的大小依次放入桶中{int number = nums[i] / divisor % 10;bucket[number].push_back(nums[i]);}for (int i = 0, index = 0; i < bucket.size(); i++)//从桶中取出并放入到nums中{for (int j = 0; j < bucket[i].size(); j++){nums[index++] = bucket[i][j];}}}
};