剑指offer第四章
剑指offer第四章
1.二叉树的镜像
二叉树的镜像:输入一个二叉树,输出它的镜像
分析:求树的镜像过程其实就是在遍历树的同时,交换非叶结点的左右子结点。
求镜像的过程:先前序遍历这棵树的每个结点,如果遍历到的结点有子结点,交换它的两个子结点,当交换完所有非叶子结点的左右子结点之后,就得到了树的镜像。
1 /*
2 struct TreeNode {
3 int val;
4 struct TreeNode *left;
5 struct TreeNode *right;
6 TreeNode(int x) :
7 val(x), left(NULL), right(NULL) {
8 }
9 };*/
10 class Solution {
11 public:
12 void Mirror(TreeNode *pRoot)
13 {
14 if(pRoot==NULL)//二叉树为空
15 return;
16 if(pRoot->left==NULL&&pRoot->right==NULL)//二叉树只有一个结点
17 return;
18
19 //交换左右结点
20 TreeNode *pTemp=pRoot->left;
21 pRoot->left=pRoot->right;
22 pRoot->right=pTemp;
23
24 if(pRoot->left) //递归求左子树的镜像
25 Mirror(pRoot->left);
26 if(pRoot->right)//递归求右子树的镜像
27 Mirror(pRoot->right);
28 }
29 };
2.顺时针打印矩阵
分析:
/*解题思路:顺时针打印就是按圈数循环打印,一圈包含两行或者两列,在打印的时候会出现某一圈中只包含一行,
要判断从左向右打印和从右向左打印的时候是否会出现重复打印,
同样只包含一列时,要判断从上向下打印和从下向上打印的时候是否会出现重复打印的情况*/
1 class Solution {
2 public:
3 vector<int> printMatrix(vector<vector<int> > matrix) {
4 vector<int>res;
5 res.clear();
6 int rows=matrix.size();//行数
7 int columns=matrix[0].size();//列数
8 //计算打印的圈数
9 int circle=((rows<columns?rows:columns)-1)/2+1;//圈数
10 for(int i=0;i<circle;i++){
11 //从左向右打印
12 for(int j=i;j<columns-i;j++)
13 res.push_back(matrix[i][j]);
14 //从上往下的每一列数据
15 for(int k=i+1;k<rows-i;k++)
16 res.push_back(matrix[k][columns-1-i]);
17 //判断是否会重复打印(从右向左的每行数据)
18 for(int m=columns-i-2;(m>=i)&&(rows-i-1!=i);m--)
19 res.push_back(matrix[rows-i-1][m]);
20 //判断是否会重复打印(从下往上的每一列数据)
21 for(int n=rows-i-2;(n>i)&&(columns-i-1!=i);n--)
22 res.push_back(matrix[n][i]);}
23 return res;
24 }
25 };
3.包含min函数的栈
定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。
1 /*
2 * 1.dataStack为存储数据的栈,minStack为存储最小值的栈;
3 * 2.push的时候将value值与minStack中的top值比较,小则minStack push value,大则push top值
4 */
5 class Solution {
6 public:
7 stack<int> dataStack, minStack;//定义两个栈,一个数据栈,一个最小值的辅助栈
8 void push(int value)
9 {
10 dataStack.push(value);//将数据进行压入数据栈
11 if (minStack.empty()) //如果辅助栈尾空,将数据压入辅助栈
12 {
13 minStack.push(value);
14 }
15 else//如果数据小于min,数据压入辅助栈,否则最小值压入辅助栈
16 {
17 int min = minStack.top();
18 value<=min?minStack.push(value):minStack.push(min);
19 }
20
21 }
22 void pop() //出栈
23 {
24 dataStack.pop();//数据栈出栈
25 minStack.pop();//辅助栈出栈
26 }
27 int top() //栈顶
28 {
29 return dataStack.top();
30 }
31 int min() //取最小
32 {
33 return minStack.top();
34 }
35 };
4、栈的压入、弹出序列
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
分析:
可以找到判断一个序列是不是栈的弹出序列的规律:如果下一个弹出的数字刚好是栈顶数字,那么直接弹出。
如果下一个弹出的数字不在栈顶,我们把压栈序列中还没有入栈的数字压入辅助栈,直到把下一个需要弹出的数字压入栈顶为止。
如果把所有的数字都压入栈仍然没有找到下一个弹出的数字,那么该序列不可能是一个弹出序列。
/*C++
模拟堆栈操作:将原数列依次压栈,栈顶元素与所给出栈队列相比,如果相同则出栈,
如果不同则继续压栈,直到原数列中所有数字压栈完毕。
检测栈中是否为空,若空,说明出栈队列可由原数列进行栈操作得到。否则,说明出栈队列不能由原数列进行栈操作得到。
*/
1 class Solution {
2 public:
3 bool IsPopOrder(vector<int> pushV,vector<int> popV)
4 {
5 if(pushV.empty() || popV.empty() || pushV.size()!=popV.size())//如果压入序列或者弹出序列为空、或者压入序列和弹出序列大小不等
6 return false;
7 stack<int> s;
8 int j=0;
9 for(int i=0;i<pushV.size();++i)
10 {
11 s.push(pushV[i]);
12 while(!s.empty()&&s.top()==popV[j])
13 {
14 s.pop();
15 ++j;
16 }
17 }
18 if(s.empty())
19 return true;
20 return false;
21 }
22 };
分析:
每一次打印一个结点的时候,如果该结点有子结点,则把该结点的子结点放入一个队列的末尾。
接下来到队列的头部去除最早进入队列的结点,重复前面的打印操作,直到队列中所有的结点都被打印出来为止。
1 class Solution {
2 public:
3 vector<int> PrintFromTopToBottom(TreeNode *root)
4 {
5 queue<TreeNode*> q;
6 q.push(root);
7 vector<int> r;
8 while(!q.empty())
9 {
10 root = q.front();
11 q.pop();
12 if(!root)
13 continue;
14 r.push_back(root -> val);
15 q.push(root -> left);
16 q.push(root -> right);
17 }
18 return r;
19 }
20 };
分析:
在后序遍历得到的序列中,最后一个数字是树的根结点的值。
数组中前面的数字可以分为两部分:第一部分是左子树结点的值,他们都比根结点的值小,第二部分是右子树结点的值,他们的值都比根结点的值大。
//1、确定root;
//2、遍历序列(除去root结点),找到第一个大于root的位置,则该位置左边为左子树,右边为右子树;
//3、遍历右子树,若发现有小于root的值,则直接返回false;
//4、分别判断左子树和右子树是否仍是二叉搜索树(即递归步骤1、2、3)。
1 class Solution {
2 public:
3 bool VerifySquenceOfBST(vector<int> sequence) {
4 vector<int> leftTree,rightTree;
5 int root; // 根结点
6 if(sequence.empty())
7 return false;
8 int index = 0; // 标记左右子树界限
9 int len = sequence.size();
10 root = sequence[len-1];
11 int i=0;
12 for(;i<len-1;++i)
13 {
14 if(sequence[i]>root)
15 break; // 找到第一个大于根结点的位置,则左边为左子树,右边为右子树
16 }
17 for(int j=i;j<len-1;++j) // 循环时去除root,因此为len-1
18 {
19 if(sequence[j]<root)
20 return false; // 有一个小于root,则返回false
21 }
22
23 if(i!=0)
24 {
25 // 即有左子树
26 for(int m=0;m<i;++m)
27 {
28 leftTree.push_back(sequence[m]);
29 }
30 }
31 if(i!=len-2)
32 {
33 for(int j=i;j<len-1;++j)
34 {
35 rightTree.push_back(sequence[j]);
36 }
37 }
38
39 bool left = true,right = true; // 看左右子树是否是二叉搜索树
40 if(leftTree.size()>1) VerifySquenceOfBST(leftTree);
41 if(rightTree.size()>1) VerifySquenceOfBST(rightTree);
42
43 return (left&&right);
44 }
45 };
7.二叉树中和为某一值的路径: 注:路径是从根结点出发到叶结点。
//非递归版本
//思路:
//1.按先序遍历把当前节点cur的左孩子依次入栈同时保存当前节点,每次更新当前路径的和sum;
//2.判断当前节点是否是叶子节点以及sum是否等于expectNumber,如果是,把当前路径放入结果中。
//3.遇到叶子节点cur更新为NULL,此时看栈顶元素,如果栈顶元素的把栈顶元素保存在last变量中,同时弹出栈顶元素,当期路径中栈顶元素弹出,
//sum减掉栈顶元素,这一步骤不更改cur的值;
//4.如果步骤3中的栈顶元素的右孩子存在且右孩子之前没有遍历过,当前节点cur更新为栈顶的右孩子,此时改变cur=NULL的情况。
1 /*
2 struct TreeNode {
3 int val;
4 struct TreeNode *left;
5 struct TreeNode *right;
6 TreeNode(int x) :
7 val(x), left(NULL), right(NULL) {
8 }
9 };*/
10 class Solution {
11 public:
12 vector<vector<int> > FindPath(TreeNode* root,int expectNumber)
13 {
14 vector<vector<int> > res;
15 if (root == NULL)
16 return res;
17 stack<TreeNode *> s;
18 s.push(root);
19 int sum = 0; //当前和
20 vector<int> curPath; //当前路径
21 TreeNode *cur = root; //当前节点
22 TreeNode *last = NULL; //保存上一个节点
23 while (!s.empty())
24 {
25 if (cur == NULL)
26 {
27 TreeNode *temp = s.top();
28 if (temp->right != NULL && temp->right != last)
29 {
30 cur = temp->right; //转向未遍历过的右子树
31 }
32 else
33 {
34 last = temp; //保存上一个已遍历的节点
35 s.pop();
36 curPath.pop_back(); //从当前路径删除
37 sum -= temp->val;
38 }
39 }
40 else
41 {
42 s.push(cur);
43 sum += cur->val;
44 curPath.push_back(cur->val);
45 if (cur->left == NULL && cur->right == NULL && sum == expectNumber)
46 {
47 res.push_back(curPath);
48 }
49 cur = cur->left; //先序遍历,左子树先于右子树
50 }
51 }
52 return res;
53
54 }
55 };
8.复杂链表的复制
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。
(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
1 /*
2 struct RandomListNode {
3 int label;
4 struct RandomListNode *next, *random;
5 RandomListNode(int x) :
6 label(x), next(NULL), random(NULL) {
7 }
8 };
9 */
10 class Solution {
11 public:
12 void CloneNodes(RandomListNode* pHead)
13 {
14 RandomListNode* pNode=pHead;
15 while(pNode!=NULL){
16 RandomListNode* pCloned=new RandomListNode(0);
17 pCloned->label=pNode->label;
18 pCloned->next=pNode->next;
19 pCloned->random=NULL;
20
21 pNode->next=pCloned;
22 pNode=pCloned->next;
23 }
24 }
25
26 void ConnectSiblingNodes(RandomListNode* pHead)
27 {
28 RandomListNode* pNode=pHead;
29 while(pNode!=NULL){
30 RandomListNode* pCloned=pNode->next;
31 if(pNode->random!=NULL)
32 {
33 pCloned->random=pNode->random->next;
34 }
35 pNode=pCloned->next;
36 }
37 }
38
39 RandomListNode* ReconnectNodes(RandomListNode* pHead)
40 {
41 RandomListNode* pNode=pHead;
42 RandomListNode* pClonedHead=NULL;
43 RandomListNode* pClonedNode=NULL;
44
45 if(pNode!=NULL)
46 {
47 pClonedHead=pClonedNode=pNode->next;
48 pNode->next=pClonedNode->next;
49 pNode=pNode->next;
50 }
51
52 while(pNode!=NULL)
53 {
54 pClonedNode->next=pNode->next;
55 pClonedNode=pClonedNode->next;
56 pNode->next=pClonedNode->next;
57 pNode=pNode->next;
58 }
59 return pClonedHead;
60 }
61 RandomListNode* Clone(RandomListNode* pHead)
62 {
63 CloneNodes(pHead);
64 ConnectSiblingNodes(pHead);
65 return ReconnectNodes(pHead);
66 }
67 };
9.二叉搜索树与双向链表
输入一颗二叉搜索树,将该二叉搜索树转换成一个排序的双向链表(要求不能创建新的结点,只能调整树中结点指针的指向)
分析:在搜索二叉树中,左子结点的值总是小于父节点的值,右子节点的值总是大于父节点的值。因此我们在转换成排序双向链表时,原先指向左子节点的指针调整为链表中指向前一个结点的指针,原先指向右子节点的指针调整为链表中指向后一个结点指针。
1 /*
2 struct TreeNode {
3 int val;
4 struct TreeNode *left;
5 struct TreeNode *right;
6 TreeNode(int x) :
7 val(x), left(NULL), right(NULL) {
8 }
9 };*/
10 class Solution {
11 public:
12 void ConvertNode(TreeNode* pNode,TreeNode** pLastNodeInList)
13 {
14 if(pNode==NULL)
15 return;
16 TreeNode *pCurrent=pNode;
17 if(pCurrent->left!=NULL)
18 ConvertNode(pCurrent->left,pLastNodeInList);
19 pCurrent->left=*pLastNodeInList;
20 if(*pLastNodeInList!=NULL)
21 (*pLastNodeInList)->right=pCurrent;
22 *pLastNodeInList=pCurrent;
23 if(pCurrent->right!=NULL)
24 ConvertNode(pCurrent->right,pLastNodeInList);
25 }
26 TreeNode* Convert(TreeNode* pRootOfTree)
27 {
28 TreeNode *pLastNodeInList=NULL;
29 ConvertNode(pRootOfTree,&pLastNodeInList);
30 TreeNode *pHeadOfList=pLastNodeInList;
31 while(pHeadOfList!=NULL&&pHeadOfList->left!=NULL)
32 pHeadOfList=pHeadOfList->left;
33 return pHeadOfList;
34 }
35 };
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述:输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
递归思路:分为两步,首先求所有可能出现在第一个位置的字符,即把第一个字符和后面所有字符交换。第二步固定第一个字符,求后面所有字符的排列。
仍然分为两部分:后面字符的第一个字符,以及这个字符之后的所有字符。然后把第一个字符逐一和它后面字符交换。
1 class Solution {
2 public:
3 set<string> res;
4 void fun(string str, int pos)
5 {
6 if (pos == str.length())
7 {
8 res.insert(str);
9 return;
10 }
11 for (int i = pos; i < str.length(); ++i)
12 {
13 swap(str[i], str[pos]);
14 fun(str, pos + 1);
15 swap(str[i], str[pos]);
16 }
17 }
18 vector<string> Permutation(string str) {
19 res.clear();
20 vector<string> st;
21 if (str.length() == 0)
22 return st;
23 fun(str, 0);
24 set<string>::iterator it;
25 for (it = res.begin(); it != res.end(); ++it)
26 st.push_back(*it);
27 return st;
28 }
29 };