重刷剑指offer

mac2022-06-30  19

数组中重复的数字

题目描述

在一个长度为 n 的数组里的所有数字都在 0 到 n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字是重复的,也不知道每个数字重复几次。请找出数组中任意一个重复的数字。

思路:

数组大小为n,数字范围为0~n-1,也就是说如果数组不重复,则排序后每个数组i上的元素就应该是i

为此,我们可以模拟这个排序的过程,如果i位置上的元素不为i,则把它与nums[i]位置的元素进行交换,这相当于把nums[i]放到他该去的地方。如果nums[i]位置的元素已经是i了,则这刚好就是重复的数字;否则将交换到i位置上的数字继续这个过程,如果交换过来的数等于i,则说明这个数放在了他应该放的地方,继续处理i+1上的数,如果仍然不等,则将这个换来的数放到他应该放的地方....最后,对于i位置来说,要么找到了值为i的元素,要么找到了重复的元素,是不可能出现无限循环的,因为无限循环要求换来的数字与i位置的数字相等,但这时候已经判定重复并跳出返回了。

class Solution { public: // Parameters: // numbers: an array of integers // length: the length of array numbers // duplication: (Output) the duplicated number in the array number // Return value: true if the input is valid, and there are some duplications in the array number // otherwise false bool duplicate(int numbers[], int length, int* duplication) { if(length<=1) return false; int p1=0; while(p1<length) { if(numbers[p1]!=p1) { while(numbers[p1]!=p1) { int right=numbers[numbers[p1]]; if(numbers[p1]==right) { *duplication=right; return true; } swap(numbers[numbers[p1]],numbers[p1]); } } p1++; } return false; } void swap(int& a,int& b) { int tmp=a; a=b; b=tmp; } };

  

二维数组中的查找

题目描述

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。 思路: 一开始想用两次二分,这实际上是不可行的 比如: 7 [1,2,8,9] [2,4,9,12] [4,7,10,13] [6,8,11,15] 实际上7并不是出现在第四行 问题出现在不能保证比数组头元素大的元素一定出现在这一行,不能保证上一行的某个元素一定会大于这一行的头元素,因为只能保证头元素大于上一行的头元素。对上一行的头元素来说相当于有两个递增方向。只有每行的头都大于上一行的尾,也就是严格单调递增时,可以这样写    实际上只要找到只有一个方向递增,一个方向递减的方向,从这个位置开始搜索,即相当于遍历一个有序数组。这个位置在左下角或右上角。左下角向右递增,向上递减;右上角向左递减,向下递增。 class Solution { public: bool Find(int target, vector<vector<int> > array) { auto& nums=array; if(nums.empty()||nums[0].empty()) return false; int row=nums.size()-1; int col=0; while(row>=0&&col<nums[row].size()) { if(nums[row][col]==target) return true; else if(nums[row][col]<target) col++; else row--; } return false; } };

  

替换空格

题目描述

请实现一个函数,将一个字符串中的每个空格替换成“ ”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We Are Happy。 思路: 最优解当然是不适用空间复杂度 首先计算拓展后的空间大小,然后用2个指针指向2个尾,逐个进行复制。 唯一值得注意的是,由于传进来的是str,因此尾部要取'\0'的位置,因为也要把'\0'也拷进去。 因此2个尾部分别是:

int pLast=len+count*2; int pCur=len;

而不是

int pLast=len+count*2-1; int pCur=len-1;

虽然这一种也能实现,但少了个'\0',需要自己在补一个。

class Solution { public: void replaceSpace(char *str,int length) { int len=length; int count=0; for(int i=0;i<len;i++) { if(str[i]==' ') count++; } int pLast=len+count*2; int pCur=len; while(pCur>=0&&pLast>pCur) { if(str[pCur]!=' ') { str[pLast]=str[pCur]; pLast--; } else { str[pLast--]='0'; str[pLast--]='2'; str[pLast--]='%'; } pCur--; } return ; } };

  

从尾到头打印链表

题目描述

输入一个链表,按链表从尾到头的顺序返回一个ArrayList。 思路: 最简单的就是push进vector后reverse,如果不用这种方式,递归也可以 class Solution { public: vector<int> printListFromTailToHead(ListNode* head) { vector<int> ans; if(!head) return ans; Core(ans,head); return ans; } void Core(vector<int>& in,ListNode* head) { if(!head) return; Core(in,head->next); in.push_back(head->val); } };

  

重建二叉树★★★

思路很清晰的题,但容易出错,多写几次注意下细节,特别是传参长度的处理。

题目描述

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。 要唯一的确定一个二叉树,必须知道两种遍历方式,且其中必须要有一个中序遍历,因为前序与后序只能分割出父与子的关系,而无法分割出左右子树的关系。   思路: 根据前序的第一个数,找到中序中的那个数的位置,然后将其分割为左右子树,相当于说前序提供父子关系,中序提供左右子树关系。 然后递归这个过程。 值得注意的是Core中的传参,其实只要前序的起点,中序的起点与长度就可以了,因为前序与中序的长度必须一致。如果每次传入前序的起点与终点,中序的起点与终点,则太复杂,也太麻烦。 class Solution { public: TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) { if(pre.empty()||pre.size()!=vin.size()) return NULL; return reConstructCore(pre,vin,0,0,pre.size()); } TreeNode* reConstructCore(vector<int>& pre,vector<int>& vin,int pstart,int vstart,int len) { if(len<=0) return NULL; int tmp=pre[pstart]; int pos=0; for(;pos<len&&vin[pos+vstart]!=tmp;pos++); pos+=vstart; int left=pos-vstart; int right=len-left-1; TreeNode* root=new TreeNode(tmp); root->left=reConstructCore(pre,vin,pstart+1,vstart,left); root->right=reConstructCore(pre,vin,pstart+left+1,pos+1,right); return root; } };

  

二叉树的下一个结点★★★

要考虑的情况很多,值得好好看看,体味下二叉树的结构

题目描述

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。 思路:  其实就是分为多种情况: 1).当前节点是父亲节点的左子树时: 此时要遍历该节点的右子树: 如果右子树存在,则找到右子树根节点的最左边的一个节点 如果右子树不存在,以此点为根的树已经遍历完了,此时由于它是父节点的左子树,因此下一个应该遍历父节点 2).当前节点时父亲节点的右子树时: 此时要遍历该节点的右子树: 如果右子树存在,则找到右子树根节点的最左边的一个节点 如果右子树不存在,则不仅以此节点为根的的子树已经遍历完了,由于是父节点的右子树,相当于父节点也遍历完了;如果父节点是祖父节点的右子树,则相当于祖父节点也已经遍历完了。。。。。 因此要找到祖先节点中的某一个节点,该节点要么父亲节点时空,要么父亲节点的左子树的根就是这个节点。 当为选中的祖先节点的父节点为空,相当于已经遍历完了所有节点,返回NULL 如果非空,则相当于已经遍历完了该祖先节点父节点的左子树,此时应该返回该祖先的父亲节点。   class Solution { public: TreeLinkNode* GetNext(TreeLinkNode* pNode) { if(!pNode) return NULL; return GetNextCore(pNode); } TreeLinkNode* GetNextCore(TreeLinkNode* node) { if(!node->next) { auto it=node->right; if(!it) return NULL; while(it->left) it=it->left; return it; } auto pLast=node->next; if(pLast->left==node) { if(node->right) { auto right=node->right; while(right->left) right=right->left; return right; } else return pLast; } else if(pLast->right==node) { if(node->right) { auto right=node->right; while(right->left) right=right->left; return right; } else { while(pLast->next&&pLast->next->right==pLast) pLast=pLast->next; if(pLast->next==NULL) return NULL; else return pLast->next; } } else return NULL; } };

  这个分类讨论太多了,其实完全可以简化:

1).如果该节点存在右子树,则应该遍历右子树中的最左节点

2).如果不存在,则要找到某个节点,该节点是其父的左子节点(包括目前的这个节点)

这样就简化很多了

class Solution { public: TreeLinkNode* GetNext(TreeLinkNode* pNode) { if(!pNode) return NULL; auto right=pNode->right; if(right) { while(right->left) right=right->left; return right; } else { while(pNode->next&&pNode->next->right==pNode) pNode=pNode->next; if(pNode->next) return pNode->next; else return NULL; } } };

用两个栈实现队列

题目描述

用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

思路:

栈的特点是先入后出,队列的特点是先入先出

现在给了两个栈,其实可以想象成这样一个情景:

Tail和Head相当于两个水管,水从tail中流入,从Head中流出,这样实际上就相当于实现了队列

我们在Tail中灌水,就相当于push进数,然后pop提出最先灌进去的数,则从Head中读出。

 

 

 

 如果Head现在是空,也就是说没有元素,则把Tail里的数全部转到Head中,当然顺序要变化,这也就相当于上图里用个管子把两个容器相连,然后把Tail底部的水抽到Head中

每次push都push到Tail中。

class Solution { public: void push(int node) { stack2.push(node); } int pop() { if(!stack1.empty()) { auto ans=stack1.top(); stack1.pop(); return ans; } else { if(stack2.empty()) return INT_MIN; while(!stack2.empty()) { stack1.push(stack2.top()); stack2.pop(); } auto ans=stack1.top(); stack1.pop(); return ans; } } private: stack<int> stack1;//head stack<int> stack2;//tail };

  

用两个队列实现栈

两个队列:

某个队列pop到只剩一个元素,其他的元素PUSH到另一个队列。

一个队列:

push进后,将新元素前面的元素重新pop并push进队列。

矩形覆盖

题目描述我们可以用 2*1 的小矩形横着或者竖着去覆盖更大的矩形。请问用 n 个 2*1 的小矩形无重叠地覆盖一个 2*n 的大矩形,总共有多少种方法?

思路:

矩形可以横着摆与竖着摆,竖着摆时只能组成2x1;而横着摆可以用2个组成一个2X2

因此2*n的矩形可以来自于:2*(n-1)的矩形加一个2X1,或来自于2*(n-2)的矩形加两个横着摆的2x1

class Solution { public: int rectCover(int number) { if(number<=0) return 0; if(number<2) return number; vector<int> dp(number,0); dp[0]=1; dp[1]=2; for(int i=2;i<dp.size();i++) dp[i]=dp[i-1]+dp[i-2]; return dp.back(); } };

  

旋转数组的最小数字★★★★★

题目描述

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 思路: 将数组分为两部分,左部分为旋转点右侧的所有元素,右部分为包括旋转点在内的旋转点前的所有元素。 使用二分查找的方法,当mid位于左部分时,l=mid+1;当位于右部分时,h=mid( 不减1是因为如果mid刚好指向旋转点,则减1则使h调到了左部分) 现在还有一个问题是如何判定mid位于左部分还是右部分,如果nums[mid]>nums[h],则一定位于左部分;如果nums[mid]<nums[h],则一定位于右部分.如果nums[mid]=nums[h],则说不好。 因为如果是11101111,当0左与右都有1则无法判定到底是哪个部分 此时对h--,因为h--相当于缩减l与h的窗口,使重新计算的mid向左部分偏移,这个过程持续到直到nums[mid]>nums[h]或l=h(此时相当于l与h间都是同一种数字) class Solution { public: int minNumberInRotateArray(vector<int> rotateArray) { auto& nums=rotateArray; if(nums.size()==0) return 0; int l=0;int h=nums.size()-1; int mid=(l+h)/2; while(l<h) { mid=(l+h)/2; if(nums[mid]>nums[h]) l=mid+1; else if(nums[mid]<nums[h]) h=mid; else h--; } return nums[h]; } };

  

剪绳子★★★

题目描述把一根绳子剪成多段,并且使得每段的长度乘积最大。

思路:

切割绳子时,可以将它看做切成两半,然后每半部分再决定切不切

比如10,可以先切成4,6,由于已经切了一次了,因此4和6均可以选择继续切或者不切,这样就有四种情况,取其中最大值即可

class Solution { public: int integerBreak(int n) { vector<int> dp(n+1,0); dp[0]=1; dp[1]=1; for(int i=2;i<dp.size();i++) { for(int j=1;j<=i-j;j++) dp[i]=max(dp[i],max(dp[j],j)*max((i-j),dp[i-j])); } return dp.back(); } };

  

二进制中 1 的个数

题目描述

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。 思路: 1).用flag循环左移 由于负数的原因,用n右移会出现问题,因此对1个初值为1的数不断左移相与  class Solution { public: int NumberOf1(int n) { int num=sizeof(n)*8; int flag=1; int count=0; while(flag) { if((flag&n)==flag) count++; flag=flag<<1; } return count; } };

  

2).n&(n-1)会使n最右边一个1变为0

这是因为,当n最右边的数为1时,与n-1相与可以让n的最后一位从1变为0

如果n最右边一位为0,则n-1会使n最优边1个1变为0,而是这个1右侧的0全变为1,再与n相与,则那个1及它右侧全变为0.

这样n&(n-1)将不断使右边的1变为0,直到n==0;

class Solution { public: int NumberOf1(int n) { int count=0; while(n) { count++; n&=n-1; } return count; } };

  

数值的整数次方★★★

题目描述给定一个 double 类型的浮点数 base 和 int 类型的整数 exponent,求 base 的 exponent 次方。

思路:

如果每次只乘base则速度太慢,因此考虑每次进行平方,如果剩余的指数不足以平方,则递归剩余的指数,重复这个过程。

注意exponent为负数的情况,要先判断,最后用1.0去除

class Solution { public: double Power(double base, int exponent) { if(!base) return 0; if(!exponent) return 1; bool flag=false; if(exponent<0) flag=true;//判断exponent为负数的情况 exponent=abs(exponent); auto ans=PowerCore(base,exponent,base); if(flag) ans=1.0/ans; return ans; } double PowerCore(double num,int exponent,double base) { if(!exponent) return 1; if(exponent==1) return exponent*base; int count=2; while(count<=exponent) { num*=num; exponent-=count; count*=2; } return num*PowerCore(num,exponent,base); } };

  

打印从 1 到最大的 n 位数

题目描述输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数即999。

思路:

模拟加法过程,当最低位满足10进1时,通过while一直进位。

pCur用于跟踪最高位。

class Solution { public: void display(int n) { if(!n) return; if(n==1) { for(int i=0;i<10;i++) cout<<i<<endl; return; } string str(n+1,'0'); int pCur=str.size()-1; while(str[0]!='1') { cout<<str.data()+pCur<<endl; if(str.back()=='9') { int p=str.size()-1; while(str[p]=='9') { str[p]='0'; p--; } if(pCur-p==1) pCur=p; str[p]+=1; } else str.back()+=1; } return; } };

  

删除链表中重复的结点★★★

题目描述

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5 思路: pCur指向的是无重复链表的最后一个节点,pre为pCur后一个节点,通过next找到第一个不与pre相等的节点,如果pre与next相邻,则说明无重复,否则令pCur指向next. 注意第一个元素就开始重复的情况,因此创建一个头节点。 class Solution { public: ListNode* deleteDuplication(ListNode* pHead) { if(!pHead) return NULL; if(!pHead->next) return pHead; ListNode* head=new ListNode(100); head->next=pHead; ListNode* pCur=head; ListNode* pre=head->next; while(pre) { ListNode* next=pre->next; while(next&&next->val==pre->val) next=next->next; if(pre->next==next) { pCur=pre; pre=next; } else { pCur->next=next; pre=next; } } return head->next; } };

  

正则表达式匹配★★★★★

题目描述请实现一个函数用来匹配包括 '.' 和 '*' 的正则表达式。模式中的字符 '.' 表示任意一个字符,而 '*' 表示它前面的字符可以出现任意次(包含 0 次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串 "aaa" 与模式 "a.a" 和 "ab*ac*a" 匹配,但是与"aa.a" 和 "ab*a" 均不匹配。

思路:

dp[i][j]表示主串0~i与模式串0~j是否匹配。

状态转移方程为:

1).s[i]==p[j]||[j]=='.':dp[i][j]=dp[i-1][j-1],即只要前i-1与前j-1相等,则匹配

2).s[i]!=p[j]:

a).p[j]!='*':此时p[j]与s[i]完全不等,则dp[i][j]=false;

b).p[j]='*':此时*可以使前面的字母为0个,1个或多个

当p[j-1]!=s[i]时,此时只能使他为0:dp[i][j]=dp[i][j-1];

当p[j-1]==s[i]时,此时可以使前面的字母为0个,1个或多个

为0个时:dp[i][j]=dp[i][j-1]

为1个时:dp[i][j]=dp[i-1][j-1]

为多个时:此时拿出一个前面的字母与s[i]匹配,因此主串还剩i-1个,但由于p[j]='*'且此时是会产生多个前面字母的情况,因此即使已经分出一个了,但还可能分出很多个,因此还是拿s的前i-1个与p的前j个来匹配,因为p[j]='*',相当于他可以无限的拿出来。

举个例子:

s:###bbb

p:###b*

现在*与b不等,我们令*产生多个b的情况,此时先产生一个b与s的b消除:

s:###bb

p:###b*

而此时p串仍然为###b*,这时在令*产生一个b:

s:###b

p:###b*

此时再令*使前面的b为0个:

s:###

p:###

这样就匹配了

相当于说*产生多个p让他分步一步步来,而不是一下生产2个b

因此此时dp[i][j]=dp[i-1][j]

也就是说先拿出一个b与主串的消掉,然后将问题转换为dp[i-1][j],如果剩下的i-1还有b,但这已经是dp[i-1][j]的问题了。

另外注意初始状态:

dp[0][i],当p[i]='*'时,此时应该与dp[0][i-2]一致,也就是隐去i前面的一个

比如

s:""

p".*"

此时*将.隐去,而相当于dp[0][0]=true

class Solution { public: bool match(char* str, char* pattern) { string str1(str); string str2(pattern); vector<vector<bool>> dp(str1.size()+1,vector<bool>(str2.size()+1,false)); dp[0][0]=true; if(str2[0]=='*') return false; for(int i=0;i<str2.size();i++) { if(str2[i]=='*') dp[0][i+1]=dp[0][i-1]; } for(int i=1;i<dp.size();i++) { for(int j=1;j<dp[i].size();j++) { if((str1[i-1]==str2[j-1])||(str2[j-1]=='.')) dp[i][j]=dp[i-1][j-1]; else if(str2[j-1]=='*') { if(j==1) return false; if(str2[j-2]==str1[i-1]||str2[j-2]=='.') dp[i][j]=dp[i][j-2]||dp[i-1][j-1]||dp[i-1][j]; else dp[i][j]=dp[i][j-2]; } } } return dp.back().back(); } };

  

表示数值的字符串

题目描述

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。

 思路:

以e/E为分界线将整个数分为两部分:

左部分的数字部分可以为整数或者小数,+-只能出现在0,只能有一个'.'

右部分的数字部分只能是整数,+-只能出现在e后面一个位置,不能有'.'

class Solution { public: bool isNumeric(char* string) { char * str=string; int p1=0; while(p1<strlen(str)&&str[p1]!='e'&&str[p1]!='E') p1++; return isLeft(str,p1-1)&&isRight(str,p1+1); } bool isLeft(char* str,int end) { if(end<0) return false; int pointnum=0; for(int i=0;i<=end;i++) { if((str[i]=='+'||str[i]=='-')) { if(i!=0) return false; continue; } if(str[i]=='.') { pointnum++; if(pointnum==2) return false; continue; } if(str[i]<'0'||str[i]>'9') return false; } return true; } bool isRight(char* str,int start) { if(start==strlen(str)) return false; if(start>strlen(str)) return true; int len=strlen(str); for(int i=start;i<len;i++) { if((str[i]=='+'||str[i]=='-')) { if(i!=start) return false; continue; } if(str[i]<'0'||str[i]>'9') return false; } return true; } };

  

调整数组顺序使奇数位于偶数前面★★★

题目描述

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。 思路: 每当找到一个奇数,就使用类似于冒泡的思想,将它放到前面。 while来找到一个位置j,这个位置的前面j-1是奇数,此时这个j就是插入位置. class Solution { public: void reOrderArray(vector<int> &array) { if(array.empty()) return; vector<int>& nums=array; for(int i=0;i<nums.size();i++) { int target=nums[i]; if(target&0x1) { int j=i; while(j>0&&!(array[j-1]&0x1))//当j==0或j前面是奇数时停止 { array[j]=array[j-1]; j--; } array[j]=target; } } return; } };

  

链表中倒数第k个结点

题目描述

输入一个链表,输出该链表中倒数第k个结点 思路: 倒数第k个节点,比如倒数第一个节点,也就是最后一个节点,其领先NULL一个身位;倒数第二个领先2个身位。。。因此实际上只要先找到一个节点,其领先起点k个身位,则当这个节点走到NULL时,起点的那个指针也走到了倒数第k个节点的位置 class Solution { public: ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) { ListNode* pNext=pListHead; if(k<=0||!pListHead) return NULL; while(pNext&&k) { pNext=pNext->next; k--; } if(k!=0) return NULL; ListNode* pCur=pListHead; while(pNext) { pCur=pCur->next; pNext=pNext->next; } return pCur; } };

  

链表中环的入口结点

题目描述

给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。 思路: 首先找到一个环内的节点,如果找不到,return NULL; 其次如果找到环内的节点了:假设这个链表由两部分组成,一部分为环,另一部分为一个链。 设环的周长为r,链的长为l,由于环会回到起点,也就是有l=l+r。因此如果让一个节点先走r的长度,则当他们相遇时,先走的节点走了l+r,而后走的节点走了l,这个l刚好就是环的入口 此外注意下slow-fast指针的写法。 class Solution { public: ListNode* EntryNodeOfLoop(ListNode* pHead) { if(!pHead) return NULL; ListNode* p1=pHead; ListNode* p2=pHead; while(p2&&p2->next) { p1=p1->next; p2=p2->next->next; if(p1==p2) break; } if(!p2||!p2->next) return NULL; p1=pHead; auto tmp=p2; do{ tmp=tmp->next; p1=p1->next; }while(tmp!=p2); auto pCur=pHead; while(pCur!=p1) { pCur=pCur->next; p1=p1->next; } return pCur; } };

  

反转链表

题目描述

输入一个链表,反转链表后,输出新链表的表头。 思路: 两种方法: 最简单的: 头插法,不多说了。 其次: 使用三个指针pre,pCur,pNext;pre保存上一个节点,pCur保存当前节点,pNext保存下一个节点。 每次循环让pCur的next指向pre,然后更新pre为pCur,更新pCur为pNext; 头插法: class Solution { public: ListNode* ReverseList(ListNode* pHead) { if(!pHead) return NULL; ListNode* pCur=new ListNode(250); while(pHead) { auto tmp=pHead->next; pHead->next=pCur->next; pCur->next=pHead; pHead=tmp; } return pCur->next; } }; 三指针: class Solution { public: ListNode* ReverseList(ListNode* pHead) { if(!pHead) return NULL; if(!pHead->next) return pHead; ListNode* pre=NULL; ListNode* pCur=pHead; ListNode* pNext=pHead->next; while(pNext) { pNext=pCur->next; pCur->next=pre; pre=pCur; pCur=pNext; } return pre; } };

  

合并两个排序的链表

题目描述

输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。 思路: 小的加入新链表。 最后如果有链表还有剩则补进新链表 class Solution { public: ListNode* Merge(ListNode* pHead1, ListNode* pHead2) { if(!pHead1) return pHead2; if(!pHead2) return pHead1; ListNode* pHead=new ListNode(-1); ListNode* pCur=pHead; while(pHead1&&pHead2) { if(pHead1->val<pHead2->val) { auto tmp=pHead1->next; pCur->next=pHead1; pHead1->next=NULL; pHead1=tmp; } else { auto tmp=pHead2->next; pCur->next=pHead2; pHead2->next=NULL; pHead2=tmp; } pCur=pCur->next; } if(pHead1) pCur->next=pHead1; if(pHead2) pCur->next=pHead2; return pHead->next; } };

  

树的子结构★★★

题目描述

输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构) 思路: 递归 class Solution { public: bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2) { if(!pRoot2||!pRoot1) return false; return HasSubtreeCore(pRoot1,pRoot2); } bool HasSubtreeCore(TreeNode* p1,TreeNode* p2) { if(!p1&&p2) return false; if(!p2) return true; if(p1->val==p2->val &&HasSubtreeCore(p1->left,p2->left)//根相等且左右子树都是子结构 &&HasSubtreeCore(p1->right,p2->right)) return true; return HasSubtreeCore(p1->left,p2)||HasSubtreeCore(p1->right,p2); } };

  

二叉树的镜像

题目描述

操作给定的二叉树,将其变换为源二叉树的镜像。 思路: 。。。。 class Solution { public: void Mirror(TreeNode *pRoot) { if(!pRoot) return; MirrorCore(pRoot); } void MirrorCore(TreeNode* root) { if(!root) return; MirrorCore(root->left); MirrorCore(root->right); auto tmp=root->right; root->right=root->left; root->left=tmp; return; } };

  

对称的二叉树★★★

题目描述

请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。 思路: isSymCore用来判断两个树是否对称相等,我们用这个来递归判断根的左子树与右子树。 递归中首先判断根是否相等,如果相等则判断这两个树的左与右和右与左的对称情况 class Solution { public: bool isSymmetrical(TreeNode* pRoot) { if(!pRoot) return true; return isSymCore(pRoot->left,pRoot->right); } bool isSymCore(TreeNode* p1,TreeNode* p2) { if(!p1&&!p2) return true; if(p1&&!p2||p2&&!p1) return false; if(p1->val!=p2->val) return false; return isSymCore(p1->left,p2->right)&&isSymCore(p1->right,p2->left); } };

  

顺时针打印矩阵★★★★★

题目描述

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字 思路: 一开始想的太贪,想用一下模拟所有情况,实际上只剩一行或只剩一列的情况是要特殊考虑的。 r_num为当前轮次的行数,c_num为当前轮次的列数 class Solution { public: vector<int> printMatrix(vector<vector<int> > matrix) { if(matrix.empty()||matrix[0].empty()) return vector<int> (); int circle=0; int r_num=matrix.size(); int c_num=matrix[0].size(); vector<int> ans; while(r_num>0&&c_num>0) { if(r_num==1) { for(int i=0;i<c_num;i++) ans.push_back(matrix[circle][circle+i]); } else if(c_num==1) { for(int i=0;i<r_num;i++) ans.push_back(matrix[circle+i][circle]); } else { for(int i=0;i<c_num-1;i++) ans.push_back(matrix[circle][circle+i]); for(int i=0;i<r_num-1;i++) ans.push_back(matrix[circle+i][circle+c_num-1]); for(int i=0;i<c_num-1;i++) ans.push_back(matrix[circle+r_num-1][circle+c_num-1-i]); for(int i=0;i<r_num-1;i++) ans.push_back(matrix[circle+r_num-1-i][circle]); } circle++; r_num-=2; c_num-=2; } return ans; } };

  

包含min函数的栈★★★

题目描述

定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。 思路: 由于栈会push与pop,这都会导致栈内元素出现变化:push可能push进一个更小的数,pop可能pop出最小的数。 因此不能只存当前最小的数,因为他可能被pop出去。 准备一个栈aux用于存放可能成为最小的数的数,当当前push进来的数小于aux栈顶元素时,这个元素将成为最小元素,因此要将他存进aux;而原来的栈顶元素,由于对新元素pop后原来的栈顶元素又将成为最小,因此要留下。 如果大于栈顶元素,则它不可能成为当前栈内的最小元素,且即使pop出去也不会影响最小元素,因此不入aux. 由于aux与min栈不是完全相等的,因此如果要判断min pop出去的元素是不是aux的栈顶元素,只能在aux中存放索引而不是值。 class Solution { public: void push(int value) { nums.push_back(value); if(nums.size()==1) { aux.push(0); return; } auto tmp=aux.top(); if(value<nums[tmp]) aux.push(nums.size()-1); } void pop() { if(nums.empty()) return; auto idx=nums.size()-1; nums.pop_back(); if(idx==aux.top()) aux.pop(); } int top() { if(nums.empty()) return INT_MIN; return nums.back(); } int min() { if(nums.empty()) return INT_MIN; return nums[aux.top()]; } private: vector<int> nums; stack<int> aux; };

  

栈的压入、弹出序列★★

 题目描述

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。 思路: 模拟压入与弹出的过程,用一个栈aux,push到栈顶等于popV中的某个元素。如果无法达到则return false。 然后aux.pop;p2++。相当于找下一个出栈的元素。 如果最后栈内空,说明可以实行,return true. class Solution { public: bool IsPopOrder(vector<int> pushV,vector<int> popV) { if(pushV.size()!=popV.size()) return false; if(pushV.empty()) return true; int p1=0;int p2=0; stack<int> aux; while(p2<popV.size()) { while(p1<popV.size()&&(aux.empty()||aux.top()!=popV[p2]))//1 2 3 4 5 ;4 5 3 2 1 { aux.push(pushV[p1]); p1++; } if(p1>=pushV.size()&&aux.top()!=popV[p2]) { return false; } aux.pop(); p2++; } if(aux.empty()) return true; else return false; } };

  

从上往下打印二叉树

题目描述

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

思路:

层次遍历。。。

class Solution { public: vector<int> PrintFromTopToBottom(TreeNode* root) { if(!root) return vector<int>(); queue<TreeNode*> aux; aux.push(root); vector<int> ans; while(!aux.empty()) { ans.push_back(aux.front()->val); auto tmp=aux.front(); aux.pop(); if(tmp->left) aux.push(tmp->left); if(tmp->right) aux.push(tmp->right); } return ans; } };

  

把二叉树打印成多行

题目描述

从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。 思路:  count记录每一层的节点个数。tmp记录下一层的节点个数,当这一层的pop完毕后,count=tmp,即进入下一层。 class Solution { public: vector<vector<int> > Print(TreeNode* pRoot) { if(!pRoot) return vector<vector<int>>(); queue<TreeNode*> aux; int count=1; aux.push(pRoot); vector<vector<int>> ans; while(!aux.empty()) { int tmp=0; vector<int> in; while(count) { in.push_back(aux.front()->val); if(aux.front()->left) { aux.push(aux.front()->left); tmp++; } if(aux.front()->right) { aux.push(aux.front()->right); tmp++; } aux.pop(); count--; } count=tmp; ans.push_back(in); } return ans; } };

  

按之字形顺序打印二叉树★★★★

题目描述

请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。 思路: 分解成多个问题: 1.层次遍历 2.要记录每一层的个数 3.要求之字形打印 首先层次遍历想到用队列,其次要分层打印,因此还要一个count记录当前层数量。 最后由于要之字型打印: 也就是说如果这一层从左到右,则下一层则要从右到左,此时可以考虑,在push子节点时,先push进一个栈,这样再从栈里push进队列时,就是从右到左了 当要求下一层从右到左时,应该先push左,在push右,这样栈里才会是右->左 而当要求下一层从左到右时,应该先push右,在push左,这样栈里才会是左->右 因此还要加入一个flag,用于不同情况下的左右的顺序 class Solution { public: vector<vector<int> > Print(TreeNode* pRoot) { vector<vector<int>> ans; if(!pRoot) return ans; int count=1; queue<TreeNode*> aux; stack<TreeNode*> carry; aux.push(pRoot); int flag=1; while(!aux.empty()) { int tmp=0; vector<int> val; while(count) { val.push_back(aux.front()->val); auto first=(flag==1)?aux.front()->left:aux.front()->right; auto second=(flag==1)?aux.front()->right:aux.front()->left; if(first) { carry.push(first); tmp++; } if(second) { carry.push(second); tmp++; } count--; aux.pop(); } while(!carry.empty()) { aux.push(carry.top()); carry.pop(); } flag=-flag; count=tmp; ans.push_back(val); } return ans; } };

  

二叉搜索树的后序遍历序列★★★★

题目描述

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。 思路: 首先,后序遍历代表根节点在尾部。 其次,二叉搜索树代表左右子树可以根据根节点的大小分开。 要确定是否为后序遍历,有两个地方: 1.两个子树也要是二叉搜索树 2.尾部的节点要比左子树中所有树大,比右子树中所有树小。 因此: 首先找到尾部节点,根据尾部节点的值把左子树分割出来,然后看看剩余的数里有没有比根节点小的,如果有则说明不是二叉搜索树。 然后递归左子树与右子树,如果都是二叉搜索树的后序遍历,则return true. class Solution { public: bool VerifySquenceOfBST(vector<int> sequence) { if(sequence.empty()) return false; return VerifyBSTCore(sequence,0,sequence.size()); } bool VerifyBSTCore(vector<int>& nums,int start,int len) { if(len<=1) return true; int val=nums[start+len-1]; int i=0; for(;i<len&&nums[start+i]<val;i++); int left=i; for(;i<len&&nums[start+i]>=val;i++); if(i!=len) return false; return VerifyBSTCore(nums,start,left)&&VerifyBSTCore(nums,left,len-left-1); } };

  

转载于:https://www.cnblogs.com/lxy-xf/p/11516404.html

最新回复(0)