排序
快排
void quick_sort(vector<int> &nums, int l, int r)
{
int low = l;
int high= r;
int val= nums[low];
while (low<high)
{
while (low < high&& nums[high] >= val) high--;
while (low < high&& nums[low] <= val) low++;
if(low<high) swap(nums[low],nums[high]);
}
swap(nums[l],nums[high]);
quick_sort(nums,l,low-1);
quick_sort(nums,low+1,r);
}
链接:https://blog.youkuaiyun.com/qq_28584889/article/details/88136498
堆排序
从小到大排序
void heapSort(vector<int> &v,int indexEnd){
//para1:vector;
//para2:index of the last element
for(int i=indexEnd;i>0;i--){
bigHeapCreate(v, i);
swap(v,0,i);
}
}
void bigHeapCreate(vector<int> &v,int j){
for(int i=(j-1)/2;i>=0;--i){
if(v[i]<v[2*i+1] || (2*i+2)<=j && v[i]<v[2*i+2]){
if((2*i+2)<=j && v[2*i+1]<v[2*i+2]){//右子节点最大
swap(v,i,2*i+2);
int k=2*i+2;
while(2*k+1<=j){
if((2*k+2<=j) && v[k]<v[2*k+2] || v[k]<v[2*k+1]){//破坏了
if((2*k+2<=j) && v[2*k+1]<v[2*k+2]){
swap(v,k,2*k+2);
k=2*k+2;
}
else{
swap(v,k,2*k+1);
k=2*k+1;
}
}
else{
break;
}
}
}
else{//左子节点最大
swap(v,i,2*i+1);
int k=2*i+1;
while(2*k+1<=j){
if((2*k+2<=j) && v[k]<v[2*k+2] || v[k]<v[2*k+1]){//破坏了
if((2*k+2<=j) && v[2*k+1]<v[2*k+2]){
swap(v,k,2*k+2);
k=2*k+2;
}
else{
swap(v,k,2*k+1);
k=2*k+1;
}
}
else{
break;
}
}
}
}
}
}
void swap(vector<int> &v,int i,int j){
int tmp=v[i];
v[i]=v[j];
v[j]=tmp;
}
寻找第K大(快排)
法一:快排
int quick_sort(vector<int>& v,int l,int r,int n,int k){
int low=l;
int high=r;
cout<<"low="<<low<<"high="<<high<<endl;
int value=v[low];
int res=0;
while(low<high){
while(low<high && v[high]>=value) --high;
while(low<high && v[low]<=value) ++low;
if(low<high) swap(v[low],v[high]);
}
swap(v[l],v[high]);
if(n-high==k) return v[n-k];
else if(n-high<k) res=quick_sort(v, l, high-1, n, k);
else if(n-high>k) res=quick_sort(v, high+1, r, n, k);
return res;
}
int findKth(vector<int> a, int n, int K) {
// write code here
return quick_sort(a, 0, n-1, n, K);
}
链表
反转链表
要求:空间复杂度O(1)
解法:双指针
- 一个指针指向待反转的部分,一个指针指向已反转的部分,遍历链表。
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
ListNode* cur=NULL;
ListNode* node=pHead;
while(node){
ListNode* tmp=node->next;
node->next=cur;
cur=node;
node=tmp;
}
return cur;
}
};
链表环的入口节点
题目:牛客NC3
解法:快慢指针
- 让快指针每次走2步,慢指针每次走1步;
- 若有环,则在环中某一点相遇。相遇后,让快指针重新从头结点出发,慢指针继续走,快慢指针每次都走1步,再次相遇处即为环的入口节点;
- 若无环,则快指针走着走着会指向空;
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead) {
if(!pHead || !pHead->next) return nullptr;
ListNode* fast=pHead;
ListNode* slow=pHead;
while(fast && fast->next){
fast=fast->next->next;
slow=slow->next;
if(fast==slow){
break;
}
}
if(fast){
fast=pHead;
while(fast!=slow){
fast=fast->next;
slow=slow->next;
}
}
return fast;
}
};
删除链表的倒数第n个节点
题目:牛客NC53
解法:快慢指针
- 快指针先走n步,然后快慢指针一起走,快指针走到空的时候慢指针正好在倒数第n个结点上;
- 要删除某个结点的话,还需要需要知道它的前一个结点是什么;
- 如果要删除的结点是链表头结点的话,这个时候没有前一个结点,需要特判一下;
快指针可能刚走一步就为空,需要特判一下;
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* fast=head;
ListNode* slow=head;
ListNode* pre=head;
for(int i=0;i<n;++i){
fast=fast->next;
}
if(!fast){
return head->next;
}
while(fast){
fast=fast->next;
pre=slow;
slow=slow->next;
}
pre->next=slow->next;
return head;
}
二叉树
创建二叉树
TreeNode* create_tree(vector<int> v) {
queue<TreeNode*> q;
TreeNode* root = new TreeNode{0,0,0};
TreeNode* node = new TreeNode{0,0,0};
node = root;
node->val = v[0];
q.push(node);
v.erase(v.begin());
while (!v.empty() || !q.empty()) {
node = q.front();
q.pop();
if (!node->left && !v.empty()) {
TreeNode* tmp = new TreeNode{ v[0],0,0 };
v.erase(v.begin());
node ->left= tmp;
q.push(tmp);
}
if (!node->right && !v.empty()) {
TreeNode* tmp = new TreeNode{ v[0],0,0 };
v.erase(v.begin());
node->right = tmp;
q.push(tmp);
}
}
return root;
}
前序遍历
递归实现
void preorder(TreeNode* root){
if(root==NULL){
return;
}
cout<<root->data<<" ";
preorder(root->left);
preorder(root->right);
return;
}
栈实现
void preorder(TreeNode* root){
stack<TreeNode*> s;
TreeNode* node=root;
while(node|| !s.empty()){
if(node){
cout<<node->val<<" ";
s.push(node);
node=node->left;
}
else{
node=s.top();
s.pop();
node=node->right;
}
}
cout<<endl;
return;
}
层序遍历
队列实现
vector<int> cxbl(TreeNode* root) {
queue<TreeNode*> q;
vector<int> v;
q.push(root);
while (!q.empty()) {
TreeNode* node = q.front();
v.push_back(node->val);
q.pop();
if (node->left) {
q.push(node->left);
}
if (node->right) {
q.push(node->right);
}
}
return v;
}
重建二叉树
题目:根据前序遍历和中序遍历重建二叉树,返回二叉树的根节点。(牛客 NC12)
递归法
方法原理:
- 前序遍历:根左右
- 中序遍历:左根右
- 后序遍历:左右根
先根据前序遍历找出根节点,中序遍历中根节点的左边是左子树,右边是右子树,对左右子树分别递归可以重建出二叉树。
class Solution {
public:
TreeNode* rebuild(vector<int>& pre,int pre_left,int pre_right,vector<int>& vin,int vin_left,int vin_right){
if(pre_left>pre_right) return nullptr ;
// locate root node
int index=vin_left;
while(index<vin_right && vin[index]!=pre[pre_left]) ++index;
TreeNode* root=new TreeNode(pre[pre_left]);
root->left=rebuild(pre,pre_left+1,pre_left+index-vin_left,vin,vin_left,index-1);
root->right=rebuild(pre,pre_left+index+1-vin_left,pre_right,vin,index+1,vin_right);
return root;
}
TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
TreeNode* root=rebuild(pre, 0, pre.size()-1, vin, 0, vin.size()-1);
return root;
}
};
二叉树根节点到叶子节点和为指定值的路径
题目:给定一个二叉树和一个值\ sum sum,请找出所有的根节点到叶子节点的节点值之和等于\ sum sum 的路径。
思路:使用dfs遍历,从根节点开始,用sum不断地减去遍历到的每一个节点,一直到叶子节点,在减去叶子节点之前查看sum是否等于叶子节点,如果等于说明我们找到了一组。
方法:dfs(递归版)
在这里插入代码片
图
省份数量
题目(LeetCode 547)
有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。
省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。
给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。
返回矩阵中 省份 的数量。
输入:isConnected = [[1,1,0],[1,1,0],[0,0,1]]
输出:2
方法一:dfs(递归版)
class Solution {
public:
int findCircleNum(vector<vector<int>>& isConnected) {
int n=isConnected.size();
int res=0;
vector<int> is_visited(n);
for(int i=0;i<n;++i){
if(is_visited[i]==0){
++is_visited[i];
++res;
dfs(i,n,is_visited,isConnected);
}
}
return res;
}
void dfs(int i,int n,vector<int>& is_visited,vector<vector<int>>& isConnected){
for(int j=0;j<isConnected[i].size();++j){
if(isConnected[i][j]==1&&is_visited[j]==0){//节点相连并且未被访问过
is_visited[j]=1;
dfs(j,n,is_visited,isConnected);
}
}
return ;
}
};
递归
统计每个月兔子的总数
题目(牛客HJ37)
有一只兔子,从出生后第3个月起每个月都生一只兔子,小兔子长到第三个月后每个月又生一只兔子,假如兔子都不死,问每个月的兔子总数为多少?
本题有多组数据。
输入描述:输入int型表示month
输出描述:输出兔子总数int型
#include<iostream>
using namespace std;
int born(int month){
if(month<=2){
return 1;
}
else{
return born(month-1)+born(month-2);
}
}
int main()
{
int month;
while(cin>>month){
int res=born(month);
cout<<res<<endl;
}
return 0;
}
走方格的方案数
- 题目(HJ91):
请计算n*m的棋盘格子(n为横向的格子数,m为竖向的格子数)沿着各自边缘线从左上角走到右下角,总共有多少种走法,要求不能走回头路,即:只能往右和往下走,不能往左和往上走。- 输入描述:
每组样例输入两个正整数n和m,用空格隔开。(1≤n,m≤8)- 输出描述:
每组样例输出一行结果
#include<iostream>
using namespace std;
int func(int n,int m){
if(n==0 || m==0){
return 1;
}
return func(n-1,m)+func(n,m-1);
}
int main()
{
int n,m;
while(cin>>n>>m){
int res=func(n,m);
cout<<res<<endl;
}
return 0;
}
暴力法
最大公共子串
题目:给定两个只包含小写字母的字符串,计算两个字符串的最大公共子串的长度。(牛客HJ75)
#include<iostream>
using namespace std;
#include<string>
int get_max_len_of_sub_str(string& s1,string& s2){
int max_len=0;
if(s1.empty() || s2.empty()) return max_len;
for(int i=0;i<s1.size();++i){
for(int j=0;j<s2.size();++j){
if(s1[i]==s2[j]){
int max_tmp=0;
int a=i,b=j;
for(;a<s1.size() && b<s2.size();++a,++b){
if(s1[a]!=s2[b]) break;
++max_tmp;
}
max_len=max_tmp>max_len?max_tmp:max_len;
}
}
}
return max_len;
}
int main()
{
string s1,s2;
while(getline(cin,s1) && getline(cin,s2)){
int max_len=get_max_len_of_sub_str(s1, s2);
cout<<max_len<<endl;
}
return 0;
}
贪心算法
原理:每一步在一定标准下做出当前的最优决策,做出该决策之后不可更改,得到局部最优的结果。
股票(无限次交易)
- 题目(牛客NC134 ):
假定你知道某只股票每一天价格的变动。
你最多可以同时持有一只股票。但你可以无限次的交易(买进和卖出均无手续费)。
请设计一个函数,计算你所能获得的最大收益。- 示例:
输入:[1,2,3,4,5]
返回值:4
说明:第一天买进,最后一天卖出最优。中间的当天买进当天卖出不影响最终结果。最大收益为4。
解题思路:
只要当前上涨就买入,涨到最高点就卖出,即可找到所有上涨的收益之和。
在数组中用后一个元素减去前一个元素,得到的差值大于0则上涨,求取所有大于0的差值即可。
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 计算最大收益
* @param prices int整型vector 股票每一天的价格
* @return int整型
*/
int maxProfit(vector<int>& prices) {
// write code here
vector<int> v;
for(int i=1;i<prices.size();++i){
v.push_back(prices[i]-prices[i-1]);
}
int res=0;
for(auto& ele:v){
if(ele>0){
res+=ele;
}
}
return res;
}
};
动态规划
动态规划通过dp数组记录“重叠子问题”,避免重复计算,实现优化。
主要是要写出状态转移方程,需要确定“状态”和“选择”,“选择”是使“状态”发生变化的行为。
子数组的最大累加和问题
题目:给定一个数组arr,返回子数组的最大累加和。(牛客 NC19)
解法:动态规划
class Solution {
public:
/**
* max sum of the subarray
* @param arr int整型vector the array
* @return int整型
*/
int sum(vector<int>& v,int left,int right){
int sum=0;
for(int i=left;i<=right;++i){
sum+=v[i];
}
return sum;
}
int maxsumofSubarray(vector<int>& arr) {
// write code here
if (arr.size()==0) return 0;
vector<int> dp(arr.size(),INT_MIN);
dp[0]=arr[0];
int max_sum_index_start=0;
for(int i=1;i<arr.size();++i){
int max_tmp;
int sum_tmp=sum(arr,max_sum_index_start,i);
if(dp[i-1]>arr[i] && dp[i-1]>sum_tmp){
dp[i]=dp[i-1];
}
else{
if(arr[i]>=sum_tmp){
dp[i]=arr[i];
max_sum_index_start=i;
}
else{
dp[i]=sum_tmp;
}
}
}
return dp[arr.size()-1];
}
};
矩阵的最小路径和
题目:给定一个 n * m 的矩阵 a,从左上角开始每次只能向右或者向下走,最后到达右下角的位置,路径上所有的数字累加起来就是路径和,输出所有的路径中最小的路径和。(牛客 NC59)
方法:动态规划
dp矩阵第一行和第一列需要单独初始化。
状态方程:dp[i][j]=min(dp[i-1][j],dp[i,j-1])+matrix[i][j]
class Solution {
public:
/**
*
* @param matrix int整型vector<vector<>> the matrix
* @return int整型
*/
int minPathSum(vector<vector<int> >& matrix) {
// write code here
int n=matrix.size();
int m=matrix[0].size();
vector<int> v_tmp(m,0);
vector<vector<int>> dp(n,v_tmp);
for(int i=0;i<n;++i){
for(int j=0;j<m;++j){
if(i==0){
if(j==0) dp[i][j]=matrix[i][j];
else dp[i][j]=dp[i][j-1]+matrix[i][j];
}
else if(j==0){
if(i==0) continue;
else dp[i][j]=dp[i-1][j]+matrix[i][j];
}
else{
dp[i][j]=min(dp[i-1][j],dp[i][j-1])+matrix[i][j];
}
}
}
return dp[n-1][m-1];
}
};
01背包
题目(牛客NC145):
- 已知一个背包最多能容纳体积之和为v的物品,现有 n 个物品,第 i 个物品的体积为 vi , 重量为 wi
求当前背包最多能装多大重量的物品?- 示例:
输入:10,2,[[1,3],[10,4]]
返回值:4- 说明:
第一个物品的体积为1,重量为3,第二个物品的体积为10,重量为4。只取第二个物品可以达到最优方案,取物重量为4
思路:
每次需要拿起一个物体,查看当前背包剩余体积大小,然后选择是否将该物体放入背包,判断条件是拿或者不拿能否使得背包总重量最大。
问题分解:
当前的“状态”为物体编号和背包剩余的体积,“选择”为面对第i个物体时是否选择该物体,选择之后会引起“状态”的变化,优化目标是背包总重量,作为dp数组的值;
将两个“状态”作为循环的控制变量,遍历所有的“状态”,通过“选择”推动“状态”的变化;
声明一个大小为 dp[n][V] 的二维数组, dp[i][j] 表示 在面对第 i 件物品,且背包容量为 j 时所能得到的最大重量,为方便陈述,记第i个物体的体积为v[i],重量为w[i]:
- j < v[i] 时,背包容量不足以放下第 i 件物品,只能选择不拿 dp[ i ][ j ] = dp[ i-1 ][ j ]
- j>=v[i] 时,背包容量可以放下第 i 件物品,要比较拿和不拿情况下的最大重量,取大的一方。
- 拿:dp[ i ][ j ]=dp[ i-1 ][ j-v[ i ] ] + w[ i ]。 这里的dp[ i-1 ][ j-w[ i ] ]指的就是考虑了i-1件物品,背包容量为j-w[i]时的最大价值,也是相当于为第i件物品腾出了w[i]的空间。
- 不拿:dp[ i ][ j ] = dp[ i-1 ][ j ] ;
返回遍历到第n个物体,背包容量为V情况下dp数组的值。
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 计算01背包问题的结果
* @param V int整型 背包的体积
* @param n int整型 物品的个数
* @param vw int整型vector<vector<>> 第一维度为n,第二维度为2的二维数组,vw[i][0],vw[i][1]分别描述i+1个物品的vi,wi
* @return int整型
*/
int knapsack(int V, int n, vector<vector<int> >& vw) {
// write code here
if(V==0 || n==0 || vw.empty()) return 0;
int max_weight=0;
vector<int> v_tmp(V+1,0);
vector<vector<int>> dp(n+1,v_tmp);
for(int i=1;i<=n;++i){
for(int j=1;j<=V;++j){
if(vw[i-1][0]>j){
dp[i][j]=dp[i-1][j];
}
else{
dp[i][j]=max(dp[i-1][j],dp[i-1][j-vw[i-1][0]]+vw[i-1][1]);
}
}
}
return dp[n][V];
}
};
回溯
括号生成
题目:给出n对括号,请编写一个函数来生成所有的由n对括号组成的合法组合。(牛客 NC26)
方法:回溯法
- 穷举,每次将选择加入track,然后进入树的下一层,从下一层返回的时候再从track中删除刚才的选择。
- 为了保证括号成对,在选择时需要施加约束条件进行剪枝,当已选的右括号的个数等于左括号的时候,不能继续选择右括号。
class Solution {
vector<string> res;
set<string> myset;//用于保证得到的括号组合不重复
public:
/**
*
* @param n int整型
* @return string字符串vector
*/
void backtrack(string& s,int n, string& track,int count_left,int count_right){//string s,s中元素个数n,路径track,track中左括号个数,track中右括号个数
if(count_left==n && count_right==n){
if(!myset.count(track)){
res.push_back(track);
myset.insert(track);
}
return;
}
for(auto& ele : s){
if(count(track.begin(),track.end(),ele)==n){//判断当前括号是否还有剩余
continue;
}
if(ele==')' && count_left==count_right){//当已选的右括号个数和左括号个数相等时,不能继续选右括号
continue;
}
track.push_back(ele);
if(ele=='(') ++count_left;
else ++count_right;
backtrack(s,n,track,count_left,count_right);
track.pop_back();
if(ele=='(') --count_left;
else --count_right;
}
}
vector<string> generateParenthesis(int n) {
// write code here
string s;
for(int i=0;i<n;++i){
s+="()";
}
string track;
backtrack(s,n,track,0,0);
return res;
}
};
杂
查找输入整数二进制中1的个数
输入一个正整数,计算它在二进制下的1的个数。(牛客HJ62)
#include<iostream>
using namespace std;
int count_1(int num){
int sum=0;
while(num){
if(num&1){
++sum;
}
num>>=1;
}
return sum;
}
int main()
{
int num;
while(cin>>num){
int res=count_1(num);
cout<<res<<endl;
}
return 0;
}
牛客代码本地调试辅助代码
链表
#include<iostream>
using namespace std;
#include<vector>
struct ListNode {
int val;
struct ListNode *next;
ListNode(int val) :val(val), next(nullptr) {};
};
ListNode* create_listNode(vector<int>& v) {//根据vector创建链表
ListNode* head = new ListNode(0);
ListNode* node = head;
for (auto& ele : v) {
ListNode* tmp = new ListNode(ele);
node->next = tmp;
node = node->next;
}
return head->next;
}
void print_listNode(ListNode* head) {//打印链表
ListNode* node = head;
while (node) {
cout << node->val << " ";
node = node->next;
}
cout << endl;
}
class Solution {
public:
/**
*
* @param head ListNode类
* @param k int整型
* @return ListNode类
*/
ListNode* func(ListNode* head) {
// write code here
}
};
int main()
{
vector<int> v{ 1,2,3,4,5 };//change ListNode here
ListNode* head = create_listNode(v);
//print_listNode(head);
Solution solution;
ListNode* node = solution.func(head);//change function name here
//print_listNode(node);
return 0;
}
二叉树
#include<iostream>
#include<vector>
#include<queue>
#include<deque>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) :val(x), left(NULL), right(NULL) {}
};
TreeNode* ConstructBinaryTree(vector<int>& vec, int len, int i) {
if (vec.empty() || len < 1) return nullptr;
TreeNode *root = NULL;
if (i < len && vec[i] != 'null') {
root = new TreeNode(0);
if (root == nullptr) return nullptr;
root->val = vec[i];
root->left = ConstructBinaryTree(vec, len, 2 * i + 1);
root->right = ConstructBinaryTree(vec, len, 2 * i + 2);
}
return root;
}
void print_vector(vector<vector<int>>& v) {
for (int i = 0; i < v.size(); i++) {
for (int j = 0; j < v[i].size(); j++) {
cout << v[i][j] << " ";
if (j == v[i].size() - 1)
cout << endl;
}
}
}
void print_vector(vector<int>& v) {
for (auto& ele : v) {
cout << ele << " ";
}
cout << endl;
}
class Solution {
public:
vector<vector<int> > func(TreeNode* pRoot) {
// write code here
}
};
//test
int main() {
TreeNode *root;
vector<int> data{ 1,2,3,'null','null',4,5 };//change data here ( # need to changed to 'null')
root = ConstructBinaryTree(data, data.size(), 0);
Solution s;
vector<vector<int>>result = s.func(root);//change function name here
//print result
print_vector(result);
return 0;
}
其他
变量类型选择
- int
4byte,一般在10^9以内的整数都可以定义成int型- float
只有6~7位有效精度- double
有效精度有15~16位(碰到浮点型的数据都应该用double)
printf
#include<stdio.h>
- 输出格式
%md:不足m位时,高位用空格补齐
%0md:不足m位时,高位用0补齐
%.mf:让浮点数保留m位输出
最小整数和最大整数
#include <limits.h>
int n1= INT_MIN;
int n2= INT_MAX;
输出小数点位数
#include<iostream>
#include<iomanip>
using namespace std;
int main()
{
float a=3.1;
cout << fixed << setprecision(2);//控制小数点后的有效位数
cout<<a<<endl;
}
读取一行位置数量的int放入vector
#include<iostream>
using namespace std;
#include<vector>
#include<string>
#include<sstream>
int main()
{
vector<int> v;
string s,s_tmp;
getline(cin, s);
stringstream ss(s);
while (ss >> s_tmp) {
int int_tmp = atoi(s_tmp.c_str());
v.push_back(int_tmp);
}
return 0;
}
0(longlong)
long long a=0LL;
[1]:图解快速排序(C++实现)
[2]:堆排序 C++非递归 通俗易懂
[3]:leetcode中,代码如何调试,创造本地运行环境
[4]:Leetcode 本地调试环境(C++)