快速排序算法(快排)O(N*logN) + O(1)
非稳定(即,排序后的相对顺序和排序之前有可能不同)
快排中的边界处理
选取每次快排过程中的 目标数值 的时候,要选取 中位数向上取整 target = data[ (L+R+1)/2 ],避免仅包含两个数组的无限循环。
#include <iostream>
#include <vector>
using namespace std;
template<typename T>
void quickSort(vector<T> & data, int l, int r){
// 需要重写 = 和 < 、 > 运算符 有可能需要重写swap()函数
if( l >= r ) return;
T x = data[(l+r+1)/2];
int left = l - 1, right = r + 1;
while(left < right){
do ++left; while(data[left] < x) ;
do --right; while(data[right] > x) ;
if( left < right ) swap(data[left], data[right]);
}
quickSort(data, l, left-1);
quickSort(data, left, r);
}
快速选择算法 O(N) + O(1)
用于求数组排序后的 第k个数
与快排类似,但是每次只选择一半,即k所在的那一半进行迭代
#include <iostream>
#include <vector>
using namespace std;
template<typename T>
T quickSelect(vector<T> & data,int l,int r,int k){
//重写 T 的 比较符 >、<
if(l == r) return data[l];
T x = data[ (l + r) / 2 ];
int left = l - 1, right = r + 1;
while(left < right){
while(data[++ left] < x) continue;
while(data[-- right] > x) continue;
if(left < right) swap(data[left], data[right]);
}
int lLen = right - l +1;
if(lLen >= k){
return quickSelect(data, l, right, k);
}
return quickSelect(data, right+1, r, k-lLen);
}
归并排序算法 O(N*logN) + O(N)
稳定(即,排序后的相对顺序和排序之前相同)
#include <iostream>
#include <vector>
using namespace std;
template <class T>
void mergeSort(vector<T> & data, int L, int R){
if(L >= R) return;
int mid = L + ((R - L) >> 1);
mergeSort(data, L, mid);
mergeSort(data, mid+1, R);
int idx1 = L, idx2 = mid + 1;
vector<T> tpdata(R - L + 1);
int tpidx = 0;
while(idx1 <= mid && idx2 <= R)
if(data[idx1] <= data[idx2]) tpdata[tpidx ++] = data[idx1 ++];
else tpdata[tpidx ++] = data[idx2 ++];
while(idx1 <= mid) tpdata[tpidx ++] = data[idx1 ++];
while(idx2 <= R) tpdata[tpidx ++] = data[idx2 ++];
for(int i = L; i <= R; i ++) data[i] = tpdata[i - L];
}
逆序对数量 O(N*logN) + O(N)
返回一个数组中的逆序对数量。基于归并排序。
#include <iostream>
#include <vector>
using namespace std;
template<class T>
long long reversePair(vector<T> & data, int L, int R){
if(L >= R) return 0;
int mid = L + R >> 1;
long long res = reversePair(data, L, mid) + reversePair(data, mid + 1, R);
vector<T> tpdata(R - L + 1, 0);
int tpL = L, tpR = mid + 1, idx = 0;
while(tpL <= mid && tpR <= R){
if(data[tpL] <= data[tpR]) tpdata[idx ++] = data[tpL ++];
else{
tpdata[idx ++] = data[tpR ++];
res += mid - tpL + 1;
}
}
while(tpL <= mid) tpdata[idx ++] = data[tpL ++];
while(tpR <= R) tpdata[idx ++] = data[tpR ++];
for(int i = L, idx = 0; i <= R; i ++){
data[i] = tpdata[idx ++];
}
return res;
}
二分查找法 O(logN) O(1)
vector<T>必须有序,单调递增
containS :若数组包含target,则返回索引,否则返回-1
leftBound: 若数组包含target,则返回target的最左位置,否则返回-1
rightBound:若数组包含target,则返回target的最右位置,否则返回-1
还有另外一种规则引出的函数为,返回>=target的最左位置 / 返回<=target的最右位置,则只需要把函数最后的if(data[l] == target)删掉即可。
二分法中的边界处理
二分找最右边界的时候,要选取中位数向上取整 mid = ( L + R + 1) / 2,避免仅含两个数的数组无限循环。
#include <iostream>
#include <vector>
using namespace std;
template<class T>
int containS(vector<T> &data,T target){
//重写T的 == < >
int l = 0, r = data.size() - 1;
while(l < r){
int mid = l+ ((r - l) >> 1);
if(data[mid]>target) r = mid - 1;
else if(data[mid] == target) return mid;
else l = mid + 1;
}
if(data[l] == target) return l;
return -1;
}
template<class T>
int leftBound(vector<T> &data,T target){
//重写T的 == < >
int l = 0, r = data.size() - 1;
while(l < r){
int mid = l+ ((r - l) >> 1);
if(data[mid]>=target) r = mid;
else l = mid + 1;
}
if(data[l] == target) return l;
return -1;
}
template<class T>
int rightBound(vector<T> &data,T target){
//重写T的 == < >
int l = 0, r = data.size() - 1;
while(l < r){
int mid = l+ ((r - l + 1) >> 1);
if(data[mid] <= target) l = mid;
else r = mid - 1;
}
if(data[l] == target) return l;
return -1;
}