力扣刷题小记+24秋招好题记录

数组

二分法

重点: 区间 [left,right]
   右限值 right=middle-1
   左限值 left=middle+1

标准二分法

while(left<=right){					//注意小于等于
	int mid=(right-left)/2+left;
	if(nums[mid]==tagert)			//等于情况
	return tagert;
	else if(nums[mid]>tagert)
	right=mid-1;					//注意减一
	else if(nums[mid]<tagert)
	left=mid+1;						//注意加一
}

旋转数组最小值二分法

存在重复元素

while(left<right){						//由于不处理等于情况所以不能相等
	int mid=(right-left)/2+left;
	if(nums[mid]>nums[right])			
	left=mid+1;							//除的时候向下取余所以左边要缩,+1
	else if(nums[mid]<nums[right])
	right=mid;							//无法排出这个mid就是最小值,所以不能缩
	else if(nums[mid]==nums[right])
	right--;							//现在两个值相等,只能缩一个看看
}

原地移除

重点: 双重for循环,
nums[i] = =tagert ,则 j = i+1,寻找 nums[j] !=tagert, 并交换 nums[i] 与 nums[j]。
   快慢指针 一个for循环遍历快指针,如果nums[fast] !=tagert,快慢指针进行交换 ,同时慢指针+1;

https://leetcode.cn/problems/remove-element/

最短长度的连续数组

重点: 双指针,–for加while 滑动窗口
right每个回合右移一次,总和大于等于target,则while循环 res-=nums[left++] while循环内部进行最短长度的比较保留。

https://leetcode.cn/problems/minimum-size-subarray-sum/

链表

链表二分法

if(head==nullptr)
return head;
listnode*slow=head,*fast=head;
while(fast->next!=nullptr&&fast->next->next!=nullptr)
{
	slow=slow->next;
	fast=fast->next->next;
}
slow为中心位置

https://leetcode.cn/problems/binary-search/

单链表排序

链表适合归并排序,先使用上面的链表二分然后划分分治,再由分治一路合并回去
在这里插入图片描述

 ListNode* gubing(ListNode*root){
        if(root==nullptr||root->next==nullptr)
        return root;
        
		//二分找到中点
        ListNode*slow=root;
        ListNode*fast=root;
        while(fast->next!=nullptr&&fast->next->next!=nullptr){
            slow=slow->next;
            fast=fast->next->next;
             }
             
		
        ListNode*temp=slow->next;
        slow->next=nullptr;	//切开大链表
        
        ListNode* left=gubing(root);	//左半部分
        ListNode* right=gubing(temp);	//右半部分
        
        ListNode*node=new ListNode(0);	//留个头结点好返回 准备合并上面两部分的链表
        root=node;
        while(left!=nullptr&&right!=nullptr){
            if(left->val<right->val){	//比较大小放进去
                node->next=left;
                left=left->next;
            }else{
                node->next=right;
                right=right->next;
            }
            node=node->next;
        }
		//有那个要是短一截,就直接接上,然后返回头结点
        if(left!=nullptr)
        node->next=left;
        if(right!=nullptr)
        node->next=right;
        return root->next;
     }
    ListNode* sortInList(ListNode* head) {
        if(head==nullptr||head->next==nullptr)
        return head;
        return gubing(head);
         
    }

topK问题

使用根堆最佳,前k个不用排序,后面N-K个也不用排序,只要找到K就行,维护一个小根堆,它就是前k个最大数里面最小的元素。每次跟根比较,要是替换了,就调整根堆

void gendui(vector<int>&a,int father,int boundary){
        int son=father*2+1;			//找到它的儿子
        while(son<boundary){
            if(son+1<boundary&&a[son+1]<a[son]){	//看看兄弟俩哪个更小,取哪个
                son++;
            }
            if(a[father]>a[son]){				//爹要是比儿子大,就交换,然后一路查下去
                int temp=a[father];
                a[father]=a[son];
                a[son]=temp;
                father=son;
                son=father*2+1;
            }else{
                break;
            }
        }
     }
    int findKth(vector<int>& a, int n, int K) {
       vector<int>nums(a.begin(),a.begin()+K);
       int count=K/2-1;			//找第一个非叶子节点(父节点),就是n/2-1
       for(int i=count;i>=0;i--){
            gendui(nums,i,K);
       }

		//将后面的数每个都跟小根堆进行比较,根堆点为0
       for(int i=K;i<n;i++){
        if(nums[0]<a[i]){
            nums[0]=a[i];
            gendui(nums,0,K);
        }
       }
       return nums[0];
    }

移除链表元素

重点: 1.判断头指针是否要移除,2.pre->next!=null && pre!=null

https://leetcode.cn/problems/remove-linked-list-elements/

互换链表2个元素

重点: 头结点很重要,链表一定要用

https://leetcode.cn/problems/swap-nodes-in-pairs/submissions/

圈圈圆圆圈圈

计算 a + c + b = b + c + a
若无交集,则a + b = b + a

      while(node1!=node2){
            node1=node1==NULL?headB:node1->next;
            node2=node2==NULL?headA:node2->next;
        }

尾巴与另一个的头相连,while(不相等就走)

没相交的话最后都走到NULL也相等退出循环了!!
在这里插入图片描述
https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/comments/

在这里插入图片描述

在这里插入图片描述
https://leetcode.cn/problems/linked-list-cycle-ii/

二叉树-嚓嚓嚓

dfs-前-中-后-回溯

//前序,中左右
void preorderTraversal(TreeNode* root) {
       if(root==nullptr)
       	return;
       	cout<<root->val;
       	preorderTraversal(root->left);
       	preorderTraversal(root->right);

    }
//中序,左中右
	void plan(TreeNode* root) {
       if(root==nullptr)
       	return;      
       	plan(root->left);
       	cout<<root->val;
       	plan(root->right);

    }
//后序,左右中
  	 void plan(TreeNode* root) {
       if(root==nullptr)
       	return;      
       	plan(root->left);
       	plan(root->right);
        cout<<root->val;
    }

dfs-前-中-后-迭代

//前序,中左右
void dfs(treeNode* root){
	stack<treeNode*> s;
	while(root!=nullptr||!s.empty()){
		if(root==nullptr){
			root=s.top()->right;
			s.pop();
		}
		cout<<root->val;
		s.push(root);
		root=root->left;
	}
}
//中序,左中右
void dfs(treeNode* root){
	stack<treeNode*> s;
	while(root!=nullptr||!s.empty()){
		if(root==nullptr){
			cout<<s.top()->val;
			root=s.top()->right;
			s.pop();
		}		
		s.push(root);
		root=root->left;
	}
}
//后序,左右中
//先弄成中右左,再翻转一下
void dfs(treeNode* root){
	stack<treeNode*> m_s;
	vector<int> ans;
	while(root!=nullptr||!m_s.empty()){
		if(root==nullptr){
			root=m_s.top()->left;
			m_s.pop();
		}
		ans.push_back(root->val);
		m_s.push(root);
		root=root->right;
	}
	reverse(ans.begin(),ans.end());
}

层次法

void bfs(treeNode* root){
	queue<treeNode*> m_q;
	if(root!=nullptr)
		m_q.push_back(root);
	while(!m_q.empty()){
		int numscount=m_q.size();
		for(int i=0;i<numscount;i++){
			treeNode* node=m_q.front();
			m_q.pop_front();
			cout<<node->val;
			if(node->left!=nullptr)
				m_q.push_back(node->left);
			if(node->right!=nullptr)
				m_q.push_back(node->right);
		}
	}
}

判断是否为搜索二叉树

重点 搜索二叉树的中序遍历为递增的

https://leetcode.cn/problems/validate-binary-search-tree/

回溯大法

模板

问题需要返回的类型0;
中间暂存量temp
void backtrack(原参数&1int path){
	if(path==1.size()){
		0.push_back(temp);
		return;
	}
	for(int i=path;i<1.size();i++){
		temp.push_back(1[i]);
		backtrack(1,i+1);	//可重复为backtrack(1,i);
		temp.pop_back();	//回退
	}
}

如果问题当中有条件,可新建一个新函数放置判断是否符合条件
模板

问题需要返回的类型0;
中间暂存量temp
void backtrack(原参数&1int path){
	if(path==1.size()){
		0.push_back(temp);
		return;
	}
	for(int i=path;i<1.size();i++){
		if(isval(1,path,i){
			temp.push_back(1[i]);
			backtrack(1,i+1);	//可重复为backtrack(1,i);
			temp.pop_back();	//回退
		}
	}
}
bool isval(原参数&1int left,int right){
//判断流程
}

来个栗子,如分割回文字

class Solution {
public:
	vector<vector<string>> ans;
	vector<string> stemp;
    vector<vector<string>> partition(string s) {
        backtrack(s,0);
        return ans;
    }
    void backtrack(string &s,int path){

        if(path>=s.size())
        {
            ans.push_back(stemp);
            return;
        }
        for(int i=path;i<s.size();i++){
            if(ispars(s,path,i)){
              stemp.push_back(s.substr(path, i - path + 1)) ;
            backtrack(s,i+1);
            stemp.pop_back();  
            }
            
        }
    }
    bool ispars(string&s,int left,int right){
        while(left<right){
            if(s[left]!=s[right])
            return false;
            ++left;
            --right;
        }
        return true;
    }

};

小tip,used数组

使用vectorused 来记录使用过的元素,达到去重的目的,举个例子:
全排列

public:
vector<vector<int>> ans;
vector<int> temp;
    vector<vector<int>> permute(vector<int>& nums) {
        vector<bool>used(nums.size(),false);
        backtrack(nums,used);   
        return ans;
    }
    void backtrack(vector<int>&nums,vector<bool>&used){
        if(temp.size()==nums.size()){
            ans.push_back(temp);
            return;
        }
        for(int i=0;i<nums.size();i++){
            if(used[i])
            continue;
            temp.push_back(nums[i]);
            used[i]=true;
            backtrack(nums,used);
            temp.pop_back();
            used[i]=false;
        }
    }
};

贪心

重点:1.当有多个约束条件时,先考虑一边再考虑另一边,小心顾此失彼
例如:分发糖果 https://leetcode.cn/problems/candy/

		2.找重合区间,先排序,再遍历,保留最大边界,合并。

动态规划

重点

1.01背包问题,好难的,背住吧

理论思想,dp[j]中放的为背包大小为 j 时的种类/价值,dp[j-nums[i]] 为找到能放下当前物品的最大背包,加上当前物品与不加进行比较,那个大要哪个,求种类的时候则都加上+=。
套路模板

//条件:一个负重n的背包,装质量为weight[],价值为val[]的石头,他们存放在nums[]里
	vector<int> dp(n+1,0);
	for(int i=0;i<nums.size();i++){
		for(int j=n;j>weight[i];--j){
			dp[i]=max(dp[i],dp[i-weight[i]]+val[i]);
		}
	}
//有时候weight[]、val[]和nums[]是一体的。

举个栗子,如分割子集 https://leetcode.cn/problems/partition-equal-subset-sum/
目的,找到总量一半的字符

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum=0;
        for(int x:nums){
            sum+=x;
        }
        if(sum%2!=0) return false;
        vector<int> dp(sum/2+1,0);
        for(int i=0;i<nums.size();i++){
            for(int j=dp.size()-1;j>=nums[i];j--){
                dp[j]=max(dp[j],dp[j-nums[i]]+nums[i]);
            }
        }
        return dp.back()==sum/2;

    }
};

理论思想,dp[j]中放的为背包大小为 j 时的种类/价值,dp[j-nums[i]] 为找到能放下当前物品的最大背包,加上当前物品与不加进行比较,那个大要哪个,求种类的时候则都加上+=。
其中找一组符合要求的用dp[j]=max(dp[j],dp[j-nums[i]]+nums[i]);
找所有符合要求的则要用dp[j]+=dp[j-nums[i]];

完全背包

背包中的东西可以重复选择,因为可以重复选所以遍历背包的时候不需要从后往前遍历,直接从前往后遍历就行,其中分为组合和排列两种问题
1.组合情况
https://leetcode.cn/problems/coin-change-ii/

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        vector<int>dp(amount+1,0);
        dp[0]=1;
        for(int i=0;i<coins.size();i++){
            for(int j=coins[i];j<dp.size();j++){//只需改动遍历背包的顺序
                dp[j]+=dp[j-coins[i]];
            }
        }
        return dp[amount];

    }
};

2.排列,排列的时候[1,5]与[5,1]被认为是两种方法,所以先遍历背包再遍历物品
https://leetcode.cn/problems/combination-sum-iv/

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<int>dp(target+1,0);
        dp[0]=1;
      
            for(int j=0;j<dp.size();j++){	//先遍历背包!!
            for(int i=0;i<nums.size();i++){
                if(j>=nums[i]&&dp[j]<INT_MAX-dp[j-nums[i]])//判断移出来
              dp[j]+=dp[j-nums[i]];  
            }          
        }
    return dp[target];
    }
};

买卖股票

重点:状态描述

状态第一天第二天
买了省多少钱 (买入态)-prices[0]。。
卖了省多少钱 (卖出态)0。。
啥也不干省多少钱 (就绪态)0。。
(冷冻期省多少钱)(挂起态)0。。

维护k个二维数组,buyk、sellk,分别代表k天的没买、买了、没卖、卖了,四种状态,

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
    ++k;
    vector<int>buy(k, -prices[0]);
    vector<int>sell(k, 0);
    for (int i = 1 ; i < prices.size(); i++) {
        for (int j = 1; j < k; j++) {
            buy[j] = max(buy[j], sell[j-1] - prices[i]);
            sell[j] = max(sell[j], buy[j] + prices[i]);
        }
    }
   return sell.back() ;
    }
};

k为二的话退化一下看着更清楚
(只能买卖两次)

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int buy1 = -prices[0], sell1 = 0;
        int buy2 = -prices[0], sell2 = 0;
        for (int i = 1; i < prices.size(); i++) {
        buy1 = max(buy1, -prices[i]);
        sell1 = max(sell1, buy1 + prices[i]);
        buy2 = max(buy2, sell1 - prices[i]);
        sell2 = max(sell2, buy2 + prices[i]);
    }
    return sell2;
    }
};

连续的最长子串型

模板
连续的时候:
dp[i][j] :以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最长重复子数组长度为dp[i][j]。
当A[i - 1] 和B[j - 1]相等的时候,dp[i][j] = dp[i - 1][j - 1] + 1;
记得要保存下最大值

不连续的时候再加一步:
不相等时, dp[i][j]=max( dp[i-1][j] , dp[i][j-1]);

注意:定义vector以及循环的时候为len+1

连续的例如:给两个数组求最大公共子串
https://leetcode.cn/problems/maximum-length-of-repeated-subarray/

    int findLength(vector<int>& nums1, vector<int>& nums2) {
        int len1=nums1.size(),len2=nums2.size();
        int ans=0;
        vector<vector<int>>dp(len1+1,vector<int>(len2+1,0));
        for(int i=1;i<=len1;i++){
            for(int j=1;j<=len2;j++){
                if(nums1[i-1]==nums2[j-1])
                dp[i][j]=dp[i-1][j-1]+1;
                ans=ans>dp[i][j]?ans:dp[i][j];
            }
        }
        return ans;
    }

不连续的例如:最长公共子序列

https://leetcode.cn/problems/longest-common-subsequence/

    int longestCommonSubsequence(string text1, string text2) {
        int len1=text1.size(),len2=text2.size();
        vector<vector<int>>dp(len1+1,vector<int>(len2+1,0));
        for(int i=1;i<=len1;i++){
            for(int j=1;j<=len2;j++){
                if(text1[i-1]==text2[j-1])
                dp[i][j]=dp[i-1][j-1]+1;
                else
                dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
            }
        }
        return dp[len1][len2];
    }

最长相同子序列

给定两个字符串str1和str2,输出两个字符串的最长公共子序列。如果最长公共子序列为空,则返回"-1"。目前给出的数据,仅仅会存在一个最长的公共子序列
示例1
输入:
“1A2C3D4B56”,“B1D23A456A”
返回值:
“123456”

只用动态规划能算出最长的子序列长度,但还需要返回路径,则需要额外一个二维数组记录路径,最后拼接字符返回,定义二维数组时长度比字符多一位可以免去考虑字符越界的情况(如i-1>0),可以省去给边界赋值。
代码

using d_arry = vector<vector<int>>;
using o_arry = vector<int>;
class Solution {
  public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * longest common subsequence
     * @param s1 string字符串 the string
     * @param s2 string字符串 the string
     * @return string字符串
     */
    string LCS(string s1, string s2) {
        int len1 = s1.size(), len2 = s2.size();
        if(len1==0||len2==0)
        return "-1";
        d_arry dp(len1+1, o_arry(len2+1, 0));
        d_arry path(len1+1, o_arry(len2+1, 0));

        for(int i=1;i<=len1;i++){
            for(int j=1;j<=len2;j++){
                if(s1[i-1]==s2[j-1])
                {
                    dp[i][j]=dp[i-1][j-1]+1;
                    path[i][j]=3;
                }else{
                    if(dp[i-1][j]>dp[i][j-1]){
                        dp[i][j]=dp[i-1][j];
                        path[i][j]=1;
                    }else{
                        dp[i][j]=dp[i][j-1];
                        path[i][j]=2;
                    }
                }
            }
        }

        string ans;
        int i=len1,j=len2;
        if(dp[i][j]==0)
        return "-1";
        while(i>=0&&j>=0){
            switch (path[i][j]) {
                case 3:
                    ans+=s1[i-1];
                    i--;
                    j--;
                    break;
                case 2:
                     j--;
                    break;
                case 1:
                    i--;
                    break;
                default:
                    i--;
                    j--;
            }
        }
    reverse(ans.begin(),ans.end());
    return ans;
    }
};

回文字串

一个方形矩阵,dp[ i ][ j ] 代表 i 到 j 之间是否为回文串,
== 所以可由 dp[i+1][j-1] 来推断 dp [ i ][ j ];所以 i 从右往左遍历,j 从左往右遍历 ==

class Solution {
public:
    int countSubstrings(string s) {
        int res=0;
        vector<vector<bool>>dp(s.size(),vector<bool>(s.size(),false));
        for(int i=s.size()-1;i>=0;i--){
            for(int j=i;j<s.size();j++){
                if(s[i]==s[j]){
                    if(j-i<=1||dp[i+1][j-1]){
                        res++;
                        dp[i][j]=true;
                    }
                }
            }
        }
    return res;
    }
};

如果要算最长回文子序列

将矩阵bool改为int,计算i 到 j 之间最长的字串

class Solution {
public:
    int longestPalindromeSubseq(string s) {
        int len=s.size();
        vector<vector<int>> dp(len,vector<int>(len,1));
        for(int i=len-1;i>=0;i--){
            for(int j=i+1;j<len;j++){
                if(s[i]==s[j]){
                    if(j-i==1){
                    dp[i][j]=2;
                    }else{
                        dp[i][j]=dp[i+1][j-1]+2;
                    }
                }else
                dp[i][j] = max(dp[i][j - 1],dp[i+1][j]);
                
            }
        }
        return dp[0][len-1];

    }
};

回文子串(连续的)

 int getLongestPalindrome(string A) {
        vector<vector<int>>dp(A.size(),vector<int>(A.size(),0));
        int ans=0;
        for(int i=A.size()-1;i>=0;i--){
            for(int j=i;j<A.size();j++){
                if(A[i]==A[j]){
                    if (j==i)			//它自身
                    dp[i][j]=1;
                    else if(j-i==1)		//左右俩
                    dp[i][j]=2;
                    else if(dp[i+1][j-1]!=0)//注意这个判断条件,中间不是回文两头相同也没用
                    dp[i][j]=dp[i+1][j-1]+2;
                    else
                     dp[i][j]=0;
                    ans=max(ans,dp[i][j]);
                }
                else {
                dp[i][j]=0;
                }         
            }
        }
        return ans;
    }

编辑距离

两个字符串进行匹配,可以通过这种方法计算相似度
https://leetcode.cn/problems/edit-distance/
初始化 i=0 时无法匹配 j 必须操作他字符长度的量 dp[0][j]=j;
i 同理!

if (word1[i - 1] == word2[j - 1])if (word1[i - 1] != word2[j - 1])
不操作
    int minDistance(string word1, string word2) {
        int len1=word1.size(),len2=word2.size();
        vector<vector<int>> dp(len1+1,vector<int>(len2+1,0));
        for(int i=0;i<=len1;i++) dp[i][0]=i;
        for(int j=0;j<=len2;j++) dp[0][j]=j;
        for(int i=1;i<=len1;i++){
            for(int j=1;j<=len2;j++){
                if(word2[j-1]==word1[i-1])
            dp[i][j]=dp[i-1][j-1];
                else{
            dp[i][j]=min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1]))+1;
            			//替换 				删i			删j			添加和删除同操作
                }
            }
        }
        return dp[len1][len2];
    }

秋招笔试遇到的值得看到题

米哈游

世界树题
第一个节点深度为1
根据深度*节点值求和取最小,可以改动一个边

输入
第一行输入所有节点个数n
第二行输入n个整数表明节点值大小
剩下的n-1行输入边

4
1,23,10
1,2
2,3
3,4
示例
1*1+2*2+3*3+4*10;把4节点接到1处
得1*1+10+2)*2+3*3最小

代码

#include<iostream>
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<vector>
#include<set>
using namespace std;

using ll = long long;
using pii = pair<int, int>;

int n = 4;
vector<int> deep(n + 1);
vector<ll> score(n + 1), sum(n + 1);
void dfs(vector<int>&a, vector<vector<int>> &e,int u,  int depth) {
    deep[u] = depth;
    score[u] = 1LL * depth * a[u];
    sum[u] = a[u];
    for (auto&& v : e[u]) {
        dfs(a,e,v, depth + 1);
        score[u] += score[v];
        sum[u] += sum[v];
    }
}
void dfs2(vector<vector<int>>& e,int u,  ll &ans) {
    if (deep[u] > 2 && score[1] - sum[u] * (deep[u] - 2) < ans) {
        ans = score[1] - sum[u] * (deep[u] - 2);
    }
    for (auto&& v : e[u]) {
        dfs2(e,v,ans);
    }
}
int main() {

    vector<int> a={0,1,2,3,10};
    vector<vector<int>> e = { {},{2},{3},{4},{} };

    dfs(a,e,1,  1);
    ll ans = score[1];
    dfs2(e,1, ans);
    cout << ans << '\n';
    return 0;
}

京东

第二题,
输入一个数组{1,2,3,4};
可以对其将后两位进行加或乘操作,然后所得数取个位值再加入到数组中,求数组只剩最后一个数时,是几的数量。
用层次遍历的思想,总遍历nums.size()-1次,然后层里从后往前遍历,看看加或乘之后的数与之前已经进行过操作的数比较,要是一样本次不操作,让之前那个的nums值加本次的值。这样下去每层的大小最多为10。
在这里插入图片描述
代码,考虑只有一个数且为10,输出0-1

#include<iostream>
#include<string>
#include<vector>
#include<set>
#include<queue>
using namespace std;

struct arr {
    vector<int>a;
    int nums = 0;
};
int main() {
    //int n = 0;
    //cin >> n;
    //vector<int>nums(n, 0);
    //for (int i = 0; i < n; i++) {
    //    cin >> nums[i];
    //}
    vector<int>nums = { 1,2,3,4 };
    int n = nums.size();
	
	if(n==1&&nums[0]==10){		//特殊情况进行处理
	count<<"0-1"<<endl;
	reutrn;
	}
	
    vector<arr>que;
    arr temp = { nums,1 };		//初试数组放进去
    que.push_back(temp);

    for (int i = n; i > 1; i--) {   
            int count = que.size();		//当前层的大小
            vector<arr>temp_que;		//下一层临时存储
            for (int i = count-1; i >=0; i--) {	//遍历,从后往前从前往后都行
                vector<int>temp = que[i].a;			
                int temp1 = temp.back();			//拿出后两个值
                temp.pop_back();
                int temp2 = temp.back();
                temp.pop_back();

                int temp3 = (temp1 + temp2) % 10;		//加运算
                int temp4 = (temp1 * temp2) % 10;		//乘运算
                bool is3 = false;						//加后的是否有重复的
                bool is4 = false;						//乘后的是否有重复的
                for (int j = 0; j < temp_que.size(); j++) {		//遍历已操作的数组,去重本次
                    if (temp_que[j].a.back() == temp3 && temp3 == temp4) {
                        temp_que[j].nums += 2*que[i].nums;
                        is3 = true;
                        is4 = true;
                        break;
                    }
                    else if (temp_que[j].a.back() == temp3&&!is3) {
                        temp_que[j].nums += que[i].nums;
                        is3 = true;
                    }
                    else if (temp_que[j].a.back() == temp4&&!is4) {
                        temp_que[j].nums += que[i].nums;
                        is4 = true;
                    }
                }
                if (!is3) {
                    temp.push_back(temp3);
                    temp_que.push_back({ temp,que[i].nums });
                    temp.pop_back();
                }
                if (!is4) {
                    temp.push_back(temp4);
                    temp_que.push_back({ temp,que[i].nums });
                }
            }
            que = temp_que;	   			//替换本层数组
        
    }
    for (auto x : que) {
        cout << x.a[0] << "-" << x.nums << endl;
    }

}

美团

第3题
找总值,长度想等,每位都不同的组合数量,每位最大为300,最小为1;
栗子:
输入
1,1,3
输出
1----------只有2,2,1这一种可能
使用回溯+记忆数组的思路(今年考好几个这种题了)
dp[i][j] 其中i为已经填补的位的数量,j为已填补的位上数的和,dp[i][j]为剩下没填补位一共有几种可能。
直接上代码

#include <cstdlib>
#include <iostream>
#include<vector>

using namespace std;
int ans = 0;
int summ(vector<int>& path) {//vector求和函数
    int sum_path = 0;
    for (int i = 0; i < path.size(); i++) {
        sum_path += path[i];
    }
    return sum_path;
}
void track(vector<int>& nums, int sum, vector<int>& path, vector<vector<int>>& dp) {
    int len1 = nums.size(), len2 = path.size();//拿到已填充的长度和整体长度
    int temp_sum = summ(path);					//拿到已填充位之和
    if (len2 == len1 - 1) {						//如果只剩最后一位,可能的结果要不为1要不为0
        if (sum - temp_sum != nums.back())		//题中要求同位数字不能一样
            dp[len2][temp_sum] = 1;
        else
        dp[len2][temp_sum] = 0;
        return;
    }
    if (dp[len2][temp_sum] != -1) {			//记忆数组的重点,当前dp不为-1说明已经计算过,									
        return;							   //不进行继续计算
    }
    int temp = 0;							//计算本轮可组合的个数
    int begin = temp_sum - 300 * (len1 - len2 - 1);	//剪枝方法,
    int end = sum - temp_sum - len1 + len2 + 1;		//剪枝方法,
    for (int i = 1 > begin ? 1 : begin; i <= end; ++i) {
        if (nums[len2] == i)				//题中要求的条件
            continue;
        path.push_back(i);					//可填上当前位
        track(nums, sum, path, dp);
        temp+= dp[len2+1][summ(path)];
        path.pop_back();					//回退
    }
    dp[len2][temp_sum] = temp;             //记录记忆数组
}

int main() {

    int count = 3;
    vector<int>nums={1,1,3};
    vector<int>path;
    int sum = 0;
    for (int i = 0; i < count; ++i) {
        sum += nums[i];
    }
    vector<vector<int>>dp(count, vector<int>(sum, -1));
    track(nums, sum, path, dp);
    cout << dp[0][0] << endl;		//啥也不填计算要的结果
}
// 64 位输出请用 printf("%lld")

快手面试题

在这里插入图片描述
在这里插入图片描述
重点:观察题目,如果手牌中某种花色<3,一定只能凑同花,所以先紧着同花整,整不出来就肯定凑不成手牌,所有花色数量<3的牌都凑成同花再凑顺子,顺子,相当于连续数组。
上代码

#include<iostream>
#include<vector>
#include<algorithm>
#include<map>
using namespace std;

int main() {
	vector<int>nums({ 101,103,204,104,102,304 });//性感荷官在线发的牌
	vector<bool>used(nums.size(), false);		//用来标记当前手牌有没有凑成对
	sort(nums.begin(), nums.end());				//先理下牌
	map<int,int>colors;							//记录手牌花色的数量
	vector<vector<int>>ans;
	for (int i = 0; i < nums.size(); i++) {
		colors[nums[i] / 100]++;
	}
	for (auto x : colors) {						//先匹配同花,找到花色数量<3的花色
		if (x.second < 3) {
			int color = x.first;
			for (int i = 0; i < nums.size(); i++) {
				if (used[i])
					continue;
				if (nums[i] / 100 == color) {
					int num = nums[i] % 100;	//找到花色数量<3的数字
					vector<int>temp;
					temp.push_back(nums[i]);
					used[i] = true;
					for (int j = 0; j < nums.size(); j++) {
						if (used[j])
							continue;					//找到花色数量<3的数字相同且花色不同的牌
						if (num == nums[j] % 100 && color != nums[j] / 100) {
							temp.push_back(nums[j]);
							used[j] = true;
						}
					}
					if (temp.size() < 3)		//凑不到3张,退出,返回-1;
					{
						cout << -1;
						return 0;
					}
					ans.push_back(temp);	//凑到了,压入答案里
				}
			}
		}
	}
	vector<int>temp;
	for (int i = 0; i < nums.size(); i++) {		//开始找顺子
		if (used[i])
			continue;
		if (temp.empty() || temp.back() == nums[i] - 1)	//排序了,所以直接找连续数
		{
			temp.push_back(nums[i]);
			used[i] = true;
		}
	
		else {
			if (temp.size() < 3)				//凑不到3张,退出,返回-1;
			{
				cout << -1;
				return 0;
			}
			ans.push_back(temp);
			temp.clear();
		}

	}
	if (!temp.empty())						//最后一组压入答案
		ans.push_back(temp);
		
	for (auto x : used) {					//判断是否所有手牌都用了
		if (!x) {
		cout << -1;
		return 0;
		}		
	}
	for (auto x : ans) {				//打印输出结果
		for (auto x1 : x) {
			cout << x1 << " ";
		}
		cout << endl;
	}

}

广联达笔试

请添加图片描述
请添加图片描述
懒得构建树了,直接拿map查找了,其实用int也行,范围应该不会超。先找叶子,叶子为1,然后往上找节点。

#include<iostream>
#include<map>
#include<vector>
using namespace std;
int plan(map<long long, vector<long long>>& a, long long b) {
    int res = 0;
    if (a.find(b) == a.end())	//map里没有说明是叶子节点返回1;
        return 1;
    else {						//map里有说明是节点继续找叶子节点;
        vector<long long> temp = a[b];
        for (int i = 0; i < temp.size(); i++) {
            res += plan(a, temp[i]);
        }
    }
    return res;
}
int main() {
    long long n = 0, m = 0, q = 0;
    cin >> n >> m >> q;
    map<long long, vector<long long>>a;		//key为父节点,val为vector数组,存它的孩子们
    vector<vector<long long>>num(2, vector<long long>(m, 0));	//专门用来接受输入的
    vector<long long>ques(q, 0);		//结果要的路子
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < m; j++) {
            cin >> num[i][j];
        }
    }

    for (int j = 0; j < q; j++) {
        cin >> ques[j];
    }
    for (int j = 0; j < m; j++) {
        a[num[0][j]].push_back(num[1][j]);	//吧路径塞到map里
    }
    vector<long long>dp(n + 1, 0);			//dp放每个节点的路子
    for (int i = n; i > 0; i--) {
        if (a.find(i) == a.end()) {			//map里没有,说明为叶子节点直接放1
            dp[i] = 1;
        }else{							//map里有,说明为节点找它儿子看看
            vector<long long>temp = a[i];
             for (int j = 0; j < temp.size(); j++) {
               		dp[i] += plan(a, temp[j]);	  //把它儿子的路都加起来给他
           }
        }
    }
    for (int i = 0; i < ques.size(); i++) {
        cout << dp[ques[i]] << " ";
    }
}

掌阅科技笔试

第三题题目
机器人从起始点走到目标点最短的步数
。证明能走 s为起始点 #不能走 e为终点
输入

3 3
。# e
。# 。
s 。 。
使用广度优先遍历,将下一步的路径点与步数压入队列,同时遍历后将map改为0,不再走了

int main() {
    int n, m;
    cin >> n >> m;
    vector<vector<char>>map(n, vector<char>(m));
    int a, b;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> map[i][j];
            if (map[i][j] == 's') {
                a = i;
                b = j;
            }
        }
    }
    queue<int>m_q;
    m_q.push(a);//压x
    m_q.push(b);//压y
    int i = 0;
    m_q.push(i);//压步数
    while (!m_q.empty()) {
        a = m_q.front();//弹x
        m_q.pop();
        b = m_q.front();//弹y
        m_q.pop();
        i = m_q.front();//弹步数
        m_q.pop();
        if (map[a][b] == 'e') {
            cout << i;	//找到终点,输出当前步数
            return 0;
        }
        else if (map[a][b] == '.' || map[a][b] == 's') {
            map[a][b] = '0';	//减少重复遍历
            			//下面四组遍历条件,同时压队列
            if (a + 1 < map.size()) {
                m_q.push(a + 1);
                m_q.push(b);
                m_q.push(i + 1);
            }

            if (b + 1 < map[a].size())
            {
                m_q.push(a);
                m_q.push(b + 1);
                m_q.push(i + 1);
            }
            if (a - 1 >= 0)
            {
                m_q.push(a - 1);
                m_q.push(b);
                m_q.push(i + 1);
            }
            if (b - 1 >= 0)
            {
                m_q.push(a);
                m_q.push(b - 1);
                m_q.push(i + 1);
            }
        }
    }
    cout << -1;

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值