常见算法与数据结构总结

常见算法用法及模板总结

算法

排序

排序大部分是一个题的子部分,使其有序,便于其他操作。

  • partition ,用于找出第k个元素,使k的左右部分大小分明
int partition(vector<int> &nums, int l, int h) {
    int i = l, j = h + 1;
    while (true) {
        while (nums[++i] < nums[l] && i < h) ;
        while (nums[--j] > nums[l] && j > l) ;
        if (i >= j) {
            break;
        }
        swap(nums, i, j);
    }
    swap(nums, l, j); //注意是j
    return j;
    }
随机版本
int rp(vector<int> &nums, int l, int h) {
	srand(time(0));
	int r = rand() % (h - l) + l;
	swap(nums[v],nums[l]);
	return partition(nums,l,h);
}
  • 桶排序,处理频率相关

Two Pointer

主要用于遍历数组,指针移动条件明确

贪心

  • 从局部最优可以推到全局最优
  • 区间贪心。
  • 对于边界不好处理的可以加上边界。

搜索: 1.二分查找

  • 其实也属于搜索题,每次可以根据条件劈掉一半,即可使用。最常用有序数组查找、BST、要求第一个大于、小于、大于等于、小于等于元素。
  • binarySearch
while (l < r) {
	int m = l + (r - l) / 2;
	if (true)
		r = m;
	else
		l = m + 1;
}
return l;

int lower_bound(int A[], int l, int r, int x) {
	while (l < r) {
		int m = l + (r - l) / 2;
		if (A[m] >= x)
			r = m;
		else
			l = m + 1;
	}
	return l;
}

int upper_bound(int A[], int l, int r, int x) {
	while (l < r) {
		int m = l + (r - l) / 2;
		if (A[m] > x)
			r = m;
		else
			l = m + 1;
	}
	return l;
}

搜索: 2.DFS

  • 用于穷举每种情况,递归下去,大规模数据容易爆栈。
以dfs + 回溯为例
for (int i = 0; i < vsize; i++) {
	if (true)//满足搜索要求,如标记为1
		dfs();
}
void dfs() {// 返回值视需要而定
	if (true) //满足搜索值要求,如到达目的地,到达规定步数step等,做相应处理
	marked[i] = true;  //标记准备进入下一层dfs,有时可以在原grid标记,视具体情况
	for (auto d : direction) //枚举下次dfs可能走向
		dfs(); //下层
	marked[i] = false; //回溯
}

搜索: 3.BFS

  • 常用如,最短路径,floodfill
queue<Typename> q;
q.push();
while (!q.emtpy()) {
	int size = q.size();
	while (size--) {
		Typename t = q.front();
		处理所得值,如标记,路径加1
		判断t是否有下一层次
		q.push();


		q.pop();
	}
}

递归

  • 可以划分子问题

DP

  • 可以通过上一状态得到现在状态结果,即可使用

  • 背包问题
  1. 01背包问题, 有N件物品,容量V,放入第i件物品费用 C i C_i Ci,价值 W i W_i Wi
    状态方程: d p [ i ] [ v ] = m a x ( d p [ i − 1 ] [ v ] , d p [ i − 1 ] [ v − c i ] + w i ) dp[i][v] = max(dp[i - 1][v], dp[i - 1][v - c_i] + w_i) dp[i][v]=max(dp[i1][v],dp[i1][vci]+wi)
    初始化细节:不要求装满时,dp[0 ~ n]可以均取0,如果要求恰好装满dp[0]为0其他为-INF
    还有个常数项优化
    模板:
c[i]费用, w[i]价值
for (int i = 1; i <= n; i++) {
	for (int j = v; j >= c[i]; j--)
		dp[j] = max(dp[j], dp[j - c[i]] + w[i])
}
  1. 完全背包
    即每种物品无限使用
    状态方程: d p [ i ] [ v ] = m a x ( d p [ i − 1 ] [ v − k ∗ c i ] + k ∗ w i ) dp[i][v] = max(dp[i - 1][v - k * c_i] + k * w_i) dp[i][v]=max(dp[i1][vkci]+kwi)
    简单优化:如果有i,j满足 C i < = C j C_i <= C_j Ci<=Cj W i > = W j W_i >= W_j Wi>=Wj,可以将j直接省去
    模板:
for (int i = 1; i <= n; i++) {
	for (int j = c[i]; j <= v; j++)
		dp[j] = max(dp[j], dp[j - c[i]] + w[i]);
}
  1. 多重背包
    设第i件最多 M i M_i Mi件可用。
    M i M_i Mi件拆成独立的每一件,这就变成了01背包
    优化:利用二进制思想,将第i种物品分成若干件01背包中的物品,其中每件物品有一个系数。这件物品的费用和价值均是原来的费用和价值乘以这个系数。令这些系数分别为 1 , 2 , 2 2 , . . . , 2 ( k − 1 ) , M i − 2 k + 1 1, 2, 2^2, ..., 2^{(k - 1)}, M_i - 2^k + 1 1,2,22,...,2(k1),Mi2k+1,且k是满足 M i − 2 k + 1 > 0 M_i - 2^k + 1 > 0 Mi2k+1>0的最大整数。
    模板:
for (int i = 1; i <= n; i++) {
	for (int j = v; j >= c[i]; j++) {
		for (int k = 1; k <= m[i] && k * w[i] <= j; k++)
			dp[j] = max(dp[j], dp[j - k * c[i]] + k * w[i]);
	}
}
//二进制优化:
// 先处理物品
vector<pair<int, int>> goods;
for (int i = 1; i <= n ; i++) {
	for (int k = 1; k <= m[i]; k *= 2) {
		m[i] -= k;
		goods.push_back(k * c[i], k * w[i]);
	}
	if (m[i] > 0)
		goods.push_ back(m[i] * c[i], m[i] * w[i]);
}

for (auto p : goods) {
	for (int j = v; j >= p.first; j++)
		dp[j] = max(dp[j], dp[j - p.first] + p.second);
}


究极优化:单调队列
我不会。。。

  1. 混合背包
    分情况处理即可

  2. 二维费用背包
    最大重量M,m[i]
    加一重循环即可

for (int i = 1; i <= n; i++) {
	for (int j = v; j >= c[i]; j--) {
		for (int k = M; k >= m[i]; k--)
			dp[j][k] = max(dp[j][k], dp[j - c[i][k - m[i]] + w[i])
	}
}
  1. 分组背包
    有n组,每组最多只能选一个。每组个数s[i]
for (int i = 1; i <= n; i++) {
	for (int j = v; j >= 0; j--) {
		for (int k = 1; k <= s[i]; k++) {
			if (j >= c[i][k])
				dp[j] = max(dp[j], dp[j - c[i][k]] + w[i][k]);
		}
	}
}
  1. 依赖背包
  2. 方案数
    初始状态需要为-INF,体积0的方案数为1

int f[N + 1], g[N +1];
int main () {
	g[0] = 1;
	for (int i = 1; i < N; i++) f[i] = -INF;
	for (int i = 1; i<= n; i++) {
		for(int j = v; j >= c[i]; j--) {
			int t = max(f[j], f[j - c[i]] + w[i]);
			int s = 0;
			if (t == f[j]) s += g[j];
			if (t == f[j - c[i]] + w[i]) s += g[j - c[i]];
			f[j] = t;
			g[j] = s;
		}
	}
	int maxw = 0;
	int re = 0;
	for (int i = 1; i <= N; i++) maxw = max(maxw, f[i]);
	for (int i = 1; i <= N; i++) {
		if (maxw == f[i]) {
			re += g[i];
		}
	}
}
  1. 输出方案
    用最原始dp解
    如果dp[i - 1][m] == dp[i][m]说明没有使用物品i

数学

  • 素数
bool isPrime(int n) {
	if (n <= 1) return false;
	for (int i = 2; i * i <= n; i++) {
		if (n % i == 0)
		return false;
	}
	return true;
}
  • GCD
int gcd (int a,int b) {
	return b == 0 ? a : gcb(b , a % b);
}
  • 大数计算
  • 位操作
    n&(-n) 得到 n 的位级表示中最低的那一位。-n 得到 n 的反码加 1,对于二进制表示 10110100,-n 得到 01001100,相与得到 00000100
    n & (n - 1)操作,可以去除最低位的1

数据结构

链表

  • 注意判所指是否为空

  • 递归
  • BST,中序遍历为有序
  • 前中后序遍历,非递归实现
TreeNode *preOrder(TreeNode *root) {
    stack<TreeNode *>s;
    s.push(root);
    while (!s.empty()) {
   	 TreeNode *temp = s.top();
   	 s.pop();
   	 if (temp == nullptr) continue;
   	 //相应操作,如printf
   	 s.push(root->right);
   	 s.push(root->left);
    }
}

TreeNode *postOrder(TreeNode *root) {
   stack<TreeNode *> s;
   s.push(root);
   while (!s.empty()) {
   	TreeNode *temp = s.top();
   	if (temp == nullptr) continue;
   	//存值v
   	s.push(root->right);
   	s.push(root->left);
   	
   }
   //reverse v
}

TreeNode *inOrder(TreeNode *root) {
   TreeNode *cur = root;
   stack<TreeNode *>s;
   while (cur || !s.empty()) {
   	while (cur) {
   		s.push(cur);
   		cur = cur->left;
   	}
   	TreeNode *temp = s.top();
   	s.pop();
   	cur = temp->right;
   }
}
  • 层次遍历,即BFS
  • 前缀树
  • 线段树

  • 用于匹配算法,括号匹配,先进先出

Hash

  • 映射关系

字符串

数组

  • 元素交换
  • 双指针

图 基本都是模板题

  • 二分图
染色
    int color[101] = {0};
    bool isBipartite(vector<vector<int>>& graph) {
        for (int i = 0; i < graph.size(); i++) {
            if (color[i] == 0 && !dfs(i, 1, graph)) //如果染色且相邻颜色相同即false
                return false;
        }
        return true;
    }
    bool dfs(int cur, int c,vector<vector<int>>& graph) {
        if (color[cur] != 0) return color[cur] == c;
        color[cur] = c;
        for (int n : graph[cur]) {
            if (!dfs(n, -c, graph))
                return false;
        }
        return true;
    }
  • 拓扑排序
        vector<vector<int>> edge(numCourses); //边
        vector<int> in(numCourses); // 入度
        for (auto p : prerequisites) {
            edge[p[1]].push_back(p[0]);
        }
        for (int i = 0; i < numCourses; i++) {
            for (auto n : edge[i])
                in[n]++;
        }
        queue<int> q;
        vector<int> re;
        for (int i = 0; i < numCourses; i++) { //入度为0即可操作
            if (in[i] == 0) {
                re.push_back(i);

                q.push(i);
            }
        }
        while (!q.empty()) {
            int temp = q.front();
            q.pop();
            for (int n : edge[temp]) {
                in[n]--;
                if (in[n] == 0) {
                    q.push(n);
                    re.push_back(n);

                }
            }
        }
  • 并查集
void union(int a,int b) {
	int x = find(a);
	int y = find(b);
	if (x == y)
		return ;
	p[y] = x;
}
int find(int x) {
	return x == p[x]? x : find(p[x]);
}

你可能感兴趣的:(DSA,DSA,数据结构与算法)