名企高频笔试题目(2)

最小编辑代价

题目描述

给定两个字符串str1和str2,再给定三个整数ic,dc和rc,分别代表插入、删除和替换一个字符的代价,请输出将str1编辑成str2的最小代价。

示例1

输入
"abc","adc",5,3,2
输出
2

示例2

输入
"abc","adc",5,3,100
输出
8
int minEditCost(string str1, string str2, int ic, int dc, int rc) {
	// write code here
	int len1 = str1.length() + 1, len2 = str2.length() + 1;
	vector<vector<int> > dp(len1, vector<int>(len2, 0));  //str1是行,str2是列
	for (int i = 1; i < len1; ++i)  dp[i][0] = dc * i;  //从i到i-1对str1是delete
	for (int j = 1; j < len2; ++j)  dp[0][j] = ic * j;  //从j到j-1对str1是insert
	for (int i = 1; i < len1; ++i)
		for (int j = 1; j < len2; ++j)
			if (str1[i - 1] == str2[j - 1])  dp[i][j] = dp[i - 1][j - 1];
			else  dp[i][j] = min(dp[i - 1][j] + dc, min(dp[i][j - 1] + ic, dp[i - 1][j - 1] + rc));
	return dp[len1 - 1][len2 - 1];
}

找到字符串的最长无重复字符子串

题目描述

给定一个数组arr,返回arr的最长无的重复子串的长度(无重复指的是所有数字都不相同)。

示例1

输入
[2,3,4,5]
输出
4

示例2

输入
[2,2,3,4,3]
输出
3

分析:

哈希+双指针,检测到重复值则滑动窗口,记录最大的长度,时间复杂度O(N)

int maxLength(vector<int>& arr) {
    unordered_map<int, int> hash_map;  //哈希表
    int maxLen = 0;
    for (int l = 0, r = 0; r < arr.size(); ++r) {
        if (hash_map.find(arr[r]) != hash_map.end() && hash_map[arr[r]] >= l) {  //滑动窗口
            l = hash_map[arr[r]] + 1;
        }
        hash_map[arr[r]] = r;  //建立映射
        maxLen = max(maxLen, r - l + 1);
    }
    return maxLen;
}

用两个栈实现队列

题目描述

用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

class Solution
{
public:
	void push(int node) {
		stack1.push(node);
	}

	int pop() {
		if (stack1.empty()) return -1;  //异常处理
		while (!stack1.empty()) {  //主栈到辅栈,倒置
			stack2.push(stack1.top());
			stack1.pop();
		}
		int res = stack2.top();
		stack2.pop();  //删除"队首"元素
		while (!stack2.empty()) {  //辅栈到主栈,还原
			stack1.push(stack2.top());
			stack2.pop();
		}
		return res;  //返回被删除元素
	}

private:
	stack<int> stack1;  //主栈
	stack<int> stack2;//辅栈
};

在二叉树中找到两个节点额最近公共祖先节点

题目描述

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

示例1

输入
[3,5,1,6,2,0,8,#,#,7,4],5,1
输出
3

分析:

递归,边界条件是到达叶子节点或者找到目标节点。当满足同时覆盖包含两个节点时返回。

    int lowestCommonAncestor(TreeNode* root, int o1, int o2) {
        if(root == nullptr) return 0;  //达到叶子
        if(o1 == root->val || o2 == root->val) return root->val;  //找到目标节点
        int l = lowestCommonAncestor(root->left, o1, o2);  //左子树,若不包含返回0
        int r = lowestCommonAncestor(root->right, o1, o2);  //右子树,若包含返回val
        if(l && r) return root->val;  //若左右子树都包含返回该节点
        return l ? l : r;  //若左子树包含返回左,否则返回右
    }

这个写法在查找元素0的时候会有bug,但测试用例似乎没有这一项。

进制转化

题目描述

给定一个十进制数M,以及需要转换的进制数N。将十进制数M转化为N进制数

示例1

输入
7,2
输出
"111"
备注:
M是32位整数,2<=N<=16.

测试用例有负数,没法子直接只用_itos_s()函数,所以得自己写。

string decTo(int dec, int base) {  //十进制转其他进制v1.0
    string res = "";
    bool negative = false;
    if (dec < 0) {
        negative = true;
        dec = -dec;
    }
    do {
        int t = dec % base;
        if (t >= 0 && t <= 9) res += '0' + t;
        else res += t - 10 + 'a';
        dec = dec / base;
    } while (dec);
    if (negative) res += '-';
    reverse(res.begin(), res.end());
    return res;
}

只有正数的情况下,以下做法亦可。

string intTo(int dec, int base) {  //十进制转其他进制v2.0
    char buffer[20];
    _itoa_s(dec, buffer, base);
    return string(buffer);
}

int toInt(string str, int base) {  //其他进制转10进制
    char* buffer = const_cast<char*>(str.c_str());
    char *res;
    return strtol(buffer, &res, base);
}

重建二叉树

题目描述

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

分析:
人生苦短,有时候纠结cpp的指针,下标,不如Python快乐。

class Solution:
    def reConstructBinaryTree(self, pre, tin): 
        if not pre or not tin:
            return None
        root = TreeNode(pre.pop(0))
        index = tin.index(root.val)
        root.left = self.reConstructBinaryTree(pre, tin[:index])
        root.right = self.reConstructBinaryTree(pre, tin[index + 1:])
        return root

最长递增子序列

题目描述

给定数组arr,设长度为n,输出arr的最长递增子序列。(如果有多个答案,请输出其中字典序最小的)

示例1

输入
[2,1,5,3,6,4,8,9,7]
输出
[1,3,4,8,9]

示例2

输入
[1,2,8,6,4]
输出
[1,2,4]
说明
其最长递增子序列有3个,(1,2,8)、(1,2,6)、(1,2,4)其中第三个字典序最小,故答案为(1,2,4)

分析:

LIS求法见最长递增子序列,本题的不同之处:为了获取字典序最小,需要从后往前遍历,取每个位置上最后被替换的元素。

vector<int> LIS(vector<int>& arr) {
	// write code here
	int len = arr.size();
	vector<int> res;
	vector<int> temp;  //每个位置的LIS长度
	res.emplace_back(arr.front());
	temp.emplace_back(0);
	for (unsigned int i = 1; i < len; ++i)
		if (arr[i] > res.back())
		{
			res.emplace_back(arr[i]);
			temp.emplace_back(res.size() - 1);
		}
		else {
				int pos = lower_bound(res.begin(), res.end(), arr[i]) - res.begin();
				res[pos] = arr[i];
				temp.emplace_back(pos);
		}
	for (int i = len - 1, k = res.size() - 1; k >= 0; --i)
		if (temp[i] == k)
		{
			res[k] = arr[i];
			--k;
		}
	return res;
}

岛屿数量

题目描述

给一个01矩阵,1代表是陆地,0代表海洋, 如果两个1相邻,那么这两个1属于同一个岛。我们只考虑上下左右为相邻。

岛屿: 相邻陆地可以组成一个岛屿(相邻:上下左右) 判断岛屿个数。

示例1

输入
[[1,1,0,0,0],[0,1,0,1,1],[0,0,0,1,1],[0,0,0,0,0],[0,0,1,1,1]]
输出
3
void dfs(vector<vector<char> >& grid, int x, int y) {
	if (x < 0 || x >= grid.size() || y < 0 || y >= grid[0].size() || grid[x][y] == '0')
		return;
	int dx[4] = { -1,1,0,0 }, dy[4] = { 0,0,1,-1 };
	grid[x][y] = '0';
	for (int i = 0; i < 4; i++) {
		dfs(grid, x + dx[i], y + dy[i]);
	}
}
int solve(vector<vector<char> >& grid) {
	int ans = 0;
	for (int i = 0; i < grid.size(); i++)
		for (int j = 0; j < grid[0].size(); j++)
			if (grid[i][j] == '1') {
				ans++;
				dfs(grid, i, j);
			}
	return ans;
}

判断t1树与t2树是否有拓扑结构相同的子树

题目描述

给定彼此独立的两棵二叉树,判断 t1 树是否有与 t2 树拓扑结构完全相同的子树。

设 t1 树的边集为 E1,t2 树的边集为 E2,若 E2 等于 E1 ,则表示 t1 树和t2 树的拓扑结构完全相同。

示例1

输入
{1,2,3,4,5,6,7,#,8,9},{2,4,5,#,8,9}
输出
true

分析

bool isContains(TreeNode* root1, TreeNode* root2) {
	// write code here
	if (root1 == nullptr) return false;
	if (check(root1, root2)) return true;  //检查当前为根时是否满足
	return isContains(root1->left, root2) || isContains(root1->right, root2);  //递归检查左右子树
}
bool check(TreeNode* root1, TreeNode* root2) {
	if (root1 == nullptr && root2 == nullptr) return true;  //到达叶子节点
	else if (root1 == nullptr || root2 == nullptr)  return false;
	else if (root1->val != root2->val)  return false;
	return check(root1->left, root2->left) && check(root1->right, root2->right);  //检查是否一致
}

在两个长度相等的排序数组中找上中位数

题目描述

给定两个有序数组arr1和arr2,已知两个数组的长度都为N,求两个数组中所有数的上中位数。

上中位数:假设递增序列长度为n,若n为奇数,则上中位数为第n/2+1个数;否则为第n个数

[要求]

时间复杂度为O(logN)O(log**N),额外空间复杂度为O(1)O(1)

示例1

输入
[1,2,3,4],[3,4,5,6]
输出
3
说明
总共有8个数,上中位数是第4小的数,所以返回3。

示例2

输入
[0,1,2],[3,4,5]
输出
2
说明
总共有6个数,那么上中位数是第3小的数,所以返回2
class Solution {
public:
    /**
     * find median in two sorted array
     * @param arr1 int整型vector the array1
     * @param arr2 int整型vector the array2
     * @return int整型
     */
	int findMedianinTwoSortedAray(vector<int>& arr1, vector<int>& arr2) {
		// write code here
		int i = -1, j = -1, k = arr1.size() - 1;
		bool flag = true;
		while (true) {
			if (arr1[i + 1] < arr2[j + 1]) {
				++i;
				if (!flag)  flag = true;  //在左
			}
			else {
				++j;
				if (flag)  flag = false;  //在右
			}
			if (i == -1 && j == k || j == -1 && i == k || i + j == k - 1)  break;  //全在左,全在右,左右均有
		}
		if (flag) return arr1[i];
		else return arr2[j];
	}
};

检测环的入口

题目描述

对于一个给定的链表,返回环的入口节点,如果没有环,返回null

拓展:

你能给出不利用额外空间的解法么?

    ListNode *detectCycle(ListNode *head) {
        ListNode *fast = head;
        ListNode *slow = head;
        while(fast && fast->next)  //链表无环时长度可奇可偶
        {
            fast = fast->next->next;
            slow = slow->next;
            if(fast == slow)  break;  //检测环的存在
        }
        if(head == nullptr || head->next == nullptr|| fast != slow)  return nullptr;  //空链表,单节点无环链表,多节点无环链表
        ListNode *del = head;
        while(del->next)
        {   
            ListNode *temp = del;
            del = del->next;
            temp->next = nullptr;  //入口有两个next指针指向它
        }
        return del;
    }

删除链表倒数第k个节点

题目描述

给定一个链表,删除链表的倒数第n个节点并返回链表的头指针
例如,

 给出的链表为:1->2->3->4->5, n= 2.
 删除了链表的倒数第n个节点之后,链表变为1->2->3->5.

备注:

题目保证n一定是有效的
请给出请给出时间复杂度为\ O(n) O(n)的算法

示例1

输入
{1,2},2
输出
{2}

分析

双指针,尤其要注意删除节点是第一个节点时的操作。

ListNode* removeNthFromEnd(ListNode* head, int n) {
	// write code here
	if (head == nullptr)  return nullptr;
	ListNode* preDel = head;  //删除节点前一个位置
	ListNode* sentry = head;  //哨兵
	while (n--) sentry = sentry->next;
	while (sentry && sentry->next)  //删除第一个时sentry ==nullptr
	{
		preDel = preDel->next;
		sentry = sentry->next;
	}
	if (preDel == head && sentry == nullptr)  return head->next;  //第一个指针
	preDel->next = preDel->next->next;  //其他情况
	return head;
}

二叉树最大路径和

题目描述

给定一个二叉树,请计算节点值之和最大的路径的节点值之和是多少。
这个路径的开始节点和结束节点可以是二叉树中的任意节点
例如:
给出以下的二叉树,
img
返回的结果为6

示例1

输入
{-2,1}
输出
1

示例2

输入
{-2,#,-3}
输出
-2

分析

int maxValue = INT_MIN;
int maxPathSum(TreeNode* root) {
	// write code here
	pathSum(root);
	return maxValue;
}
int pathSum(TreeNode* root) {
	if (root == nullptr) return 0;  //叶子节点
	int left = max(0, pathSum(root->left));  //左子树的最大路径,筛除负数
	int right = max(0, pathSum(root->right));  //右子树的最大路径,筛除负数
	maxValue = max(maxValue, root->val + left + right);  //以当前节点为根的最长路径
	return max(left, right) + root->val;  //路径中只有一个节点能左右子树都选
}

合并两个有序的数组

题目描述

给出两个有序的整数数组imgimg,请将数组 img合并到数组 img中,变成一个有序的数组
注意:
可以假设 img数组有足够的空间存放 img数组的元素, imgimg中初始的元素数目分别为 imgimg

分析:

不必新开一个空间,可以在A数组上原地操作。从前往后处理有可能破坏未合并的数据,所以从后往前处理

void merge(int A[], int m, int B[], int n) {
	int i = m - 1, j = n - 1, k = m + n - 1;
	while (i >= 0 || j >= 0)
	{
		if (i >= 0 && A[i] >= B[j] || j < 0) A[k--] = A[i--];
		else A[k--] = B[j--];
//		if (j >= 0 && A[i] <= B[j] || i < 0) A[k--] = B[j--];
//		else A[k--] = A[i--];
	}
	return;
}

二叉搜索树的第K个节点

题目描述

给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。

分析一:二叉排序树满足左子树 < 根 < 右子树的性质,可以中序遍历,在找到第k小的节点后终止遍历。

    TreeNode* KthNode(TreeNode* pRoot, int k)
    {
        TreeNode* res = nullptr;
        int cnt = 0;
        inOrder(pRoot,res, k, cnt);
        return res;
    }
    void inOrder(TreeNode* pRoot,TreeNode* &res,int k,int &count)
    {
        if(pRoot == nullptr || k == count)  return;
        inOrder(pRoot->left,res,k,count);
        count++;
        if(count == k)  res = pRoot;
        inOrder(pRoot->right,res,k,count);
    }

分析二:根据每个节点的位置选择向左子树找还是向右子树找

    TreeNode* KthNode(TreeNode* pRoot, int k)
    {
        if(pRoot == nullptr)  return nullptr;
        int cur = countNode(pRoot->left);
        if(k <= cur)  return KthNode(pRoot->left, k);  //在左子树
        if(k == cur + 1)  return pRoot;  //当前节点
        return KthNode(pRoot->right,k - cur - 1);  //在右子树,左子树cur,根1
    }
    int countNode(TreeNode* pRoot)  //当前结点为根的子树的结点数目
    {
        if(pRoot == nullptr) return 0;
        return countNode(pRoot->left)+countNode(pRoot->right)+1;
    }
posted @ 2020-09-07 15:46  kite97  阅读(599)  评论(0编辑  收藏  举报