1.堆排序(建堆与堆的操作)
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <vector>
using namespace std ;
void heapInsert(vector<int> &a, int index){ ///建立大根堆,将数向上浮
while(a[index] > a[(index - 1) / 2]){
swap(a[index],a[(index - 1) / 2]) ;
index = (index - 1) / 2 ;
}
return ;
}
void heapify(vector<int> &a, int index , int size){ ///将数向下沉
int left = index * 2 + 1 ;
while ( left < size ){
// int largest = left + 1 < size && a[left] > a[left+1] ? left : left+1 ; ///当 && 左右两边为真时才可以,且要对应,此种写法会导致错误
int largest = left + 1 < size && a[left+1] > a[left] ? left + 1 : left ;
largest = a[largest] > a[index] ? largest : index ;
if ( largest == index ) break ;
swap( a[largest] , a[index] ) ;
index = largest ;
left = index * 2 + 1 ;
}
return ;
}
void heap_sort(vector<int> &a){
for ( int i = 0 ; i < a.size() ; i ++ ){
heapInsert( a ,i ) ;
}
int size = a.size() ;
swap(a[0],a[--size]) ; ///size每次减1,缩小范围
while ( size > 0 ){
heapify(a,0,size) ;
swap(a[0],a[--size]) ;
}
return ;
}
(可用来解决逆序对问题)
2.快速排序与随机快排的简单实现
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std ;
pair<int,int>partition( vector<int> &a ,int l, int r ){///快排始终以最后一个值作为基准值来进行比对,递归到底
int less = l-1,more = r ;
while ( l < more ){
if ( a[l] < a[r] ){
swap(a[l++],a[++less]) ; /// <=> l++ ;less++ ; 和自身交换;
}
else if ( a[l] > a[r] ){
swap(a[l],a[--more]) ;
}
else{
l ++ ;
}
}
swap(a[more],a[r]) ; ///因为始终以最后一个数为基准,交换后彻底使中间部分等于基准值
return make_pair(less+1,more); ///返回两个数用pari(相当于结构体) ,返回相等的区域的下标
}
void quickSort(vector<int> &a , int l , int r ){
if ( l < r ){
swap(a[l +rand()%(int)(r-l+1)],a[r]) ; ///加上这一句后变成随机快排,比不加快,在(l,r)范围内随机寻找一个数与a[r]交换,使随机值作为a[r],即基准值
pair<int,int> p = partition(a,l,r) ;
quickSort(a,l,p.first-1) ; ///p.first-1为小于刚刚的基准值的下标
quickSort(a,p.second+1,r) ; ///p.second为大于刚刚的基准值的下标
} ///中间就是等于基准值的部分
return ;
}
3.归并排序(Merge Sort)
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <vector>
using namespace std ;
void merge(vector<int> &a , int l , int mid , int r){
int help[r-l+1] ;
int p1 = l , p2 = mid + 1 ;
int k = 0 ;
while(p1 <= mid && p2 <= r){
help[k++] = a[p1] < a[p2] ? a[p1++]:a[p2++] ;
}
while(p1 <= mid){
help[k++] = a[p1++] ;
}
while(p2 <= r){
help[k++] = a[p2++] ;
}
for ( int i = 0 ; i < r-l+1 ; i ++ ){
a[l+i] = help[i] ;
}
return ;
}
void merge_sort(vector<int> &a, int l , int r){
if( l == r ) return ;
int mid = l + (( r - l ) >> 1) ;
merge_sort(a,l,mid) ;
merge_sort(a,mid+1,r) ;
merge(a,l,mid,r) ;
return ;
}
4.线段树(建树与查询区间最值操作)
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
const int MAXN = 1000005 ;
using namespace std ;
int segTree[MAXN] ;
int a[MAXN] ;
void build ( int node , int arr[] , int istart , int iend ){
if ( istart == iend ) segTree[node] = arr[istart] ;
else{
int mid = (istart + iend) / 2 ;
build( node * 2 + 1 , arr , istart , mid ) ;
build( node * 2 + 2 , arr , mid + 1 , iend ) ;
segTree[node] = max( segTree[node * 2 + 1] , segTree[node * 2 + 2] ) ;
}
return ;
}
int query( int node , int left_now , int right_now , int left_query , int right_query ){
if ( left_query > right_now || right_now < left_now ) return 0 ;
if ( left_now >= left_query && right_now <= right_query ) return segTree[node] ;
int mid = ( right_now + left_now ) / 2 ;
return max( query( node * 2 + 1 , left_now , mid , left_query , right_query ) ,
query( node * 2 + 2 , mid + 1 , right_now , left_query , right_query)) ;
}
(线段树参考自 http://www.cnblogs.com/TenosDoIt/p/3453089.html )
5.分解质因数
1.
const int MAXN = 100005 ;
int a[MAXN] ; ///保存质因子的种类
int b[MAXN] ; ///保存某个质因子的系数,也就是某种质因子有多少个
void Fac( int num , int &tot ){
tot = 0 ;
int now = num ;
for ( int i = 2 ; i <= sqrt(num) ; i ++ ){
if ( now % i == 0 ){
a[++tot] = i ;
b[tot] = 0 ;
while ( now % i == 0 ){
++ b[tot] ;
now /= i ;
}
}
}
if ( now != 1 ){
a[++tot] = now ;
b[tot] = 1 ;
}
return ;
}
2.略微加速版
const int MAXN = 100005 ;
int a[MAXN] ; ///保存质因子的种类
int b[MAXN] ; ///保存某个质因子的系数,也就是某种质因子有多少个
void Fac( int num , int &tot ){ //分解质因数模版
tot = 0 ; ///记录质因子个数
int now = num ;
if ( now % 2 == 0 ){ ///略微加速
a[++tot] = 2 ;
b[tot] = 0 ;
while ( now % 2 == 0 ){
++ b[tot] ;
now /= 2 ;
}
}
for ( int i = 3 ; i <= sqrt(num) ; i += 2 ){ ///略微加速
if ( now % i == 0 ){
a[++tot] = i ;
b[tot] = 0 ;
while ( now % i == 0 ){
++ b[tot] ;
now /= i ;
}
}
}
if ( now != 1 ){
a[++tot] = now ;
b[tot] = 1 ;
}
// for ( int i = 1 ; i <= tot ; i ++ ){
// cout << a[i] << " " << b[i] << endl ;
// }
return ;
}
6.极角排序
struct Point{ ///设置点
double x , y ;
}point[55] ;
double cross( Point a , Point b ){ ///设置叉积
return a.x * b.y - a.y * b.x ;
}
bool cmp( Point a , Point b ){ ///设置比较函数
if ( cross( a , b ) > 0 ) return 1 ;
return 0 ;
}
题目POJ 2007
(参考自https://www.cnblogs.com/handsomecui/p/5022240.html)
7.树状数组
预备函数:
定义一个Lowbit函数,返回参数转为二进制后,最后一个1的位置所代表的数值.
例如,Lowbit(34)的返回值将是2;而Lowbit(12)返回4;Lowbit(8)返回8。
将34转为二进制,为0010 0010,这里的"最后一个1"指的是从2^0位往前数,见到的第一个1,也就是2^1位上的1.
程序上,((Not I)+1) And I 表明了最后一位1的值,
仍然以34为例,Not 0010 0010的结果是 1101 1101(221),加一后为 1101 1110(222), 把 0010 0010与1101 1110作AND,得0000 0010(2).
Lowbit的一个简便求法:
int lowbit(int x){
return x&(-x);
}
lowbit函数原理详解请见:http://www.cnblogs.com/circlegg/p/7189676.html
新建:
定义一个数组 BIT,用以维护的前缀和,则:
具体能用以下方式实现:
void build(){
for (int i = 1; i <= MAX_N; i++){
BIT[i] = A[i - 1];
for (int j = i - 2; j >= i - lowbit(i); j--)
BIT[i] += A[j];
}
}
//注:这里的求和将汇集到非终端结点(D00形式)
//BIT中仅非终端结点i值是 第0~i元素的和
//终端结点位置的元素和,将在求和函数中求得
//BIT中的index,比原数组中大1
修改:
假设现在要将的值增加delta,
那么,需要将覆盖的区间包含的值都加上delta.
这个过程可以写成递归,或者普通的循环.
需要计算的次数与数据规模N的二进制位数有关,即这部分的时间复杂度是O(LogN)
修改函数的C++写法
void edit(int i, int delta){
for (int j = i; j <= MAX_N; j += lowbit(j))
BIT[j] += delta;
}
求和:
假设我们需要计算的值.
- 首先,将ans初始化为0,将i初始化为k.
- 将ans的值加上BIT[i]
- 将i的值减去lowbit(i)
- 重复步骤2~3,直到i的值变为0
求和函数的C/C++写法
int sum (int k){
int ans = 0;
for (int i = k; i > 0; i -= lowbit(i))
ans += BIT[i];
return ans;
}
复杂度:
初始化复杂度最优为
单次询问复杂度,其中N为数组大小
单次修改复杂度,其中N为数组大小
空间复杂度
(树状数组参考自wiki)