(四)排序【C++刷题】
排序数组
Leetcode:https://leetcode-cn.com/problems/sort-an-array/
1.问题描述
给定一个整数数组nums,将该数组升序排列。
2.输入输出
- Input:[5, 2, 3, 1]
- Output:[1, 2, 3, 5]
3.算法分析
1.【选择排序】首先找到数组中最小的那个元素,其次,将它和数组中第一个元素交换位置(如果第一个元素就是最小元素那么它和子集交换)。再次,在剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。如此循环往复。
- 时间复杂度:\(O(n^2)\)
- 空间复杂度:\(O(1)\)
2.【插入排序】将每个元素插入到其他已经有序的元素中的适当位置。为了给插入的元素腾出空间,需要将其余所有元素在插入之前都向右移动一位。
- 时间复杂度:\(O(n)-O(n^2)\)取决于输入元素的排列情况
- 空间复杂度:\(O(1)\)
3.【希尔排序】是数组中任意间隔h的元素都是有序的,对于每个h,用插入排序将h个子数组独立地排序。h从n/3开始递减至1。
- 时间复杂度:\(O(nlogn)\)
- 空间复杂度:\(O(1)\)
4.【归并排序】分治思想,将大数组划分成两个子数组进行排序,通过归并两个子数组来将整个数组排序,原地归并需要将涉及到地元素复制到一个辅助数组中,再将归并的结果放回原数组。
- 时间复杂度:\(O(nlogn)\)
- 空间复杂度:\(O(n)\)
5.【快速排序】将一个数组分成两个子数组,将两部分独立地排序。切分地位置取决于数组的内容。先随意取第一个元素作为切分元素,从数组左端开始向右扫描直到找到一个大于等于它的元素,在从数组右端开始向左扫描找到一个小于等于它的元素,交换这两个元素的位置,直到两个指针相遇,最后和切分元素交换位置即可。
- 时间复杂度:\(O(nlogn)\)
- 空间复杂度:\(O(logn)\)
6.【堆排序】在堆的构造阶段中,将原始数组重新组织安排进一个堆中,在下沉排序阶段,从堆中按递减顺序取出所有元素并得到排序结果。
- 时间复杂度:\(O(nlogn)\)
- 空间复杂度:\(O(1)\)
7.【冒泡排序】一边比较一边向后两两交换,将最大值冒泡到最后一位,当不再发生交换时排序停止。
- 时间复杂度:\(O(n^2)\)
- 空间复杂度:\(O(1)\)
8.【桶排序】实现线性排序,但当元素间值的大小有较大差距时会带来内存空间较大浪费。首先,找出待排序列中的最大元素max,申请内存为max+1的桶(数组)并初始化为0;然后,遍历排序数组,并依次将每个元素作为下标的桶元素值自增1,最后遍历桶元素,并依次将值非0的元素下标值载入排序数列(桶元素>1表明有值大小相等的元素,此时依次将他们载入排序序列),遍历完成,排序序列为有序序列。
- 时间复杂度:\(O(n)\)
- 空间复杂度:\(O(n)\)
9.【二分(折半)插入排序】顺序的把待排序的序列中的各个元素按关键字的大小,通过折半查找插入到已排序的序列的适当位置。
- 时间复杂度:\(O(n^2)\)
- 空间复杂度:\(O(1)\)
10.【基数排序】十个桶,每次取数的(个十百)…位放入桶中,重复这个过程
- 时间复杂度:\(O(n)\)
- 空间复杂度:\(O(n)\)
4.编程实现
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
class Solution{
public:
void selectSort(vector<int> &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)
int tmp = nums[i];
nums[i] = nums[minIndex];
nums[minIndex] = tmp;
}
}
void insertSort(vector<int> &nums) {
int n = nums.size();
for (int i = 1; i < n; i++) {
for (int j = i; j > 0 && nums[j] < nums[j-1]; j--) {
// 交换元素 swap(nums[j], nums[j-1])
int tmp = nums[j];
nums[j] = nums[j-1];
nums[j-1] = tmp;
}
}
}
void shellSort(vector<int> &nums) {
int n = nums.size();
int h = 1;
while (h < n/3) h = 3*h + 1;
while (h >= 1) { // 将数组变为h有序
for (int i = h; i < n; i++) { // 将nums[i] 插入到nums[i-h], nums[i-2h], nums[i-3h]…中
for (int j = i; j >= h && nums[j] < nums[j-h]; j -= h) {
// 交换nums[j]和nums[j-h]——swap(nums[j]和nums[j-h])
int tmp = nums[j];
nums[j] = nums[j-h];
nums[j-h] = tmp;
}
}
h = h/3;
}
}
void mergeSort(vector<int> &nums, int left, int right, vector<int> &temp) {
if (left == right) return; // 递归出口
// 分
int mid = left + (right - left) / 2;
mergeSort(nums, left, mid, temp);
mergeSort(nums, mid+1, right, temp);
// 治
int i = left, j = mid+1;
for (int k = left; k <= right; k++) { // 合并
if ((j > right) || (i <= mid && nums[i] <= nums[j])) {
temp[k] = nums[i++];
} else {
temp[k] = nums[j++];
}
}
for (int k = left; k <= right; k++) nums[k] = temp[k]; // 复制
}
void quickSort(vector<int> &nums, int left, int right) {
if (left+1 >= right) return;
int first = left, last = right - 1, key = nums[first];
while (first < last) {
// 从右往左找到第一个大于key的值
while (first < last && nums[last] >= key) --last;
nums[first] = nums[last];
// 从左往右找到第一个小于key的值
while (first < last && nums[first] <= key) ++first;
nums[last] = nums[first];
}
nums[first] = key;
quickSort(nums, left, first);
quickSort(nums, first+1, right);
}
//堆排序
void adjust(vector<int> &nums, int len, int index){
int left = 2 * index + 1; // index的左子节点
int right = 2 * index + 2;// index的右子节点
int maxIdx = index;
if (left < len && nums[left] > nums[maxIdx]) maxIdx = left;
if (right < len && nums[right] > nums[maxIdx]) maxIdx = right;
if (maxIdx != index) {
swap(nums[maxIdx], nums[index]);
adjust(nums, len, maxIdx);
}
}
// 堆排序
void heapSort(vector<int> &nums, int size){
for(int i = size / 2 - 1; i >= 0; i--){
adjust(nums, size, i);
}
for(int i = size - 1; i >= 1; i--){
swap(nums[0], nums[i]);
adjust(nums, i, 0);
}
}
void bubbleSort(vector<int> &nums, int n) {
bool swapped;
for (int i = 1; i < n; i++) {
swapped = false;
for (int j = 1; j < n-i+1; j++) {
if (nums[j] < nums[j-1]) {
swap(nums[j], nums[j-1]);
swapped = true;
}
}
if (!swapped) break;
}
}
void bucketSort (vector<int>& nums) {
int bk[50000 * 2 + 1];
memset(bk, 0, sizeof(bk)); // 清零
for(int i = 0; i < nums.size(); i++){
bk[nums[i] + 50000] += 1;
}
int ar = 0;
for(int i = 0; i < 100001; i++){
for(int j = bk[i]; j > 0; j--){
nums[ar++] = i - 50000;
}
}
}
void hInsertSort(vector<int> &nums, int n) {
int i,j,low,high,mid;
for (i = 0; i < n; i++ ){
int tmp = nums[i];
low = 0; high = i-1;
while(low <= high) {
mid = low + (high - low) / 2;
if(nums[mid] > tmp){
high = mid - 1;
}else{
low = mid + 1;
}
}
for(j = i-1; j >= high+1; j--){
nums[j+1] = nums[j];
}
nums[high+1] = tmp;
}
}
void radixSort(vector<int> &nums) {
int n = nums.size(), max=abs(nums[0]);
for(int i = 1; i < n; i++){
if(max < abs(nums[i]))
max = abs(nums[i]);
}
int w = 0;
while(max > 0) {
max /= 10;
w++;
}
int flag = 1;
vector<int> ans(n);
for(int i = 0; i < w; i++) {
vector<int> bucket(19, 0);
for(int j = 0; j < n; j++) {
int temp = nums[j] / flag % 10 + 9;
bucket[temp]++;
}
for(int j = 1; j < 19; j++)
bucket[j] += bucket[j-1];
for(int j = n-1; j >= 0; j--) {
int temp = nums[j] / flag % 10 + 9;
ans[--bucket[temp]] = nums[j];
}
nums.swap(ans);
flag *= 10;
}
}
vector<int> sortArray(vector<int>& nums) {
int n = nums.size();
// 1.选择排序
// selectSort(nums);
// 2.插入排序
// insertSort(nums);
// 3.希尔排序
// shellSort(nums);
// 4.原地归并排序
// vector<int> temp(n);
// mergeSort(nums, 0, n-1, temp);
// 5.快速排序
// quickSort(nums, 0, n);
// 6.堆排序
// heapSort(nums, n);
// 7.冒泡排序
// bubbleSort(nums, n);
// 8.桶排序
// bucketSort(nums);
// 9.折半插入排序
// hInsertSort(nums, n);
// 10.基数排序
radixSort(nums);
return nums;
}
};
int main() {
vector<int> nums = {2, 1, 5, 3, 4};
Solution sol;
// 排序
sol.sortArray(nums);
for (const int & val: nums) {
cout << val << " ";
}
return 0;
}
数组中的第K个最大元素
leetcode:https://leetcode-cn.com/problems/kth-largest-element-in-an-array/
1.问题描述
给定整数数组 nums和整数 k,请返回数组中第 k个最大的元素。
2.输入输出
- Input:[3,2,1,5,6,4] ,k = 2
- Output:5
3.算法分析
【快速旋转】快速旋转一般用于求解k-th Element问题,只需要找到第k大的枢(pivot)即可,不需要对其左右再进行排序。与快速排序一样,一般需要先打乱数组,注意只需要快排数组大小
- 时间复杂度:O(n)
- 空间复杂度:O(1)
4.编程实现
#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
int len = nums.size()-1;
int value = quickSort(nums, len-k, 0, nums.size()-1);
return value;
}
int quickSelection(vector<int>& nums, int k, int left, int right) {
int first = left, last = right, key = nums[first];
while (first < last) {
while (first < last && nums[last] >= key) --last;
nums[first] = nums[first];
while (first < last && nums[first] <= key) ++first;
nums[last] = nums[first];
}
nums[first] = key;
if (first == k) {
return nums[k];
} else if (first < k) {
return quickSelection(nums, k, first+1, right);
} else {
return quickSelection(nums, k, left, first-1);
}
}
};
int main(){
vector<int> nums = {3, 2, 1, 5, 6, 4};
int k = 2;
Solution sol;
cout << sol.findKthLargest(nums, k) << endl;
return 0;
}