labuladuo算法小抄模板整理

求模运算的性质:

(a * b) % k = (a % k)(b % k) % k;

框架总结:

回溯算法

result = []
def backtrack(路径, 选择列表):
	if 满足条件列表:
		result.add(路径)
		return 
	
	for选择 in 选择列表:
		做选择
		backtrack(路径, 选择列表)
		撤销选择

二分查找算法

int binarySearch(int[] nums, int target) {
  int left = 0, right = ...;
  
  while (...) {
    int mid = left + (right - left) / 2;
    if (nums[mid] == target) {
      ...
    } else if (nums[mid] < target) {
      left = ...
    } else if (nums[mid] > target) {
      right = ...
    }
  }
  return ...;

滑动窗口算法框架

void slidingWindow(string s, string t) {
  unordered_map need, window;
  for (char c: t) {
    need[c]++;
  }
  int left = 0, right = 0;
  int valid = 0;
  while (right < s.size()) {
    char c = s[right++];
    ... // 进行窗口更新的操作
   	// debug的位置
      
    // 判断左窗口是否要收缩。
    while (window needs shrink) {
      char d = s[left++];
      ...// 进行窗口内数据的一系列更新
    }
  }
}

BFS框架

int BFS(Node start, Node target) {
  queue q;
  set visited;
  
  q.push(start);
  visited.insert(start);
  int step = 0;
  
  while (!q.empty()) {
    int sz = q.size();
    for (int i = 0; i < sz; ++i) {
      Node cur = q.front();
      if (cur == target) {
        return step;
      }
      // 将cur的相邻节点加入队列
      for (Node x : cur.adj()) {
        if (x not in visited) {
          q.push(x);
          visited.insert(x);
        }
      }
    }
    ++step;
  }
}

动态规划的基本框架

for 状态1 in 状态1的所有状态:
	for 状态2 in 状态2的所有状态:
		for ...
			dp[状态1][状态2][...] = 择优(选择1, 选择2, ...)

动态规划子序列问题模板

一维的dp数组:

int n = array.size();
int[] dp = new int[n];
for (int i = 1; i < n; ++i) {
  for (int j = 1; j < i; ++j) {
    dp[i] = 最值(dp[i], dp[j] + ...)
  }
}
// dp[i]的定义:针对单个字符串/数组,如最长递增子序列。
// 在子数组arr[0...i]中,我们要求的子序列(例如:最长递增子序列)的长度是dp[i].

二维的dp数组:

int n = array.size();
int[] dp = new int[n][n];
for (int i = 1; i < n; ++i) {
  for (int j = 1; j < n; ++j) {
    if (arr[i] == arr[j]) {
      dp[i][j] = dp[i][j] + ...;
    } else {
      dp[i][j] = 最值(...);
    }
  }
}
//dp[i][j]的含义:
// case1:涉及两个字符串/数组时:
// 在子数字arr1[0...i]和子数组arr2[0...j]中,我们要求的子序列(如最长公共子序列)的长度为dp[i][j];
// case2:只设计一个字符串/数组时:
// 在arr[i...j]中,我们要求的子序列(例如最长回文子序列)的长度为dp[i][j].

贪心算法调度问题(最多开几个会的类似问题)

int intervalSchedule(vector> intvs) {
  if (intvs.empty()) {
    return 0;
  }
  // 按end升序排序
  sotr(intvs.begin(), intvs.end(), [](vector a, vector b) {
    return b[1] > a[1];
  });
 	// 至少有一个区间不相交
  int count = 1;
  // 排序后,第一个区间就是x
  int x_end = intvs[0][1];
  for (auto interval : intvs) {
    int start = interval[0];
    if (start >= x_end) {
      // 找到下一个选择区间
      ++count;
      x_end = interval[1];
    }
  }
  return count;
}

股票买卖问题

class Solution {
public:
    int maxProfit(int k, vector& prices) {
        // 备注:买进来和卖出去合起来算交易一次。
        if (k < 1 || prices.size() < 2) {
            return 0;
        }
      
      	// dp[i][j][0]:第i天,经过最多j次交易,手里未持有股票的利润。
      	// dp[i][j][1]:第i天,经过最多j次交易,手里持有股票的利润。
        vector>> dp(prices.size(), 
            vector>(k + 1, make_pair(0, 0)));
        for (int i = 1; i <= k; ++i) {
            dp[0][i].second = -prices[0];
        }
        for (int i = 1; i < prices.size(); ++i) {
            for (int j = 1; j <= k; ++j) {
                dp[i][j].first = max(dp[i - 1][j].first,
                    dp[i - 1][j].second + prices[i]);
                dp[i][j].second = max(dp[i - 1][j].second,
                    dp[i - 1][j - 1].first - prices[i]);
            }
        }
        return dp[prices.size() - 1][k].first;
    }
};

LRU

class Node {
public:
  int key, val;
  Node* next, prev;
  Node(int k, int v) {
    key = k;
    val = v;
  }
};

// 构建双向链表
class DoubleList {
public: 
  // 在链表头部添加节点x
  void addFirst(Node *x);
  
  // 删除链表中的x节点
  void remove(Node *x);
  
  // 删除链表中最后一个节点,并返回
  Node* removeLast();
  
  // 返回链表长度
  int size();
};

class LRUCache {
private: 
  Map my_map;  // 映射表
  DoubleList cache;  // 双向链表
  int cap;  // 最大容量
  
public:
  LRUCache(int capacity) {
    this.cap = capacity;
  }
  
  int get(int key) {
    if (my_map.count(key) == 0) {
      return -1; 
    }
    int val = my_may.get(key)->val;
    // 利用put方法把该数据提前
    put(key, val);
    return val;
  }
  
  void put(int key, int val) {
    // 先把新节点做出了
    Node* x = new Node(key, val);
    
    if (my_map.count(key)) {
      // 删除旧的节点,新的插到头部
      cache.remove(my_map.get(key));
      cache.addFirst(x);
      // 更新map中的数据
      map.put(key, x);
    } else {
      if (cap == cache.size()) {
        // 删链表最后一个数据
        Node* last = cache.removeLast();
        my_map.remove(last->key);
      }
      // 添加到头部
      cache.addFirst(x);
      map.put(key, x);
    }
  }
};

二叉数算法的框架

// 通用框架
void traverse(TreeNode* root) {
  // root需要做什么,在这里做
  // 其他不用root操心,抛给框架
  traverse(root->left);
  traverse(root->right);
}

// BST(二叉搜索树)的框架
void BST(TreeNode* root, int target) {
  if (root->val == target) {
    // 找到目标,做点什么
  }
  if (root->val < target) {
    BST(root->right, target);
  } else {
    BST(root->right, target);
  }
}

单调栈问题(下一个更大的数)

vector nextGreaterElement(vector &nums) {
  vector ans(nums.size()); // 存放答案的数组
  stack s;
  for (int i = nums.size() - 1; i >= 0; --i) {
    while (!s.empty() && s.top() <= nums[i]) {
      s.pop(); //小的数字弹出,
    }
      ans[i] = s.empty() ? -1 : s.top(); //这个元素后面第一个比他大的值
      s.push(nums[i]); // 进栈
  } 
   return ans;
}

单调队列

class MonotonicQueue {
private:
  deque data;
  
public:
  void push(int n) {
    while (!data.empty() && data.back() < n) {
      data.pop_back();
    }
    data.push_back(n);
  }
  
  int max() {
    return data.front();
  }
  
  void pop(int n) {
    if (!data.empty() && data.front() == n) {
      data.pop_front();
    }
  }
};

vector maxSlidingWindow(vector& nums, int k) {
  MonotonicQueue Window;
  vector res;
  fof (int i = 0; i < nums.size(); ++i) {
    if (i < k - 1) {
      window.push(nums[i]);
    } else {
      window.push(nums[i]);
      res.push_back(window.max());
      window.pop(nums[i - k + 1]);
    }
  }
  return res;
}

twoSum问题

// 利用哈希表优化时间负责度,用空间换取时间。
class TwoSum {
private:
  map freq;
public:
  void add(int number) {
    freq.insert(make_pair(number, freq[number]++));
  }
  
  bool find(int value) {
    for (auto key : freq) {
      int other = value - key.first;
      
      // 情况1
      if (other == key.first && key.second > 1) {
        return true;
      }
      
      // 情况2
      if (other != key && freq.count(other) > 0) {
      	return true;
      }
    }
    return false;
  }
};

常用的位操作

// 利用或操作和空格将英文字母转化为小写
('a' | ' ') = 'a';

// 利用与操作和下划线将英文字母转换为大写
('b' & '_') = 'B';

// 利用异或操作和空格进行英文字符大小写互换
('d' ^ ' ') = 'D';

// 判断两个数是否异号
int x = -1, y = 2;
bool f = ((x ^ y) < 0); // true

int x = 3, y = 2;
bool f = ((x ^ y) < 0); // false

// 交换两个数
int a = 1, b = 2;
a ^= b;
b ^= a;
a ^= b;
// 现在a = 2, b = 1;

// 加一
int n = 1;
n = -~n; //n = 2;

// 减一
int n = 2;
n = ~-n; // n = 1;

// 消除数字n的二进制表示中的最后一个1
n & (n - 1);

前缀和

int n = nums.length;
vector pre_sum(n + 1, 0);
pre_sum[0] = 0;
for (int i = 0; i < n; ++i) {
  pre_sum[i + 1] = pre_sum[i] + nums[i];
}

FloodFill算法(二维矩阵中的搜索问题)

// (x, y)为坐标位置
void fill(int x, int y) {
  fill(x - 1, y);  // 上
  fill(x + 1, y);  // 下
  fill(x, y - 1);  // 左
  fill(x, y + 1);  // 右
}

并查集

class UF {
private:
  int count;
  vector parent;
  vector size;
public:
  UF(int n) {
    this->count = n;
    for (int i = 0; i < n; ++i) {
      parent.push_back(i);
      size[i] = 1;
    }
  }
  
  void union(int p, int q) {
    int root_p = find(p);
    int root_q = find(q);
    if (root_p == root_q) {
      return;
    }
    
    if (size[root_p] > size[root_q]) {
      parent[root_p] = root_p;
      size[root_p] += size[root_q];
    } else {
      parent[root_p] = root_q;
      size[root_q] += size[root_p];
    }
    --count;
  }
  
  bool connected(int p, int q) {
    int root_p = find(p);
    int root_q = find(q);
    return  (root_p == root_q);
  }
  
  int find(int x) {
    while (parent[x] != x) {
      parent[x] = parent[parent[x]];
      x = parent[x];
    }
    return x;
  }
  
  int count() {
    return count;
  }
};

你可能感兴趣的:(百宝箱,数据结构,算法,c++)