劍指offer(21-40題)
P129
調整陣列順序使奇數位於偶數前面
void reOrderArray(vector<int> &array) { int n = array.size(); int left = 0, right = n-1; while(left < right){ while(left< right){ if((array[left]&1)==1) left++; else break; } while(right > left){ if((array[right]&1) ==0) right--; else break; } if(left < right){ int tmp = array[right]; array[right]= array[left]; array[left] = tmp; left++; right--; } } }
牛客網的題目有另一個約束:奇數和奇數,偶數和偶數之間的相對位置不變
https://www.nowcoder.com/practice/beb5aa231adc45b2a5dcc5b62c93f593?tpId=13&tqId=11166&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking
P134
連結串列中倒數第k個節點
此題要考慮清楚各種情況,程式碼的魯棒性要強
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) { if( pListHead == NULL || k == 0) return NULL; // k==0 要注意 ListNode * quickPoint = pListHead; for(int i=0; i< k-1; i++){ quickPoint = quickPoint->next; if(quickPoint == NULL) return NULL; } ListNode * slowPoint = pListHead; while(quickPoint->next){ slowPoint = slowPoint->next; quickPoint = quickPoint->next; } return slowPoint; }
P139
連結串列中環的入口節點
ListNode* EntryNodeOfLoop(ListNode* pHead) { if(pHead == NULL) return NULL; ListNode * quickPoint = pHead->next, * slowPoint = pHead->next; if(slowPoint==NULL) return NULL; quickPoint = quickPoint->next; if(quickPoint==NULL) return NULL; while(slowPoint!=quickPoint){ slowPoint = slowPoint->next; quickPoint = quickPoint->next; if(quickPoint==NULL) return NULL; quickPoint = quickPoint->next; if(quickPoint==NULL) return NULL; } ListNode * meet = slowPoint; slowPoint = slowPoint->next; int cnt = 1; while(slowPoint != meet){ cnt++; slowPoint = slowPoint->next; } quickPoint = pHead; slowPoint = pHead; while(cnt){ quickPoint = quickPoint->next; cnt--; } while(slowPoint != quickPoint){ slowPoint = slowPoint->next; quickPoint = quickPoint->next; } return slowPoint; }
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
if(pHead == NULL) return NULL;
ListNode * quickPoint = pHead->next, * slowPoint = pHead->next;
if(slowPoint==NULL) return NULL;
quickPoint = quickPoint->next;
while(quickPoint){
if(quickPoint == slowPoint) break;
slowPoint = slowPoint->next;
quickPoint = quickPoint->next;
if(quickPoint==NULL) return NULL;
quickPoint = quickPoint->next;
}
if(quickPoint == NULL) return NULL;
ListNode * meet = slowPoint;
slowPoint = slowPoint->next;
int cnt = 1;
while(slowPoint != meet){
cnt++;
slowPoint = slowPoint->next;
}
quickPoint = pHead;
slowPoint = pHead;
while(cnt){
quickPoint = quickPoint->next;
cnt--;
}
while(slowPoint != quickPoint){
slowPoint = slowPoint->next;
quickPoint = quickPoint->next;
}
return slowPoint;
}
P142
反轉連結串列
ListNode* ReverseList(ListNode* pHead) {
if(pHead == NULL) return NULL;
if(pHead->next == NULL) return pHead;
ListNode * pre = NULL, * cur = pHead, * tmp = NULL;
while(cur != NULL){
tmp = cur->next;
cur->next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
P145
合併兩個排序的連結串列
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if( pHead1 == NULL ) return pHead2;
if(pHead2 == NULL) return pHead1;
ListNode * emptyNode = new ListNode(0);
ListNode * pre = emptyNode;
ListNode * cur = NULL, * cur1 = pHead1, * cur2 = pHead2;
while(cur1 != NULL && cur2 != NULL){
if(cur1->val < cur2->val) {
cur = cur1;
cur1 = cur1->next;
}else {
cur = cur2;
cur2 = cur2->next;
}
pre->next = cur;
pre = cur;
}
if( cur1 ){
pre->next = cur1;
}
if(cur2 ){
pre->next = cur2;
}
return emptyNode->next;
}
遞迴方法
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if(pHead1 == NULL) return pHead2;
if(pHead2 == NULL) return pHead1;
if( pHead1->val < pHead2->val) {
pHead1->next = Merge(pHead1->next, pHead2);
return pHead1;
}else{
pHead2->next = Merge(pHead1, pHead2->next);
return pHead2;
}
}
P148
樹的子結構
對自己來說,難度有點大
class Solution {
public:
bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
{
bool result = false;
if(pRoot1!=NULL && pRoot2!=NULL){
if( abs(pRoot1->val - pRoot2->val)<1e-6){
result = isRight(pRoot1, pRoot2);
}
if(!result){
result = HasSubtree(pRoot1->left, pRoot2);
}
if(!result){
result = HasSubtree(pRoot1->right, pRoot2);
}
}
return result;
}
private:
bool isRight(TreeNode * pRoot1, TreeNode * pRoot2){
if(pRoot2 == NULL) return true;
if(pRoot1 == NULL) return false;
if( abs(pRoot1->val - pRoot2->val)<1e-6 ){
return isRight(pRoot1->left, pRoot2->left) && isRight(pRoot1->right, pRoot2->right);
}else
return false;
}
};
P157
二叉樹的映象
void Mirror(TreeNode *pRoot) {
if(pRoot == NULL) return ;
if(pRoot->left == NULL && pRoot->right==NULL) return ;
TreeNode * tmp = pRoot->left;
pRoot->left = pRoot->right;
pRoot->right = tmp;
Mirror(pRoot->left);
Mirror(pRoot->right);
}
非遞迴
void Mirror(TreeNode *pRoot) {
if(pRoot == NULL) return ;
queue<TreeNode *> q;
q.push(pRoot);
TreeNode * tmp = NULL;
while(q.size()){
tmp = q.front();
q.pop();
swap(tmp->left, tmp->right);
if(tmp->left) q.push(tmp->left);
if(tmp->right) q.push(tmp->right);
}
}
P159
對稱的二叉樹
對自己來說,有點難度
class Solution {
public:
bool isSymmetrical(TreeNode* pRoot)
{
return isRight(pRoot, pRoot);
}
private:
bool isRight(TreeNode * r1, TreeNode * r2){
if(r1 == NULL && r2 == NULL ) return true;
if(r1 == NULL || r2==NULL) return false;
if( r1->val!= r2->val ) return false;
return isRight(r1->left, r2->right) && isRight(r1->right, r2->left);
}
};
P161
順時針列印矩陣
https://www.nowcoder.com/profile/6606749/codeBookDetail?submissionId=15814779
vector<int> printMatrix(vector<vector<int> > matrix) {
vector<int> res;
int row = matrix.size();
if(row == 0) return res;
int col = matrix[0].size();
vector<vector<int>> flag(row, vector<int>(col,0));
int i=0, j=0;
int all = row*col;
int cnt=0;
while(cnt<all){
while(j<col && flag[i][j]==0){
res.push_back(matrix[i][j]);
flag[i][j]=1;
cnt++;
j++;
}
j--;
i++;
while(i<row && flag[i][j]==0){
res.push_back(matrix[i][j]);
flag[i][j]=1;
cnt++;
i++;
}
i--;
j--;
while(j>=0 && flag[i][j]==0){
res.push_back(matrix[i][j]);
flag[i][j]=1;
cnt++;
j--;
}
j++;
i--;
while(i>=0 && flag[i][j]==0){
res.push_back(matrix[i][j]);
flag[i][j]=1;
cnt++;
i--;
}
i++;
j++;
}
return res;
}
class Solution {
public:
vector<int> printMatrix(vector<vector<int> > matrix) {
vector<int> res;
int row = matrix.size();
if(row ==0 )return res;
int col = matrix[0].size();
if(col ==0) return res;
int left=0, right=col-1, top=0, bottom= row-1;
while(left<=right && top<=bottom){
for(int i=left;i<=right;i++){
res.push_back(matrix[top][i]);
}
for(int i=top+1; i<= bottom;i++){
res.push_back(matrix[i][right]);
}
if(top < bottom){
for(int i= right-1; i>=left;i--)
res.push_back(matrix[bottom][i]);
}
if(left < right){
for(int i= bottom-1; i>top;i--)
res.push_back(matrix[i][left]);
}
left++;
top++;
right--;
bottom--;
}
return res;
}
};
P165
包含min函式的棧
class Solution {
public:
void push(int value) {
s.push(value);
if( minStack.empty()) minStack.push(value);
else if( minStack.top() < value)
minStack.push(minStack.top());
else
minStack.push(value);
}
void pop() {
if(!s.empty()){
s.pop();
minStack.pop();
}
}
int top() { // 個人覺得在top時也要判斷是否空,但是無法在牛客上ac
return s.top();
}
int min() {
return minStack.top();
}
private:
stack<int> s;
stack<int> minStack;
};
P168
棧的壓入、彈出序列
https://www.nowcoder.com/profile/8709341/codeBookDetail?submissionId=17099336
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
int len1 = pushV.size(), len2 = popV.size();
int start = 0;
for(int i=0; i< len2; i++){
if(!s.empty() && s.top()== popV[i])
s.pop();
else{
while(start<len1 && pushV[start]!=popV[i]){
s.push(pushV[start]);
start++;
}
if( start >= len1) return false;
else
start++;
}
}
if(s.empty())
return true;
else return false;
}
private:
stack<int> s;
};
P171
從上到下列印二叉樹
以下程式碼是不分行列印二叉樹
vector<int> PrintFromTopToBottom(TreeNode* root) {
vector<int> res;
if(root == NULL) return res;
queue<TreeNode *> q;
q.push(root);
TreeNode * tmp= NULL;
while(!q.empty()){
tmp = q.front();
q.pop();
res.push_back(tmp->val);
if(tmp->left) q.push(tmp->left);
if(tmp->right) q.push(tmp->right);
}
return res;
}
以下程式碼是分行列印二叉樹
vector<vector<int> > Print(TreeNode* pRoot) {
vector<vector<int>> res;
if(!pRoot) return res;
queue<TreeNode *> q;
q.push(pRoot);
TreeNode * tmp = NULL;
int k = 0;
while(!q.empty()){
k = q.size();
vector<int> v;
for(int i=0; i< k; i++){
tmp = q.front();
q.pop();
v.push_back(tmp->val);
if(tmp->left) q.push(tmp->left);
if(tmp->right) q.push(tmp->right);
}
res.push_back(v);
}
return res;
}
之字形列印二叉樹
方法一:仍用佇列
vector<vector<int> > Print(TreeNode* pRoot) {
vector<vector<int>> res;
if(!pRoot) return res;
queue<TreeNode *> q;
q.push(pRoot);
int k = 0;
TreeNode * tmp = NULL;
int flag=0;
while(!q.empty()){
flag = 1- flag;
k = q.size();
vector<int> v;
for(int i=0;i<k;i++){
tmp = q.front();
q.pop();
v.push_back(tmp->val);
if(tmp->left) q.push(tmp->left);
if(tmp->right) q.push(tmp->right);
}
if(flag) res.push_back(v);
else{
reverse(v.begin(),v.end());
res.push_back(v);
}
}
return res;
}
方法二:使用兩個棧
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
vector<vector<int>> res;
if(!pRoot) return res;
s1.push(pRoot);
TreeNode * tmp = NULL;
while(!s1.empty() || !s2.empty()){
vector<int> v;
if(!s1.empty()){
while(!s1.empty()){
tmp = s1.top();
s1.pop();
v.push_back(tmp->val);
if(tmp->left) s2.push(tmp->left);
if(tmp->right) s2.push(tmp->right);
}
}else{
while(!s2.empty()){
tmp = s2.top();
s2.pop();
v.push_back(tmp->val);
if(tmp->right) s1.push(tmp->right);
if(tmp->left) s1.push(tmp->left);
}
}
res.push_back(v);
}
return res;
}
private:
stack<TreeNode *> s1;
stack<TreeNode *> s2;
};
P180
二叉搜尋樹的後序遍歷序列
此題,對我還是有點難度
class Solution {
public:
bool VerifySquenceOfBST(vector<int> sequence) {
int n = sequence.size();
if(n==1) return true;
if(n==0) return false;
return rec(sequence, 0, n-1);
}
bool rec(vector<int> & sequence, int start, int end){
if(start >= end) return true;
int root = sequence[end];
int k=start;
while(k < end ){
if(sequence[k] > root)
break;
k++;
}
for(int i=k; i< end; i++){
if(sequence[i]<root)
return false;
}
return rec(sequence, start, k-1) && rec(sequence, k, end-1 );
}
};
P182
二叉樹中和為某一值的路徑
class Solution {
public:
vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
vector<vector<int>> res;
if(!root) return res;
vector<int> v;
rec(root, expectNumber, res, v);
return res;
}
private:
void rec( TreeNode * root, int num, vector<vector<int>> & res, vector<int> & v){
v.push_back(root->val);
num = num - root->val;
if(root->left==NULL && root->right==NULL && num==0 ) {
res.push_back(v);
}
if(root->left){
rec(root->left, num, res, v);
}
if(root->right){
rec(root->right, num, res,v);
}
v.pop_back();
}
};
P187
複雜連結串列的複製
此題值得思考的點還是挺多的,書中指明瞭三種方法。
class Solution {
public:
RandomListNode* Clone(RandomListNode* pHead)
{ if(pHead == NULL) return NULL;
// step 1: 複製節點
RandomListNode * cur = pHead;
RandomListNode * tmp = NULL;
while(cur){
RandomListNode * newNode = new RandomListNode(cur->label);
tmp = cur->next;
cur->next = newNode;
newNode->next = tmp;
cur = tmp;
}
// step 2: 連線兄弟節點
cur = pHead;
while(cur){
if(cur->random){
cur->next->random = cur->random->next;
}
cur = cur->next->next;
}
// step 3: 拆分連結串列
cur = pHead;
RandomListNode * res = cur->next;
while(cur){
tmp = cur->next;
cur->next = tmp->next;
if(cur->next == NULL) break; // 這一行要注意加上
tmp->next = cur->next->next;
cur = cur->next;
}
return res;
}
};
P191
二叉搜尋樹與雙向連結串列
對二叉搜尋樹中序遍歷得到的結果就是從小到大
還是對遞迴掌握不好
class Solution {
public:
TreeNode* Convert(TreeNode* pRootOfTree)
{
if(pRootOfTree == NULL) return NULL;
TreeNode * pre = NULL;
inOrder(pRootOfTree, pre);
while(pre->left)
pre = pre->left;
return pre;
}
void inOrder( TreeNode * cur, TreeNode * & pre){
if(cur == NULL) return ;
inOrder(cur->left, pre);
cur->left = pre;
if(pre) pre->right = cur;
pre = cur;
inOrder(cur->right, pre);
}
};
P195
序列化二叉樹
https://blog.csdn.net/u011475210/article/details/78889876
個人更傾向字串的方法,因為樹的val的值可正可負
class Solution {
public:
// 本質上就是樹的先根遍歷
void serializeHelper(TreeNode * cur, string & s){
if(cur==NULL) {
s += "$,";
return ;
}else{
s += to_string(cur->val);
s += ",";
serializeHelper(cur->left,s);
serializeHelper(cur->right,s);
}
}
char* Serialize(TreeNode *root) {
if(!root) return NULL;
string s = "";
serializeHelper(root, s);
char * res = new char[s.length()+1];
strcpy(res, s.c_str());
return res;
}
TreeNode * deserializeHelper(string & s){
if(s[0]=='$'){
s = s.substr(2);
return NULL;
}
TreeNode * res = new TreeNode(stoi(s));
s = s.substr(s.find_first_of(',')+1);
res->left = deserializeHelper(s);
res->right = deserializeHelper(s);
return res;
}
TreeNode* Deserialize(char *str) {
if(!str) return NULL;
string s = str;
return deserializeHelper(s);
}
};
P197
字串的排列
下面的連線很好,裡面是多個排列題目
https://blog.csdn.net/sinat_33442459/article/details/73732486
牛客網要求按字典序列印,下面通過set做到,同時也考慮了字元可能重複。
class Solution {
public:
vector<string> permutation;
set<string> permutationSet;
void p(string s, int index, int n){
if(index == n-1) {
permutationSet.insert(s);
return ;
}
p(s, index+1,n);
for(int i = index; i < n ;i++){
if(s[i]!=s[index]){
char tmp = s[index];
s[index] = s[i];
s[i] = tmp;
p(s, index+1, n);
tmp = s[index];
s[index] = s[i];
s[i] = tmp;
}
}
}
vector<string> Permutation(string str) {
if(str.length()==0) return permutation;
int n = str.length();
p(str, 0, n);
for(set<string> :: iterator iter = permutationSet.begin(); iter != permutationSet.end(); ++iter ){
permutation.push_back(*iter);
}
return permutation;
}
};
P205
陣列中出現次數超過一半的數字
這個題很重要
https://blog.csdn.net/liangzhaoyang1/article/details/51049237
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
map<int,int> m;
int n = numbers.size();
for(int i=0;i<n;i++)
m[numbers[i]]++;
map<int,int>::iterator iter = m.begin();
while(iter!=m.end()){
if(iter->second > n/2)
return iter->first;
iter++;
}
return 0;
}
};
利用了partition
class Solution {
public:
void partition(vector<int> & numbers, int target, int start, int end){
if(start>=end) return;
int val;
int left = start, right =end;
val = numbers[left];
while(left<right){
while(right>left && numbers[right]>=val)
right--;
if(right > left){
numbers[left] = numbers[right];
left++;
}
while(left<right && numbers[left]<val)
left++;
if(left < right){
numbers[right] = numbers[left];
right--;
}
}
numbers[left] = val;
if(left == target/2) return;
else if( target/2 > left)
partition(numbers, target, left+1,end );
else
partition(numbers, target, start, left-1);
}
int MoreThanHalfNum_Solution(vector<int> numbers) {
int n = numbers.size();
if(n==0) return 0;
int index = n/2;
partition(numbers, index,0,n-1);
int val = numbers[index];
int cnt = 0;
for(int i=0;i<n;i++){
if(numbers[i]==val)
cnt++;
}
if(cnt>index)
return val;
else
return 0;
}
};
P209
最小的k個數
方法一:
時間複雜度O(n log n), 同時原陣列也進行了改動
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> res;
int n = input.size();
if(n<k || k<=0) return res;
sort(input.begin(), input.end());
for(int i=0;i<k;i++)
res.push_back(input[i]);
return res;
}
};
方法二:
時間複雜度O(n), 用上一題“陣列中出現次數超過一半的數字”的方法,基於partition函式定位到第k大的數字,那麼位於左邊的就是我們要的結果,注意:得到的這k個數字不一定有序
此外,此方法也修改了輸入的陣列
可見,快排的思想還是很重要的,很多地方都有用到
class Solution {
public:
int partition(vector<int> & input, int left, int right ){
int val = input[left];
while(left < right){
while(right > left && input[right]>=val)
right--;
if(right > left){
input[left] = input[right];
left++;
}
while(left < right && input[left]<val)
left++;
if(left < right){
input[right]=input[left];
right--;
}
}
input[left] = val;
return left;
}
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> res;
int n = input.size();
if(n<k || k<=0) return res;
if(n == k) return input;
int start = 0, end = n-1;
int index = partition(input, start, end);
while(index!=k){
if(index <k ){
start = index + 1;
index = partition(input, start,end );
}else{
end = index -1;
index = partition(input, start, end);
}
}
for(int i=0;i<k;i++)
res.push_back(input[i]);
return res;
}
};
方法三:
時間複雜度O(n log k), `特別適合處理海量資料!!!
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> res;
int len = input.size();
if(len < k || k<=0 ) return res;
multiset<int, greater<int> > leastNumbers;
multiset<int, greater<int>>::iterator iterGreater;
for(int i=0;i<len;i++){
if(leastNumbers.size()<k)
leastNumbers.insert(input[i]);
else{
iterGreater = leastNumbers.begin();
if(input[i] < *iterGreater){
leastNumbers.erase(iterGreater);
leastNumbers.insert(input[i]);
}
}
}
for(iterGreater=leastNumbers.begin();iterGreater!=leastNumbers.end();iterGreater++)
res.push_back(*iterGreater);
return res;
}
};