# B-排序(1)- O( N logN )排序算法
B-排序(1)- O( N logN )排序算法
题目
- 数组中的逆序对
- 最小的k个数
- 快排代码
- 归并排序代码
!!! 排序算法解题思路 !!!
看到排序问题,首先看题目的目的。从三种排序算法中做选择。(堆排序、归并排序、快速排序)
- 三种排序都是基于比较的排序方法,时间复杂度都是 O(N*logN) 但是各有各的特点
- 题目要求, 要求排序算法稳定。就选归并排序,merge过程中,先拷贝左侧区域的值就可以稳定
- 题目要求, 要求额外空间复杂度 O(1) 。就选 堆排序。
- 题目要求, 不追求稳定性和额外空间复杂度 ,追求综合指标-就选快速排序。
- 归并排序指标:O(N*logN) O(N) 稳定 (merge的过程需要辅助数组)
- 堆排序指标:O(N*logN) O(1) 不稳定
- 快速排序指标: O(N*logN) O(logN) 不稳定
1 归并排序
- 常规 归并排序问题
- 扩展 小和问题
- 扩展 逆序对问题
题目1 归并排序
#include <iostream>
#include <vector>
#include <string>
using namespace std;
struct Student
{
string _name;
int _grade;
void print()
{
cout << _name << " " << _grade << endl;
}
};
class MergeSort{
public:
// 归并排序-函数
void mergeSort( vector<Student>& stv, bool order )
{
if (stv.size() < 2 || stv.empty())
return;
process(stv, 0, stv.size() - 1, order);
}
// 归并排序递归辅助函数
void process(vector<Student>& stv, int l, int r, bool order)
{
// 递归的出口
if (r == l)
return;
// 求左右的中点
int m = l + ((r - l) >> 1);
// 左侧区域有序
process(stv, l, m, order);
// 右侧区域有序
process(stv, m + 1, r, order);
// 合并左右区域
merge(stv, l, r, m, order);
}
// merge 函数
void merge(vector<Student> &s1, int l, int r, int m, bool order)
{
// 如果需要合并的下标相等,return
if (l == r)
return;
// 定义左右部分指针的起点
int p1 = l;
int p2 = m + 1;
vector<Student> help;
// 当左右区域的下标都没有越界的情况下,升序放置
while (p1 <= m && p2 <= r && order == true)
{
help.push_back(s1[p1]._grade <= s1[p2]._grade ? s1[p1++] : s1[p2++]);
}
// 当左右区域的下标都没有越界的情况下,降序放置
while (p1 <= m && p2 <= r && order == false)
{
help.push_back(s1[p1]._grade >= s1[p2]._grade ? s1[p1++] : s1[p2++]);
}
// 当左侧访问越界,把右侧容器的元素拷贝到help
while (p1 <= m)
{
help.push_back(s1[p1++]);
}
// 当右侧访问越界,把左侧容器的元素拷贝到help
while (p2 <= r)
{
help.push_back(s1[p2++]);
}
// 把辅助空间的元素拷贝到 原容器对应的位置上,merge完成
for (int i = 0; i < help.size(); i++)
{
s1[l + i] = help[i];
}
}
};
题目2 数组中的逆序对
class Solution {
public:
int reversePairs(vector<int>& nums) {
if (nums.empty())
{
return 0;
}
return process(nums , 0 , nums.size()-1);
}
int process( vector<int>& nums , int L ,int R )
{
// base case
if ( L == R )
{
return 0 ;
}
int mid = L + ( (R-L)>>1);
return process( nums , L, mid ) +
process( nums, mid+1, R ) +
merge(nums , L , R , mid );
}
int merge( vector<int>& nums, int l , int r, int m)
{
int res = 0;
vector<int> merged;
int p1 = l ;
int p2 = m+1;
while ( p1 <= m && p2 <= r )
{
if (nums[p2] < nums[p1])
{
merged.push_back(nums[p2++]);
res += m- p1+1;
}else{
merged.push_back(nums[p1++]);
}
}
while ( p1<=m )
{
merged.push_back(nums[p1++]);
}
while (p2 <= r)
{
merged.push_back(nums[p2++]);
}
for ( int i = 0 ; i < merged.size();i++)
{
nums[ l + i ] = merged[i];
}
return res;
}
};
2 堆排序
- 堆的概念和实现
- HeapInsert() 和 Heapify()
- 堆排序- 用已给的数据建立大根堆或小根堆,popMax(); heapify();
最小的k个数
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
if( arr.empty() || arr.size() <= k )
{
return arr;
}
vector<int> ans;
priority_queue<int> heap;
for ( auto i : arr)
{
heap.push(i);
if (heap.size() > k)
{
heap.pop();
}
}
for(int i = 0 ; i < k ; ++i)
{
int m = heap.top();
heap.pop();
ans.push_back(m);
}
return ans;
}
};
例题:
215. 数组中的第K个最大元素
这道题,我用了快排,但是效果不理想
3 快速排序
- 荷兰国旗问题(Partition to 3 )
- 随机快排算法
#include <iostream>
#include <vector>
#include <ctime>
#define random(x) rand()%(x)
using namespace std;
vector<int> partition(vector<int> &nums, int L, int R) ;
void swap(vector<int> &nums, int a, int b);
void process(vector<int> &nums, int L, int R);
void quickSort(vector<int> &nums);
void quickSort(vector<int> &nums)
{
if (nums.size() == 0)
return;
if (nums.size() == 1)
return;
process(nums, 0, nums.size() - 1);
}
void process(vector<int> &nums, int L, int R)
{
if (L < R)
{
srand((int) time(0));
int target = ( random(R - L + 1) ) + L;
swap(nums , target , R );
vector<int> p = partition( nums, L, R );
process(nums, L, p[0] - 1);
process(nums, p[1] + 1, R);
}
}
vector<int> partition(vector<int> &nums, int L, int R )
{
int l = L - 1;
int r = R + 1;
int cur = L;
while (cur < r)
{
if (nums[cur] < nums[R])
{
swap(nums, ++l, cur++);
}
else if (nums[cur] > nums[R])
{
swap(nums, --r, cur);
}
else if (nums[cur] == nums[R])
{
cur++;
}
}
vector<int> index;
index.push_back(l + 1);
index.push_back(r - 1);
return index;
}
void swap(vector<int> &nums, int a, int b)
{
int tmp = nums[a];
nums[a] = nums[b];
nums[b] = tmp;
}
void print ( const vector<int>& nums )
{
for( int i = 0; i < nums.size(); i++)
{
cout << nums[i] << " " ;
}
cout << "End" << endl;
}
int main()
{
vector<int> test;
test.clear();
test.push_back(3);
test.push_back(3);
test.push_back(6);
test.push_back(2);
test.push_back(8);
test.push_back(1);
test.push_back(7);
test.push_back(5);
print(test);
quickSort(test);
print(test);
return 0;
}
题目 1 成绩排序
- 输入任意(用户,成绩)序列,可以获得成绩从高到低或从低到高的排列
- 相同成绩都按先录入排列在前的规则处理。
- 输入多行,先输入要排序的人的个数,然后输入排序方法0(降序)或者1(升序)再分别输入他们的名字和成绩,以一个空格隔开
- 按照指定方式输出名字和成绩,名字和成绩之间以一个空格隔开
// 题目:输入任意(用户,成绩)序列,可以获得成绩从高到低或从低到高的排列,相同成绩都按先录入排列在前的规则处理。
// 输入多行,先输入要排序的人的个数,然后输入排序方法0(降序)或者1(升序)再分别输入他们的名字和成绩,以一个空格隔开
// 按照指定方式输出名字和成绩,名字和成绩之间以一个空格隔开
#include <iostream>
#include <vector>
#include <string>
using namespace std;
struct Student
{
string _name;
int _grade;
void print()
{
cout << _name << " " << _grade << endl;
}
};
void mergeSort(vector<Student> &stv, bool order);
void process(vector<Student> &stv, int l, int r, bool order);
void merge(vector<Student> &s1, int l, int r, int m, bool order);
// 归并排序-函数
void mergeSort(vector<Student> &stv, bool order)
{
if (stv.size() < 2 || stv.empty())
return;
process(stv, 0, stv.size() - 1, order);
}
// 归并排序递归辅助函数
void process(vector<Student> &stv, int l, int r, bool order)
{
// 递归的出口
if (r == l)
return;
// 求左右的中点
int m = l + ((r - l) >> 1);
// 左侧区域有序
process(stv, l, m, order);
// 右侧区域有序
process(stv, m + 1, r, order);
// 合并左右区域
merge(stv, l, r, m, order);
}
// 归并两个vector
void merge(vector<Student> &s1, int l, int r, int m, bool order)
{
// 如果需要合并的下标相等,return
if (l == r)
return;
// 定义左右部分指针的起点
int p1 = l;
int p2 = m + 1;
vector<Student> help;
// 当左右区域的下标都没有越界的情况下,升序放置
while (p1 <= m && p2 <= r && order == true)
{
help.push_back(s1[p1]._grade <= s1[p2]._grade ? s1[p1++] : s1[p2++]);
}
// 当左右区域的下标都没有越界的情况下,降序放置
while (p1 <= m && p2 <= r && order == false)
{
help.push_back(s1[p1]._grade >= s1[p2]._grade ? s1[p1++] : s1[p2++]);
}
// 当左侧访问越界,把右侧容器的元素拷贝到help
while (p1 <= m)
{
help.push_back(s1[p1++]);
}
// 当右侧访问越界,把左侧容器的元素拷贝到help
while (p2 <= r)
{
help.push_back(s1[p2++]);
}
// 把辅助空间的元素拷贝到 原容器对应的位置上,merge完成
for (int i = 0; i < help.size(); i++)
{
s1[l + i] = help[i];
}
}
int main()
{
vector<Student> students;
int num;
bool isuporder;
Student student;
// NowCode 的 oj 需要输入多组测试用例,配合一下,循环输入...
while (cin >> num )
{
cin >> isuporder;
if (num <= 0)
return -1;
int i = 0;
// 输入待排序的数组
// cout << "Input the data : " << endl;
while ( i++ < num && cin >> student._name )
{
cin >> student._grade ;
students.push_back(student);
}
// cout << "Input over " << endl;
// 调用函数排序
mergeSort(students, isuporder);
// 打印排序好的数组
for (int j = 0; j < students.size(); j++)
{
students[j].print();
}
// 同样由于需要测试多组示例,每次结束后清空容器,否则出错...
students.clear();
}
return 0;
}
本文来自博客园,作者:longlongban,转载请注明原文链接:https://www.cnblogs.com/jiangxinyu1/p/12407662.html
简单学习分享,如有错误欢迎指正