58 对称二叉树 题目描述
请实现一个函数,用来判断一棵二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
示例1
输入:
返回值:
示例2
输入:
返回值:
思路:
判断左子树和右子树是否对称,相当对判断左子树的左孩子和右子树的右孩子是否相等,左子树的右孩子和右子树的左孩子是否相等。
采用队列存取每个结点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class Solution {public : bool isSymmetrical (TreeNode* pRoot) { queue<TreeNode*> l; queue<TreeNode*> r; if (!pRoot)return true ; l.push (pRoot->left); r.push (pRoot->right); while (!l.empty ()&&!r.empty ()){ TreeNode* tl=l.front (); TreeNode* tr=r.front (); if (!tl&&!tr); else if (tl&&tr){ if (tl->val!=tr->val)return false ; l.push (tl->left); l.push (tl->right); r.push (tr->right); r.push (tr->left); } else return false ; l.pop ();r.pop (); } return true ; } };
59 按之字形顺序打印二叉树 题目描述
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
示例1
输入:
返回值:
思路:
维护两个栈,一个保存从前向后的结点,一个保存从后向前的结点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 class Solution {public : vector<vector<int > > Print (TreeNode* pRoot) { stack<TreeNode*> st; stack<TreeNode*> st2; int flag=1 ; vector<vector<int >> res; if (!pRoot)return res; st.push (pRoot); while (!st.empty ()||!st2.empty ()){ int len=st.empty ()?st2.size ():st.size (); vector<int > rol; while (len>0 ){ TreeNode* t; if (flag==1 ){ t=st.top (); if (t->left)st2.push (t->left); if (t->right)st2.push (t->right); st.pop (); } else { t=st2.top (); if (t->right)st.push (t->right); if (t->left)st.push (t->left); st2.pop (); } rol.push_back (t->val); len--; } flag*=-1 ; res.push_back (rol); } return res; } };
60 把二叉树打印成多行 题目描述
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
示例1
输入:
返回值:
思路:
维护一个队列,广度遍历二叉树,遍历的时候要记住每层的结点数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class Solution {public : vector<vector<int > > Print (TreeNode* pRoot) { queue<TreeNode*> qu; vector<vector<int >> res; if (!pRoot)return res; qu.push (pRoot); while (!qu.empty ()){ TreeNode* t; int len=qu.size (); vector<int > rol; while (len>0 ){ t=qu.front (); qu.pop (); if (t->left)qu.push (t->left); if (t->right)qu.push (t->right); rol.push_back (t->val); len--; } res.push_back (rol); } return res; } };
61 序列化二叉树 题目描述
请实现两个函数,分别用来序列化和反序列化二叉树
二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 ! 表示一个结点值的结束(value!)。
二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。
例如,我们可以把一个只有根节点为1的二叉树序列化为”1,”,然后通过自己的函数来解析回这个二叉树
示例1
输入:
返回值:
思路:
先序遍历二叉树,将二叉树的结点转换成字符串,‘#’表示空结点,’!’分割每个结点。
反序列化时以’!’为界将字符串中的数字转化成int类型,并为其创建新结点,以先序遍历的顺序创建树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 class Solution {public : char * Serialize (TreeNode *root) { string s=ser (root); char * ch=new char [s.size ()]; strcpy (ch, s.c_str ()); return ch; } string ser (TreeNode* root) { if (!root)return "#" ; string s=to_string (root->val)+"!" ; s+=ser (root->left); s+=ser (root->right); return s; } TreeNode* Deserialize (char *str) { int i=0 ; return des (i,str); } TreeNode* des (int & i,char * s) { if (s[i]=='#' ){ i++; return nullptr ; } int num=0 ; while (s[i]!='!' ){ num=num*10 +(s[i]-'0' ); i++; } i++; TreeNode* root=new TreeNode (num); root->left=des (i,s); root->right=des (i,s); return root; } };
62 二叉搜索树的第k个结点 题目描述
给定一棵二叉搜索树,请找出其中的第k小的TreeNode结点
输入:
返回值:
思路:
中序遍历二叉搜索树,返回遇到的第k个结点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Solution {public :TreeNode* KthNode (TreeNode* pRoot, int k) { if (!pRoot)return nullptr ; int i = k; TreeNode* res=nullptr ; dfs (pRoot, i, res); return res; } void dfs (TreeNode* root, int & i, TreeNode*& res) { if (root->left)dfs (root->left, i, res); if (i <= 0 )return ; if (i == 1 )res = root; i--; if (root->right)dfs (root->right, i, res); } };
63 数据流中的中位数 题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
思路:
创建一个大顶堆一个小顶堆,
第单数个数放入大顶堆,第偶数个数放入小顶堆
放入大顶堆前先放入小顶堆排序,再将小顶堆的堆顶放入大顶堆,确保大顶堆的数全都小于小顶堆
放入小顶堆前先放入大顶堆排序,再将大顶堆的堆顶放入小顶堆,确保小顶堆的数全都大于大顶堆
数据流有单数个时大顶堆的堆顶就是中位数,偶数时取两个堆顶的和*0.5
c++中priority_queue意为优先队列,有三个参数,第一个是数据类型,第二个是存储容器,第三个是排序方法,默认用vector存储,大顶堆。设置greater参数就是小顶堆
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution {public : priority_queue<int > maxHeap; priority_queue<int ,vector<int >,greater<int >> minHeap; void Insert (int num) { if (maxHeap.size ()==minHeap.size ()){ minHeap.push (num); maxHeap.push (minHeap.top ()); minHeap.pop (); } else { maxHeap.push (num); minHeap.push (maxHeap.top ()); maxHeap.pop (); } } double GetMedian () { int lenMax=maxHeap.size (),lenMin=minHeap.size (); return (lenMax==lenMin)?(maxHeap.top ()+minHeap.top ())*0.5 :maxHeap.top (); } };
64 滑动窗口的最大值 题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
窗口大于数组长度的时候,返回空
示例1
输入:
返回值:
思路:
创建一个双端队列,保存数组的下标,当队首不在窗口内出队,每加入一个新元素就和队尾比较,确保队列是一个降序队列,队首元素是当前滑动窗口最大值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution {public : vector<int > maxInWindows (const vector<int >& num, unsigned int size) { deque<int > dqu; vector<int > res; if (num.size ()==0 ||size>num.size ()||size==0 )return res; for (int i=0 ;i<num.size ();i++){ while (!dqu.empty ()&&i-dqu.front ()+1 >size)dqu.pop_front (); while (!dqu.empty ()&&num[dqu.back ()]<num[i])dqu.pop_back (); dqu.push_back (i); if (i+1 >=size)res.push_back (num[dqu.front ()]); } return res; } };
65 矩阵中的路径 题目描述
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如 $$ \begin{matrix} a&b&c&e\ s&f&c&s\ a&d&e&e\ \end{matrix} $$ 矩阵中包含一条字符串”bcced”的路径,但是矩阵中不包含”abcb”路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
示例1
输入:
1 "ABCESFCSADEE",3,4,"ABCCED"
返回值:
思路:
用回溯法解决。在搜索中,遇到这条路不可能和目标字符串匹配成功的情况(例如:此矩阵元素和目标字符不同、此元素已被访问,超出矩阵范围),则应立即返回,称之为可行性剪枝 。
每一个元素都可能是起点,所以要遍历矩阵找出符合条件的字符。
深度搜索当前字符的前后左右,搜索过的元素标为空,防止重复搜索,若不匹配返回false
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution {public : bool res=false ; bool hasPath (string matrix, int rows, int cols, string str) { for (int i=0 ;i<rows;i++){ for (int j=0 ;j<cols;j++){ if (dfs (matrix,str,rows,cols,0 ,i,j))return true ;; } } return false ; } bool dfs (string matrix,string str,int rows, int cols,int pre,int i,int j) { if (i>=rows||i<0 ||j>=cols||j<0 ||matrix[j+i*cols]!=str[pre])return false ; if (pre==(str.size ()-1 ))return true ; matrix[j+i*cols]='\0' ; bool res=dfs (matrix,str,rows,cols,pre+1 ,i+1 ,j)||dfs (matrix,str,rows,cols,pre+1 ,i-1 ,j) ||dfs (matrix,str,rows,cols,pre+1 ,i,j+1 )||dfs (matrix,str,rows,cols,pre+1 ,i,j-1 ); return res; } };
66 机器人的运动范围 题目描述
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
示例1
输入:
返回值:
思路:
从第一个位置开始,深度搜索上下左右四个位置,判断当前格子是否符合条件,不符合返回0,且不往这个格子后搜索,相当于路被堵住了。符合则返回值+1
设置flag数组记录走过的格子,避免重复
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution {public : int row,col; int movingCount (int threshold, int rows, int cols) { int res=0 ; row=rows;col=cols; vector<vector<bool >> flag (rows,vector<bool >(cols,true )); res=Compare (threshold, 0 , 0 , flag); return res; } int Compare (int k,int i,int j,vector<vector<bool >>& flag) { if (i>=row||i<0 ||j>col||j<0 ||!flag[i][j])return 0 ; string s1=to_string (i); string s2=to_string (j); int num=0 ; for (auto c:s1)num+=(c-'0' ); for (auto c:s2)num+=(c-'0' ); flag[i][j]=false ; if (num>k)return 0 ; int res=Compare (k, i+1 , j, flag)+Compare (k, i-1 , j, flag) +Compare (k, i, j+1 , flag)+Compare (k, i+1 , j-1 , flag)+1 ; return res; } };
67 剪绳子 题目描述
给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1,m<=n),每段绳子的长度记为k[1],…,k[m]。请问k[1]x…xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
输入:
返回值:
思路:
利用动态规划的思想
我们想要求长度为n的绳子剪掉后的最大乘积,可以从前面比n小的绳子转移而来 用一个dp数组记录从0到n长度的绳子剪掉后的最大乘积,也就是dp[i]表示长度为i的绳子剪成m段后的最大乘积,初始化dp[2] = 1 我们先把绳子剪掉第一段(长度为j),如果只剪掉长度为1,对最后的乘积无任何增益,所以从长度为2开始剪 剪了第一段后,剩下(i - j)长度可以剪也可以不剪。如果不剪的话长度乘积即为j * (i - j);如果剪的话长度乘积即为j * dp[i - j]。取两者最大值max(j * (i - j), j * dp[i - j]) 第一段长度j可以取的区间为[2,i/2+1],大于i/2+1会剪重复,对所有j不同的情况取最大值,因此最终dp[i]的转移方程为 dp[i] = max(dp[i], max(j * (i - j), j * dp[i - j])) 最后返回dp[n]即可
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution {public : int cutRope (int number) { vector<int > dp (number+1 ) ; dp[2 ]=1 ; for (int i=3 ;i<=number;i++){ for (int j=2 ;j<=(i/2 )+1 ;j++){ dp[i]=max (dp[i],max (j*(i-j),j*dp[i-j])); } } return dp[number]; } };