名企高頻筆試題目(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;
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM