剑指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[].size();//列数
  8. //计算打印的圈数
  9. int circle=((rows<columns?rows:columns)-)/+;//圈数
  10. for(int i=;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+;k<rows-i;k++)
  16. res.push_back(matrix[k][columns--i]);
  17. //判断是否会重复打印(从右向左的每行数据)
  18. for(int m=columns-i-;(m>=i)&&(rows-i-!=i);m--)
  19. res.push_back(matrix[rows-i-][m]);
  20. //判断是否会重复打印(从下往上的每一列数据)
  21. for(int n=rows-i-;(n>i)&&(columns-i-!=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=;
  9. for(int i=;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. };

5.从上往下打印二叉树从上往下打印出二叉树的每个节点,同层节点从左至右打印。

分析:
每一次打印一个结点的时候,如果该结点有子结点,则把该结点的子结点放入一个队列的末尾。
接下来到队列的头部去除最早进入队列的结点,重复前面的打印操作,直到队列中所有的结点都被打印出来为止。
  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. };

6.二叉搜索树的后序遍历序列输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。

分析:
在后序遍历得到的序列中,最后一个数字是树的根结点的值。
数组中前面的数字可以分为两部分:第一部分是左子树结点的值,他们都比根结点的值小,第二部分是右子树结点的值,他们的值都比根结点的值大。
//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 = ; // 标记左右子树界限
  9. int len = sequence.size();
  10. root = sequence[len-];
  11. int i=;
  12. for(;i<len-;++i)
  13. {
  14. if(sequence[i]>root)
  15. break; // 找到第一个大于根结点的位置,则左边为左子树,右边为右子树
  16. }
  17. for(int j=i;j<len-;++j) // 循环时去除root,因此为len-1
  18. {
  19. if(sequence[j]<root)
  20. return false; // 有一个小于root,则返回false
  21. }
  22.  
  23. if(i!=)
  24. {
  25. // 即有左子树
  26. for(int m=;m<i;++m)
  27. {
  28. leftTree.push_back(sequence[m]);
  29. }
  30. }
  31. if(i!=len-)
  32. {
  33. for(int j=i;j<len-;++j)
  34. {
  35. rightTree.push_back(sequence[j]);
  36. }
  37. }
  38.  
  39. bool left = true,right = true; // 看左右子树是否是二叉搜索树
  40. if(leftTree.size()>) VerifySquenceOfBST(leftTree);
  41. if(rightTree.size()>) 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 = ; //当前和
  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();
  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. };

10.字符串的排列

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串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 + );
  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() == )
  22. return st;
  23. fun(str, );
  24. set<string>::iterator it;
  25. for (it = res.begin(); it != res.end(); ++it)
  26. st.push_back(*it);
  27. return st;
  28. }
  29. };

剑指offer第四章的更多相关文章

  1. 剑指offer第七章&第八章

    剑指offer第七章&第八章 1.把字符串转换成整数 将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数. 数值为0或者字符串不是一个合法的数值则返回0 输入描述: 输入一个字符串 ...

  2. 剑指offer第六章

    剑指offer第六章 1.数字在排序数组中出现的次数 统计一个数字在排序数组中出现的次数.例如输入排序数组{1,2,3,3,3,3,4,5}和数字3,由于3在数组中出现了4次,所以输出4 分析:思路1 ...

  3. 剑指offer第五章

    剑指offer第五章 1.数组中出现次数超过一半的数 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字. 例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}.由于数字2在数组 ...

  4. 剑指offer第三章

    剑指offer第三章 1.数值的整数次方 给定一个double类型的浮点数base和int类型的整数exponent.求base的exponent次方. class Solution { public ...

  5. JS 剑指Offer(四) 从尾到头打印链表

    题目:输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回). 首先定义一下链表中的节点,关于链表这个数据结构在另外一篇文章中会详细讲 function ListNode(val) { t ...

  6. 《剑指Offer》第二章(一)题3-8

    为春招实习做准备,记录一下<剑指Offer>里面的面试题 第二章 面试题3:数组之中的重复数字. 这个题吧,虽然不难,但是不知道为什么就是看了很久,可能很久没有做算法题了.最后面一句话说的 ...

  7. 《剑指Offer》第二章(一)题 9 -12

    第二章 面试题9:用两个栈实现队列 题目:如面试题,给你两个栈, 实现队列的先进先出,即在队列头删除一个元素以及在队列的尾部添加一个元素 思路:这个题的分析感觉很巧妙,从一个具体的例子入手,找出其中的 ...

  8. 算法学习之剑指offer(四)

    题目1 题目描述 输入两棵二叉树A,B,判断B是不是A的子结构.(ps:我们约定空树不是任意一个树的子结构) /** public class TreeNode { int val = 0; Tree ...

  9. 剑指offer—第三章高质量代码(o(1)时间删除链表节点)

    题目:给定单向链表的头指针和一个节点指针,定义一个函数在O(1)时间删除该节点,链表节点与函数的定义如下:struct ListNode{int m_nValue;ListNode* m_pValue ...

随机推荐

  1. 面向对象-PHP面向对象的特性

     1.类和公有化 class Computer {     //什么叫做类内,就是创建类的花括号内的范围叫做类内,其他地方则类外.     //public 是对字段的公有化,这个字段类外即可访问,赋 ...

  2. 【程序员笔试面试必会——排序②】Python实现 计数排序、基数排序

    一.计数排序 概要: 时间复杂度O(n),空间复杂度O(k),k是输入序列的值的范围(最大值-最小值),是稳定的.计数排序一般用于已知输入值的范围相对较小,比如给公司员工的身高体重信息排序. 思路: ...

  3. dom&bom的起源,方法,内容,应用

    Document Object Model的历史可以追溯至1990年代后期微软与Netscape的"浏览器大战"(browser wars),双方为了在JavaScript与JSc ...

  4. HDU 4274 Spy's Work (树形DP)

    题意 给定一棵树,给出一些子树的权值关系,问是否矛盾(初始所有结点的下限为1) 思路 设lmin和lmax表示题目给定的限制范围,默认为[1..oo]:amin和amax表示实际符合要求的范围.从根节 ...

  5. Testing shell commands from Python

    如何测试shell命令?最近,我遇到了一些情况,我想运行shell命令进行测试,Python称为万能胶水语言,一些自动化测试都可以完成,目前手头的工作都是用python完成的.但是无法从Python中 ...

  6. 5G信令(就是用户身份信息)——手机开机后,先从USIM中读取之前运营商分配的临时身份信息GUTI/TMSI,发送携带该身份信息的信令给基站,请求接入运营商网络。

    5G时代,跟IMSI-CATCHER SAY GOODBYE from:https://unicorn.360.com/blog/2018/04/18/GoodBye_5G_IMSI-Catcher/ ...

  7. map、filter、reduce、lambda

    一.map.filter.reduce map(fuction , iterable) 映射 对可迭代对象中的每一项,使用函数去改变 filter(function, iterable) 过滤 可迭代 ...

  8. Sql sever 事务

    SQL事务 一.事务概念    事务是一种机制.是一种操作序列,它包含了一组数据库操作命令,这组命令要么全部执行,要么全部不执行.因此事务是一个不可分割的工作逻辑单元.在数据库系统上执行并发操作时事务 ...

  9. git重要命令

    body, table{font-family: 微软雅黑; font-size: 13.5pt} table{border-collapse: collapse; border: solid gra ...

  10. 《Effective C++》第4章 设计与声明(1)-读书笔记

    章节回顾: <Effective C++>第1章 让自己习惯C++-读书笔记 <Effective C++>第2章 构造/析构/赋值运算(1)-读书笔记 <Effecti ...