When it is required not to bring the board and use STL, the code can only be torn by hand.
Moreover, the hand-written words indicate a deeper understanding of the knowledge points and a faster speed during the game. (Although I retired)
However, it is likely that you will not be able to use STL during the written interview. This time will test your understanding of the basic algorithm (memory)
Generally speaking, the code that appears in the advanced guide of the algorithm competition must be tearable
Basic algorithm
Dichotomy
- Find the smallest of $ \ geq x $ in monotonically increasing sequence \ (a \)
while(l<r){
int mid = (l+r)>>1;/*右移运算 相当于除2并且向下取整*/
if(a[mid]>=x) r=mid;
else l=mid+1;
}
return a[l];
- Find the largest of the numbers in \ (\ leq x \) in the monotonically increasing sequence \ (a \)
while(l<r){
int mid = (l+r+1)>>1;
if(a[mid]<=x) l=mid;
else r=mid-1;
}
return a[l];
If the answer with the smallest answer is selected, initialize \ (l = L, r = R + 1 \) , if the answer is \ (R + 1 \) then it is not found
If you choose the one that satisfies the largest answer, initialize \ (l = L-1, r = R \) , if the answer is \ (L-1 \) then it is not found
Like in upper_bound()
and lower_bound()
, if it does not exist, it does return the last subscript +1, vector returnsend()
Sort
Bubble Sort
Adjacent comparison, large right shift
void bubble_sort() {
for (int i = n - 1; i > 0; --i) {
bool flag = false;
for (int j = 0; j + 1 <= i; ++j)
if (a[j] > a[j + 1]) {
std::swap(a[j], a[j + 1]);
flag = true;
}
if (!flag) return ;
}
return ;
}
Select sort
Select a key, the largest right shift
void selection_sort() {
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j < n; ++j) {
if(a[i] > a[j])
std::swap(a[i], a[j]);
}
}
}
Insert sort
In the case of order in the front, select the key to insert in the position where it should be
int i,j,key;
for(i = 2;i <= n;++i){
key = a[i];
j = i - 1;
while(j > 0 && a[j] > key){
a[j+1] = a[j];
j--;
}
a[j] = key;
}
Quick sort
The number between the selected, the left must be smaller than it, the right must be larger than it, and then recursive divide and conquer
void quick_sort(int l, int r) {
if (l >=r ) return ;
int i = l - 1, j = r + 1, x = a[(l + r) >> 1];
while (i < j) {
do j--; while (a[j] > x);
do i++; while (a[i] < x);
if (i < j) std::swap(a[i], a[j]);
else quick_sort(l, j), quick_sort(j + 1, r);
}
}
Merge sort
int merge_sort(int l, int r) {
if (l >= r) return 0;
int mid = (l + r) >> 1, res = 0;
res += merge_sort(l, mid);
res += merge_sort(mid + 1, r);
int i = l, j = mid + 1, cnt = 0;
while (i <= mid && j <= r)
if (a[i] <= a[j])
b[cnt++] = a[i++];
else {
res += mid - i + 1;
b[cnt++] = a[j++];
}
while (i <= mid) b[cnt++] = a[i++];
while (j <= r) b[cnt++] = a[j++];
for (int i = l, j = 0; j < cnt; ++i, ++j) a[i] = b[j];
return res;
}
Heap sort
Implementation of large root heap: x node must be greater than the child node
struct heap{
int *A;
int length;
heap(int n){
length = n;
A = new int[n+5];
for(int i=1;i<=n;++i){
cin>>A[i];
}
}
void Max_Heapify(int pos){
int l = pos*2;
int r = pos*2+1;
int largest;
if(l <= length && A[l] > A[pos]){
largest = l;
}else{
largest = pos;
}
if(r <= length && A[r] > A[largest]){
largest = r;
}
if(largest != pos){
swap(A[pos],A[largest]);
Max_Heapify(largest);
}
}
void build_max_heap(){
for(int i=length/2;i>=1;--i){
Max_Heapify(i);
}
}
void heapSort(){
build_max_heap();
int n = length;
for(int i=length;i>=2;--i){
swap(A[1],A[i]);
length--;
Max_Heapify(1);
}
for(int i=1;i<=n;++i){
cout<<A[i]<<" ";
}
}
};
Hash
Just remember a formula:
\ (H (x) \) : the hash value of the string \ (x \)
\ (p \) : hexadecimal number
\ (p ^ x \) means the value shifted to the left by \ (x \) bits under \ (p \) base
As long as the code is pre-processed \ (f (x) \) : prefix hash value \ (p (x) \) : \ (p ^ x \)
Large numbers
Most interviewers said they want to be object-oriented? Is it readable?
Use three stacks to maintain the addition of large numbers (the interviewer said the readable method)
#include<bits/stdc++.h>
using namespace std;
class bigNum{
private:
stack<int>num1,num2,sum;
public:
char *Num;
bigNum(char *s){
Num = s;
}
bigNum* add(bigNum* &right){
for(int i=0;Num[i];++i){
num1.push(Num[i] - '0');
}
for(int i=0;right->Num[i];++i){
num2.push(right->Num[i] - '0');
}
int flag = 0,tmp,x,y;
while(!num1.empty() || !num2.empty()){
x = num1.empty() == 1 ? 0:num1.top();
y = num2.empty() == 1 ? 0:num2.top();
tmp = x + y + flag;
flag = tmp/10;
sum.push(tmp%10);
if(!num1.empty()) num1.pop();
if(!num2.empty()) num2.pop();
}
if(flag){
sum.push(flag);
}
char *ans = new char[sum.size()+1];
int pos = 0;
while(!sum.empty()){
ans[pos++] = sum.top() + '0';
sum.pop();
}
return new bigNum(ans);
}
void printNum(){
cout<<Num<<endl;
}
};
char a[150],b[150];
int main(){
cin>>a;
cin>>b;
bigNum *left = new bigNum(a);
bigNum *right = new bigNum(b);
bigNum *sum = left ->add(right);
sum ->printNum();
return 0;
}
String
Dictionary tree
void ins(char *str){
int len=strlen(str);
int p=0;
for(int i=0; i<len; i++){
int ch=str[i]-'a';
if(!tire[p][ch])
tire[p][ch]=++ant;
p=tire[p][ch];
}
cnt[p]++;
}
int srh(char *str){
int ans=0;
int len=strlen(str);
int p=0;
for(int i=0; i<len; i++){
int ch=str[i]-'a';
p=tire[p][ch];
if(!p) return ans;
ans+=cnt[p];
}
return ans;
}
In fact, there are pointer writing methods, maybe the interviewer can only understand the pointer
const int maxn = 26;
struct tireNode{
tireNode* next[maxn];
bool endpos;
tire(){
endpos = 0;
}
};
struct tire{
tireNode *root;
tire(){
root = new tireNode();
}
void Insert(char *str){
tireNode *now = root;
while(*str != '\0'){
int pos = *str - 'a';
if(now->next[pos] == nullptr){
now -> next[pos] = new tireNode();
}
now = now -> next[pos];
str++;
}
now -> endpos = 1;
}
bool Find(char *str){
tireNode *now = root;
while(*str != '\0'){
int pos = *str - 'a';
if(now -> next[pos] == nullptr){
return 0;
}
now = now -> next[pos];
str++;
}
return now -> endpos;
}
};
KMP
\ (Next [i] \) : indicates the maximum matching position of the prefix and suffix of the string \ ([0 ... i] \) , neither the suffix nor the suffix can include itself
So \ (Next [0] =-1 \) , because the first character must not have this maximum matching position
So \ (i \) starts from 1, \ (j \) starts from -1
According to the loop invariant, maintained in the course of the loop, $ Next [i] \ (finally must be equal to \) j \ (, ie \) [0 ... j] \ (must be the longest with \) i $ Is the string whose ending suffix matches the pattern string prefix.
void get_Next(char *p){
Next[0] = -1;
int i = 1,j = -1;
while(p[i] != '\0'){
while(j != -1 && p[i] != p[j+1]) j = Next[j];
if(p[i] == p[j+1])++j;
Next[i++] = j;
}
}
int kmp(char *s,char *p){
int i = 0,j = -1;
int ans = 0;
while(s[i] != '\0'){
while(j != -1 && ( p[j+1] == '\0' || s[i] != p[j+1]))j = Next[j];
if(s[i] == p[j+1])++j;
f[i++] = j;
if(p[j+1] == '\0'){
//视情况
}
}
return ans;
}
AC automaton
The key point is to find the first \ (tr [u] [i] \) that is not empty from the fafail sequence. In order to save time, path compression is used.
namespace AC {
int tr[N][26], tot;
int e[N], fail[N];
void insert(char *s) {
int u = 0;
for (int i = 1; s[i]; ++i) {
if (!tr[u][s[i] - 'a']) tr[u][s[i] - 'a'] = ++tot;
u = tr[u][s[i] - 'a'];
}
e[u]++;
}
queue<int> q;
void build() {
for (int i = 0; i < 26; ++i) {
if (tr[0][i]) {
q.push(tr[0][i]);
}
}
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = 0; i < 26; ++i) {
if (tr[u][i]) {
fail[tr[u][i]] = tr[fail[u]][i];
q.push(tr[u][i]);
} else {
tr[u][i] = tr[fail[u]][i];
}
}
}
}
int query(char *t) {
int u = 0, res = 0;
for (int i = 1; t[i]; i++) {
u = tr[u][t[i] - 'a'];
for (int j = u; j && e[j] != -1; j = fail[j]) {
res += e[j], e[j] = -1;
}
}
return res;
}
}
mathematics
Linear sieve
Adoption properties: every non-prime number has a smallest prime factor
void get_prime(){
int pos = 0;
for(int i=2;i<N;++i){
if(!check[i]) {
Prime[pos++]=i;
}
for(j=0;j < pos && i*Prime[j] < N;++j){
check[i * Prime[j]] = true;
if(i % Prime[j] == 0) {
break;
}
}
}
}
Approximate number
Multiple method to get positive divisor set
for(int i = 1;i <= n;++i){
for(int j = 1;j <= n/i;++j){
factor[i*j].push_back(i);
}
}
Divisor of a single number: try division
for(int i = 1;i * i <= n;++i){
if(n % i == 0){
factor[++m] = i;
if(i != n/i) factor[++m] = n/i;
}
}
data structure
Balance tree
At least it will be flipped, if not the root node, you canRotate
inline void Rotate(int x) {
int y = s[x].fa, z = s[y].fa, chk = get(x);
//y与x的子节点相连
s[y].ch[chk] = s[x].ch[chk ^ 1];
s[s[x].ch[chk ^ 1]].fa = y;
//x与y父子相连
s[x].ch[chk ^ 1] = y;
s[y].fa = x;
// x与y的原来的父亲z相连
s[x].fa = z;
if(z) s[z].ch[y == s[z].ch[1]] = x;
//只有x和y的sz变化了
maintain(y);
maintain(x);
}
Tree array
int ask(int x){
int ans = 0;
for(;x;x -= x & -x) ans += c[x];
return ans;
}
void add(int x,int y){
for(;x <= N;x += x & -x) c[x] += y;
}
Line tree
struct node{
int l,r;
int sum,add;//和,延迟标记
}t[maxn << 2];
void build(int p,int l,int r){//后序遍历
t[p].l = l,t[p].r;
if(l == r) {t[p].sum = s[l];return;}
int mid = (l+r) >> 1;
build(p << 1,l,mid);
build(p << 1 |1,mid + 1,r);
t[p].sum = t[p << 1].sum + t[p << 1 | 1].sum;//更新来自子结点的答案
}
void spread(int p){
if(add(p)){//如果有标记
t[p << 1].sum = t[p].add * (t[p << 1].r - t[p << 1].l + 1);
t[p << 1 | 1].sum = t[p].add * (t[p << 1 | 1].r - t[p << 1 | 1].l + 1);
t[p << 1].add += t[p].add;
t[p << 1 | 1].add += t[p].add;
t[p].add;
}
}
void change(int p,int l,int r,int d){
/*if(t[p].l == t[p].r){
t[p].sum += d;
}如果不用延迟标记
*/
if(l <= t[p].l && r >= t[p].r){
t[p].sum += d * (t[p].r - t[p].l + 1);
t[p].add += d;
return;
}
spread(p);
int mid = t[p].l + t[p].r >> 1;
if(l <= mid) change(p << 1,l,r,d);
if(r > mid) change(p << 1 | 1,l,r,d);
t[p].sum = t[p << 1].sum + t[p << 1 | 1].sum;//更新来自子结点的答案
}
long long ask(int p,int l,int r){
if(l <= t[p].l && r >= t[p].r){
return t[p].sum;
}
long long ans = 0;
int mid = (t[p].l + t[p].r) >> 1;
if(l <= mid) ans += ask(p << 1,l,r);
if(r > mid) ans += ask(p << 1|1,l,r);
return ans;
}
Graph Theory
Shortest dij
- Initialize d [1] = 0, the remaining nodes are positive infinity
- Find an unmarked node with the smallest \ (d [x] \) \ (x \) and marked node \ (x \)
- Scan all the outgoing edges of the node \ (x \) \ ((x, y, z) \) , if \ (d [y]> d [x] + z \) , use \ (d [x] + z \) Update \ (y \)
memset(d,inf,sizeof(d));
d[1] = 0;
q.push(make_pair(0,1));
while(!q.empty()){
int x = q.top().second;
q.pop();
if(v[x]) continue;
v[x] = 1;
for(int i=head[x];i;i=e[i].next){
int y = e[i].to;
int z = e[i].val;
if(d[y] > d[x] + z){
d[y] = d[x] + z;
q.push(make_pair(-d[y],y));
}
}
}
Minimum spanning tree
- Establish and check sets, each point constitutes a set
- Sort all the edges by weight from small to large, and scan each edge in turn \ ((x, y, z) \)
- If \ (x, y \) belongs to the same set (connected), then ignore this edge
- Otherwise, merge the set where \ (x, y \) is located and accumulate \ (z \) into the answer
int get(int x){
if (x == fa[x]) return x;
return fa[x] = get(fa[x]);
}
void solve(){
for(int i=1;i<=n;++i) fa[i] = i;//1
sort(e + 1,e + 1 + m,cmp);//2
int ans = 0;
for(int i = 1;i <= m;++i){
int x = get(e[i].x);
int y = get(e[i].y);
if(x != y){
fa[x] = y;
ans += e[i].z;
}
}
}
Hungarian algorithm
bool dfs(int x){
for(int i=head[x];i;i=e[i].next) {
if(!vis[y = e[i].to]){
vis[y] = 1;
if(!match[y] || dfs(match[y])) {
match[y] = x;
return true;
}
}
}
}
for(int i = 1;i <= n; ++i){
memset(vis,0,sizeof(vis));
if (dfs(i)) {ans++;}
}