21.棧的壓入,彈出序列
- 題目:輸入兩個整數序列,第一個序列表示棧的壓入順序,請判斷第二個序列是否可能為該棧的彈出順序。假設壓入棧的所有數字均不相等。例如序列1,2,3,4,5是某棧的壓入順序,序列4,5,3,2,1是該壓棧序列對應的一個彈出序列,但4,3,5,1,2就不可能是該壓棧序列的彈出序列。(注意:這兩個序列的長度是相等的)
- 思路:首先要理解題意,為何(序列4,5,3,2,1是該壓棧序列對應的一個彈出序列,但4,3,5,1,2就不可能是該壓棧序列的彈出序列。)彈出序列即出棧的一種方式,4,5,3,2,1即在進棧后位1,2,3,4的情況下先出棧4,再進棧5成為1,2,3,5然后在依次出棧得到的序列。而4,3,5,1,2的最后1,2這個序列是無法實現的,2必須在1之前才正確。
因此借用一個輔助的棧,遍歷壓棧順序,先講第一個放入棧中,這里是1,然后判斷棧頂元素是不是出棧順序的第一個元素,這里是4,很顯然1≠4,所以我們繼續壓棧,直到相等以后開始出棧,出棧一個元素,則將出棧順序向后移動一位,直到不相等,這樣循環等壓棧順序遍歷完成,如果輔助棧還不為空,說明彈出序列不是該棧的彈出順序。
舉例:
入棧1,2,3,4,5
出棧4,5,3,2,1
首先1入輔助棧,此時棧頂1≠4,繼續入棧2
此時棧頂2≠4,繼續入棧3
此時棧頂3≠4,繼續入棧4
此時棧頂4=4,出棧4,彈出序列向后一位,此時為5,,輔助棧里面是1,2,3
此時棧頂3≠5,繼續入棧5
此時棧頂5=5,出棧5,彈出序列向后一位,此時為3,,輔助棧里面是1,2,3
….
依次執行,最后輔助棧為空。如果不為空說明彈出序列不是該棧的彈出順序。
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
if(pushV.empty() || popV.empty() || pushV.size()!=popV.size())
return false;
stack<int> s;
int j=0;
for(int i=0;i<pushV.size();++i){
s.push(pushV[i]);
while(!s.empty()&&s.top()==popV[j]){
s.pop();
++j;
}
}
if(s.empty())
return true;
return false;
}
};
22.從上往下打印二叉樹
- 題目:從上往下打印出二叉樹的每個節點,同層節點從左至右打印。
- 思路:使用兩個隊列一個存放節點,一個存放值。先將根節點加入到隊列中,然后遍歷隊列中的元素,遍歷過程中,訪問該元素的左右節點,再將左右子節點加入到隊列中來
class Solution {
public:
vector<int> PrintFromTopToBottom(TreeNode* root) {
vector<int> result;
if(NULL == root)
return result;
queue<TreeNode* > q;
q.push(root);
while(!q.empty())
{
TreeNode* temp = q.front();
q.pop();
result.push_back(temp->val);
if(NULL != temp->left)
q.push(temp->left);
if(NULL != temp->right)
q.push(temp->right);
}
return result;
}
};
23.二叉搜索樹的后續遍歷序列
- 題目:輸入一個整數數組,判斷該數組是不是某二叉搜索樹的后序遍歷的結果。如果是則輸出Yes,否則輸出No。假設輸入的數組的任意兩個數字都互不相同。
-
知識點:后序遍歷首先遍歷左子樹,然后遍歷右子樹,最后訪問根結點
后序遍歷結果:DEBFCA
已知前序遍歷和中序遍歷,就能確定后序遍歷
二叉搜索樹(Binary Search Tree),(又:二叉搜索樹,二叉排序樹)它或者是一棵空樹,或者是具有下列性質的二叉樹: 若它的左子樹不空,則左子樹上所有結點的值均小于它的根結點的值; 若它的右子樹不空,則右子樹上所有結點的值均大于它的根結點的值; 它的左、右子樹也分別為二叉排序樹。
- 思路:
已知條件:后序序列最后一個值為root;二叉搜索樹左子樹值都比root小,右子樹值都比root大。
1、確定root;
2、遍歷序列(除去root結點),找到第一個大于root的位置,則該位置左邊為左子樹,右邊為右子樹;
3、遍歷右子樹,若發現有小于root的值,則直接返回false;
4、分別判斷左子樹和右子樹是否仍是二叉搜索樹(即遞歸步驟1、2、3)。
class Solution {
public:
bool VerifySquenceOfBST(vector<int> sequence) {
vector<int> leftTree,rightTree;
int root; // 根結點
if(sequence.empty()) return false;
int index = 0; // 標記左右子樹界限
int len = sequence.size();
root = sequence[len-1];
int i=0;
for(;i<len-1;++i)
{
if(sequence[i]>root) break; // 找到第一個大于根結點的位置,則左邊為左子樹,右邊為右子樹
}
for(int j=i;j<len-1;++j) // 循環時去除root,因此為len-1
{
if(sequence[j]<root) return false; // 有一個小于root,則返回false
}
if(i!=0)
{
// 即有左子樹
for(int m=0;m<i;++m)
{
leftTree.push_back(sequence[m]);
}
}
if(i!=len-2)
{
for(int j=i;j<len-1;++j)
{
rightTree.push_back(sequence[j]);
}
}
if(leftTree.size()>1) VerifySquenceOfBST(leftTree);
if(rightTree.size()>1) VerifySquenceOfBST(rightTree);
return true;
}
};
24.二叉樹中和位某一值的路徑
- 題目:輸入一顆二叉樹的根節點和一個整數,打印出二叉樹中結點值的和為輸入整數的所有路徑。路徑定義為從樹的根結點開始往下一直到葉結點所經過的結點形成一條路徑。(注意: 在返回值的list中,數組長度大的數組靠前)
- 思路:
1.按先序遍歷把當前節點cur的左子樹依次入棧同時保存當前節點,每次更新當前路徑的和sum;
2.判斷當前節點是否是葉子節點以及sum是否等于expectNumber,如果是,把當前路徑放入結果中。
3.遇到葉子節點cur更新為NULL,此時看棧頂元素,如果棧頂元素的把棧頂元素保存在last變量中,同時彈出棧頂元素,當期路徑中棧頂元素彈出,sum減掉棧頂元素,這一步驟不更改cur的值;
4.如果步驟3中的棧頂元素的右子樹存在且右子樹之前沒有遍歷過,當前節點cur更新為棧頂的右子樹,此時改變cur=NULL的情況。
class Solution {
public:
vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
vector<vector<int> > res;
if (root == NULL)
return res;
stack<TreeNode *> s;
s.push(root);
int sum = 0; //當前和
vector<int> curPath; //當前路徑
TreeNode *cur = root; //當前節點
TreeNode *last = NULL; //保存上一個節點
while (!s.empty()){
if (cur == NULL){
TreeNode *temp = s.top();
if (temp->right != NULL && temp->right != last){
cur = temp->right; //轉向未遍歷過的右子樹
}else{
last = temp; //保存上一個已遍歷的節點
s.pop();
curPath.pop_back(); //從當前路徑刪除
sum -= temp->val;
} }
else{
s.push(cur);
sum += cur->val;
curPath.push_back(cur->val);
if (cur->left == NULL && cur->right == NULL && sum == expectNumber){
res.push_back(curPath);
}
cur = cur->left; //先序遍歷,左子樹先于右子樹
}
}
return res;
}
};
25.復雜鏈表的復制
- 題目:輸入一個復雜鏈表(每個節點中有節點值,以及兩個指針,一個指向下一個節點,另一個特殊指針指向任意一個節點),返回結果為復制后復雜鏈表的head。(注意,輸出結果中請不要返回參數中的節點引用,否則判題程序會直接返回空)
-
思路:
class Solution {
public:
RandomListNode* Clone(RandomListNode* pHead)
{
if(!pHead) return NULL;
RandomListNode *currNode = pHead;
while(currNode){
RandomListNode *node = new RandomListNode(currNode->label);
node->next = currNode->next;
currNode->next = node;
currNode = node->next;
}
currNode = pHead;
while(currNode){
RandomListNode *node = currNode->next;
if(currNode->random){
node->random = currNode->random->next;
}
currNode = node->next;
}
//拆分
RandomListNode *pCloneHead = pHead->next;
RandomListNode *tmp;
currNode = pHead;
while(currNode->next){
tmp = currNode->next;
currNode->next =tmp->next;
currNode = tmp;
}
return pCloneHead;
}
};
26.二叉搜索樹與雙向鏈表???
- 題目:輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。要求不能創建任何新的結點,只能調整樹中結點指針的指向。
-
知識點:雙向鏈表圖解
- 思路:
1.核心是中序遍歷的非遞歸算法。
2.修改當前遍歷節點與前一遍歷節點的指針指向。
class Solution {
public:
TreeNode* Convert(TreeNode* pRootOfTree)
{
if(pRootOfTree == nullptr) return nullptr;
TreeNode* pre = nullptr;
convertHelper(pRootOfTree, pre);
TreeNode* res = pRootOfTree;
while(res ->left)
res = res ->left;
return res;
}
void convertHelper(TreeNode* cur, TreeNode*& pre)
{
if(cur == nullptr) return;
convertHelper(cur ->left, pre);
cur ->left = pre;
if(pre) pre ->right = cur;
pre = cur;
convertHelper(cur ->right, pre);
}
};
27.字符串的排列
- 題目:輸入一個字符串,按字典序打印出該字符串中字符的所有排列。例如輸入字符串abc,則打印出由字符a,b,c所能排列出來的所有字符串abc,acb,bac,bca,cab和cba。
輸入描述:
輸入一個字符串,長度不超過9(可能有字符重復),字符只包括大小寫字母。
- 思路:
遞歸法,問題轉換為先固定第一個字符,求剩余字符的排列;求剩余字符排列時跟原問題一樣。
(1) 遍歷出所有可能出現在第一個位置的字符(即:依次將第一個字符同后面所有字符交換);
(2) 固定第一個字符,求后面字符的排列(即:在第1步的遍歷過程中,插入遞歸進行實現)。
需要注意的幾點:
(1) 先確定遞歸結束的條件,例如本題中可設begin == str.size() - 1;
(2) 形如 aba或 aa等特殊測試用例的情況,vector在進行push_back時是不考慮重復情況的,需要自行控制;
(3) 輸出的排列可能不是按字典順序排列的,可能導致無法完全通過測試用例,考慮輸出前排序,或者遞歸之后取消復位操作。
class Solution {
public:
vector<string> Permutation(string str) {
vector<string> result;
if(str.empty()) return result;
Permutation(str,result,0);
// 此時得到的result中排列并不是字典順序,可以單獨再排下序
sort(result.begin(),result.end());
return result;
}
void Permutation(string str,vector<string> &result,int begin)
{
if(begin == str.size()-1) // 遞歸結束條件:索引已經指向str最后一個元素時
{
if(find(result.begin(),result.end(),str) == result.end())
{
// 如果result中不存在str,才添加;避免aa和aa重復添加的情況
result.push_back(str);
}
}
else
{
// 第一次循環i與begin相等,相當于第一個位置自身交換,關鍵在于之后的循環,
// 之后i != begin,則會交換兩個不同位置上的字符,直到begin==str.size()-1,進行輸出;
for(int i=begin;i<str.size();++i)
{
swap(str[i],str[begin]);
Permutation(str,result,begin+1);
swap(str[i],str[begin]); // 復位,用以恢復之前字符串順序,達到第一位依次跟其他位交換的目的
}
}
}
void swap(char &fir,char &sec)
{
char temp = fir;
fir = sec;
sec = temp;
}
};
28.數組中出現次數超過一半的數字
- 題目:數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。例如輸入一個長度為9的數組{1,2,3,2,2,2,5,4,2}。由于數字2在數組中出現了5次,超過數組長度的一半,因此輸出2。如果不存在則輸出0。
- 思路:
數組排序后,如果符合條件的數存在,則一定是數組中間那個數。(比如:1,2,2,2,3;或2,2,2,3,4;或2,3,4,4,4等等)
這種方法雖然容易理解,但由于涉及到快排sort,其時間復雜度為O(NlogN)并非最優;
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
// 因為用到了sort,時間復雜度O(NlogN),并非最優
if(numbers.empty()) return 0;
sort(numbers.begin(),numbers.end()); // 排序,取數組中間那個數
int middle = numbers[numbers.size()/2];
int count=0; // 出現次數
for(int i=0;i<numbers.size();++i)
{
if(numbers[i]==middle) ++count;
}
return (count>numbers.size()/2) ? middle : 0;
}
};
29.最小的K個數
- 題目:輸入n個整數,找出其中最小的K個數。例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個數字是1,2,3,4,。
- 思路:
基于堆排序算法,構建最大堆。時間復雜度為O(nlogk)
如果用快速排序,時間復雜度為O(nlogn)
如果用冒泡排序,時間復雜度為O(n*k)
全排列 時間復雜度O(nlogn)
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> res;
if(input.empty()||k>input.size()) return res;
sort(input.begin(),input.end());
for(int i=0;i<k;i++)
res.push_back(input[i]);
return res;
}
};
30.連續子數組的最大和
- 題目:HZ偶爾會拿些專業問題來忽悠那些非計算機專業的同學。今天測試組開完會后,他又發話了:在古老的一維模式識別中,常常需要計算連續子向量的最大和,當向量全為正數的時候,問題很好解決。但是,如果向量中包含負數,是否應該包含某個負數,并期望旁邊的正數會彌補它呢?例如:{6,-3,-2,7,-15,1,2,2},連續子向量的最大和為8(從第0個開始,到第3個為止)。給一個數組,返回它的最大連續子序列的和,你會不會被他忽悠住?(子向量的長度至少是1)
- 思路:
首先要搞懂這題目的意思,連續子序列并不一定從第0個開始,因此最大子序列很有可能是沖中間開始計算
因此遍歷求和,遇到負和拋棄之前的結果,重新積累,期間保留最大值
class Solution {
public:
int FindGreatestSumOfSubArray(vector<int> array) {
if(array.empty())
return 0;
int cSum = 0;
int result = array[0]; // result存儲最大和,不能初始為0,存在負數
for(int i = 0;i<array.size();++i)
{
if(cSum < 0) // 當前和<0,拋棄不要
cSum = array[i];
else
cSum += array[i];
if(cSum > result) // 存儲最大結果
result = cSum;
}
return result;
}
};