给秋招加点料——Hot15道高频算法面试题!

Posted _Rikka_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了给秋招加点料——Hot15道高频算法面试题!相关的知识,希望对你有一定的参考价值。

1.链表篇

反转链表

难度:easy

考察次数:189

传送门

题目描述
输入一个链表,反转链表后,输出新链表的表头。

思路:创建三个指针,一个表示前一个结点pre,一个表示当前节点cur,一个表示后一个节点next。循环next=cur->next,cur->next=pre,pre=cur,cur=next,直到当前节点为NULL。

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
    ListNode* cur=pHead,*pre=NULL,*next;
        while(cur!=NULL)
        {
            next=cur->next;
            cur->next=pre;
            pre=cur;
            cur=next;
        }
        return pre;}
};

判断链表中是否有环

难度:easy

考察次数:120

传送门

题目描述
判断给定的链表中是否有环。如果有环则返回true,否则返回false。
你能给出空间复杂度的解法么?

思路:采用快慢指针解决,慢指针每次向前移动一步,快指针每次向前移动2步,如果链表中有环,快慢指针一定会相遇,注意一下溢出,判断快指针的下一步是不是NULL,如果为NULL则fast->next->next就溢出了。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        ListNode *fast=head,*cur=head;
        while(fast!=NULL&&fast->next!=NULL)
        {
            cur=cur->next;
            fast=fast->next->next;
            if(cur==fast)return true;
        }
        return false;
    }
};

合并有序链表

难度: middle

考察次数:57

传送门

题目描述

将两个有序的链表合并为一个新链表,要求新的链表是通过拼接两个链表的节点来生成的,且合并后新链表依然有序。

思路:先确定表头,创建一个cur表示当前指针,之后比较两个链表表头节点,每次都让cur->next=较小表头的指针,并让该指针移动到其next,直到某链表为空,将剩余链表全部接到cur后面即可。

AC code

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */

class Solution {
public:
    /**
     * 
     * @param l1 ListNode类 
     * @param l2 ListNode类 
     * @return ListNode类
     */
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        // write code here
        ListNode* cur,*head;
            if(l1==NULL)return l2;
            else if(l2==NULL)return l1;
            else if(l1->val<=l2->val){cur=l1;l1=l1->next;}
        else {cur=l2;l2=l2->next;}
        head=cur;
        while(!(l1==NULL&&l2==NULL))
        {
            if(l2==NULL){cur->next=l1;l1=l1->next;}
            else if(l1==NULL) {cur->next=l2;l2=l2->next;}
            else if(l1->val<=l2->val){cur->next=l1;l1=l1->next;}
            else {cur->next=l2;l2=l2->next;}
            cur=cur->next;
        }
        return head;
    }
};

2.动态规划篇

跳台阶

难度: easy

考察次数: 55

传送门

题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

思路: d[i]表示从0到第i个台阶总共的方案数。转移方程:dp[i]=dp[i-1]+dp[i-2],因为当前台阶可以由前两级台阶走到,初始dp[0]=1,dp[1]=1

AC code

class Solution {
public:
    int jumpFloor(int number) {
    int dp[10000];
        dp[1]=1,dp[0]=1;
        for(int i=2;i<=number;i++)
        {
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[number];
    }
};

子数组的最大累加和

难度:easy

考察次数:52

传送门

题目描述

给定一个数组arr,返回子数组的最大累加和
例如,arr = [1, -2, 3, 5, -2, 6, -1],所有子数组中,[3, 5, -2, 6]可以累加出最大的和12,所以返回12.
题目保证没有全为负数的数据
[要求]
时间复杂度为O(n)O(n)O(n),空间复杂度为O(1)O(1)O(1)

思路:dp[i]为以第i个元素为结尾的最大累加和,状态转移方程:dp[i]=max(dp[i-1]+arr[i],arr[i]),从这可看出其实dp[i]只与dp[i-1]有关,所以对于dp的存储只需要一个空间,每次更新当前dp即可dp(相当于更新后的dp[i])=max(dp(相当于dp[i-1])+arr[i],arr[i])。

AC code

class Solution {
public:
    /**
     * max sum of the subarray
     * @param arr int整型vector the array
     * @return int整型
     */
    int dp=0,ans=0;
    int maxsumofSubarray(vector<int>& arr) {
        // write code here
        for(int i=0;i<arr.size();i++)
        {
            dp=max(dp,0)+arr[i];
            ans=max(ans,dp);
        }
        return ans;
        
    }
};

求路径

难度:easy

考察次数:15

传送门

题目描述

一个机器人在m×n大小的地图的左上角(起点)。
机器人每次向下或向右移动。机器人要到达地图的右下角(终点)。
可以有多少种不同的路径从起点走到终点?

在这里插入图片描述

备注:m和n小于等于100,并保证计算结果在int范围内

思路: dp[i][j]表示走到 位置 i,j 的路径数,状态转移方程 dp[i][j]=dp[i-1][j]+dp[i][j-1] ,注意边界的处理情况。

AC code

class Solution {
public:
    /**
     * 
     * @param m int整型 
     * @param n int整型 
     * @return int整型
     */
    
    long long dp[105][105];
    long long dfs(int x,int y,int n,int m)
    {
        if(dp[x][y])return dp[x][y];
        if(x<1||x>n||y<1||y>m)return 0;
        return dp[x][y]=dfs(x-1,y,n,m)+dfs(x,y-1,n,m);
    }
    long long uniquePaths(int m, int n) {
        // write code here
        dp[1][1]=1;
        return dfs(m,n,m,n);
    }
};

最长公共子串

难度:middle

考察次数:52

传送门

题目描述
给定两个字符串str1和str2,输出两个字符串的最长公共子串
题目保证str1和str2的最长公共子串存在且唯一。

思路: dp[i][j]代表str1串第以第i个字符结尾和str2串的公共串长度。状态转移方程:如果:str1[i]==str2[j],dp[i][j]=dp[i-1][j-1]+1,如果str1[i]!=str2[j],dp[i][j]=0。然后题目求的是最长串,那我们只需记录哪时dp[i][j]最大,最长公共串即为此时的i向前取dp[i][j]长度。

AC code

class Solution {
public:
    /**
     * longest common substring
     * @param str1 string字符串 the string
     * @param str2 string字符串 the string
     * @return string字符串
     */
    int dp[5005][5005];
        int ans=0;

   
        string LCS(string str1, string str2) {
        
        string str;
        for (int i = 0;i < str1.size();i++)
        {
            for (int j = 0;j < str2.size();j++)
            {
                if (str1[i] == str2[j])
                {
                    if (i - 1 >= 0 && j - 1 >= 0)
                    {
                        dp[i][j] = dp[i - 1][j - 1] + 1;
                        if (dp[i - 1][j - 1] + 1 > ans)
                        {
                            str = str1.substr(i - dp[i - 1][j - 1], dp[i - 1][j - 1] + 1);
                            ans = max(dp[i - 1][j - 1] + 1, ans);
                        }
                    }
                    else {[`
                        dp[i][j] = 1;
                        if (1 >= ans) { ans = 1;str = str1[i]; }
                    }
                }
                else dp[i][j] = 0;
            }
        }
        return str;
    }
    
};

3.树篇

两个节点最近公共祖先

难度:middle

考察次数:32

传送门

题目描述
给定一棵二叉树以及这棵树上的两个节点 o1 和 o2,请找到 o1 和 o2 的最近公共祖先节点。

思路: 用一个map[i]记录以当前节点i为根,如果子树有一个所要求的节点则map[i]=1,两个则map[i]=2,从根节点深度优先搜索,第一个map为二的节点即为最近的公共祖先。

AC code

/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 * };
 */

class Solution {
public:
    /**
     * 
     * @param root TreeNode类 
     * @param o1 int整型 
     * @param o2 int整型 
     * @return int整型
     */
   map<int, int> ma;
int dfs(TreeNode* n,int a,int b)
{
	if (n == NULL)return 0;
	if (n->val == a )ma[a]++;
	if (n->val == b )ma[b]++;

		int g = ma[n->val] + dfs(n->left, a, b) + dfs(n->right, a, b);
		if (f==-1 && g >= 2)
		{
			f = n->val;
		}
	return g;
}
int f = -1;
int lowestCommonAncestor(TreeNode* root, int o1, int o2) {
	dfs(root, o1, o2);
    return f;

}
};

实现二叉树先中后序排列

难度:middle

考察次数:97

传送门

题目描述
分别按照二叉树先序,中序和后序打印所有的节点。

思路: 深度优先搜索,先序遍历即每到一个节点优先访问当前的节点值,后续遍历最后访问当前节点值,中序遍历先访问左节点再访问当前节点值最后访问右节点。

AC code

/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 * };
 */
#include<stack>

class Solution {
public:
    /**
     * @param root TreeNode类 the root of binary tree
     * @return int整型vector<vector<>>
     */
    int lst[4][100005];
    int g[4];
    void preorder(TreeNode *p)
    {
        if(p==NULL)return;
        lst[1][++g[1]]=p->val;
        preorder(p->left);
        preorder(p->right);
    }
    void pastorder(TreeNode *p)
    {
        if(p==NULL)return;
        pastorder(p->left);
        pastorder(p->right);
        lst[3][++g[3]]=p->val;
    }
    void inOrder(TreeNode* p)
    {
        if(p==NULL)return;
        inOrder(p->left);
        lst[2][++g[2]]=p->val;
        inOrder(p->right);
    }
    vector<vector<int> > threeOrders(TreeNode* root) {
        // write code here
        g[1]=0,g[2]=0,g[3]=0;
        preorder(root);
        
        inOrder(root);
        
        pastorder(root);
            
        vector<vector<int>> ans;
        
        for(int i=1;i<=3;i++)
        {
            vector<int> vec;
            for(int j=1;j<=g[i];j++)vec.push_back(lst[i][j]);
            ans.push_back(vec);
        }
        return ans;
    }
};

二叉树之字形遍历

难度:middle

考察次数:37

传送门

题目描述
给定一个二叉树,返回该二叉树的之字形层序遍历,(第一层从左向右,下一层从右向左,一直这样交替)
例如:
给定的二叉树是{3,9,20,#,#,15,7},

该二叉树之字形层序遍历的结果是

[
[3],
[20,9],
[15,7]
]

思路: 每次遍历一层的节点保存到一个列表中,然后下一层要从相反方向遍历,那么只需从当前层的后面往前面遍历子节点即可(每个下一层都是前一层的相反方向就实现了之字形遍历了),还要注意每次到新的一层要改变一下遍历方向,比如这层先遍历左节点再右节点,下层先右节点再左节点。

AC code

/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 * };
 */

class Solution {
public:
    /**
     * 
     * @param root TreeNode类 
     * @return int整型vector<vector<>>
     */
    vector<vector<int> > zigzagLevelOrder(TreeNode* root) {
        // write code here
        vector<vector<int>> ans;
        if(root==NULL)return ans;
        vector<TreeNode*> vec,t;
        vector<int> lst{root->val};
        ans.push_back(lst);
        vec.push_back(root);
        
        int f=1;
        while(!vec.empty())
        {
           t.clear();
                for(int i=vec.size()-1;i>=0;i--)
                {
                    if(f==1)
                    {
                    if(vec[i]->right!=NULL)t.push_back(vec[i]->right);
                    if(vec[i]->left!=NULL)t.push_back(vec[i]->left);
                        
                    }
                    else
                    {
                    if(vec[i]->left!=NULL)t.push_back(vec[i]->left);
                    if(vec[i]->right!=NULL)t.push_back(vec[i]->right);
                        
                    }
                }
            vec=t;
            vector<int> tt;
            for(int i=0;i<vec.以上是关于给秋招加点料——Hot15道高频算法面试题!的主要内容,如果未能解决你的问题,请参考以下文章

秋招冲刺应届生JAVA岗-每日5道高频面试题Day6- 基础篇

秋招冲刺-每日打卡应届生JAVA岗-每日5道高频面试题Day2-基础篇

秋招冲刺应届生JAVA岗-每日5道高频面试题Day7- 集合篇

秋招冲刺-每日打卡应届生JAVA岗-每日5道高频面试题Day3-基础篇

秋招冲刺-每日打卡应届生JAVA岗-每日5道高频面试题Day5- 异常类篇

秋招冲刺-每日打卡应届生JAVA岗-每日5道高频面试题Day4-基础篇