最小編輯代價
題目描述
給定兩個字符串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;
}
二叉樹最大路徑和
題目描述
給定一個二叉樹,請計算節點值之和最大的路徑的節點值之和是多少。
這個路徑的開始節點和結束節點可以是二叉樹中的任意節點
例如:
給出以下的二叉樹,
返回的結果為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; //路徑中只有一個節點能左右子樹都選
}
合並兩個有序的數組
題目描述
給出兩個有序的整數數組和
,請將數組
合並到數組
中,變成一個有序的數組
注意:
可以假設 數組有足夠的空間存放
數組的元素,
和
中初始的元素數目分別為
和
分析:
不必新開一個空間,可以在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;
}