找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
位图法:
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
int * bitmap;
int index,bit,tmp;
int size=(100000/32+1)*sizeof(int);
bitmap= (int*) malloc(size);
memset(bitmap,0,size);
for(auto e: nums){
index=e/32;
bit=e-index*32;
tmp=bitmap[index]>>bit;
tmp=tmp & 1;
if (tmp){
free(bitmap);
return e;
}else{
bitmap[index]|=1<<bit;
}
}
free(bitmap);
return 0;
}
};
就地法:
遍历nums
假设nums[i]=tmp
如果nums[tmp]=tmp,返回tmp
否则交换nums[tmp]和 nums[i]
剑指 Offer 04. 二维数组中的查找
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
示例:
现有矩阵 matrix 如下:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
给定 target = 20,返回 false。
思路: 从左下角开始,若小于target,列数加1,反之行数减一。
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
int row=matrix.size();
if (row==0){
return false;
}
int col=matrix[0].size();
int i=row-1;
int j=0;
while(true){
if(i>=0 &&j<=col-1){
if(matrix[i][j]>target){
i-=1;
}else if(matrix[i][j]<target){
j+=1;
}else{
return true;
}
}else{
return false;
}
}
}
};
青蛙跳台阶问题
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
示例 1:
输入:n = 2
输出:2
思路 : 在循环内部取余,而不是在return 取余
class Solution {
public:
int numWays(int n) {
if(n == 0) return 1;//没有台阶,直接在上面,一种
if(n <= 2) return n;//一个或两个台阶
int a = 1, b = 2, c = 0;//a保存f(n-2),b保存f(n-1),c保存f(n)
for(int i = 3; i <= n; ++i)//i:第i个台阶
{
c = (a + b) % 1000000007;//第i个台阶跳法:a:i-2个台阶跳发,b:i-1个台阶跳发
a = b;//a保存b
b = c;//b保存c
}
return c;
}
};
给定一个二维网格和一个单词,找出该单词是否存在于网格中。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例:
board =
[
[‘A’,‘B’,‘C’,‘E’],
[‘S’,‘F’,‘C’,‘S’],
[‘A’,‘D’,‘E’,‘E’]
]
给定 word = “ABCCED”, 返回 true
给定 word = “SEE”, 返回 true
给定 word = “ABCB”, 返回 false
class Solution {
public:
bool exist(vector<vector<char>>& board, string &word) {
int row=board.size();
int col=board[0].size();
vector<char> tmp;
for (int r=0;r<row;r++){
for (int c=0;c<col;c++){
if(dfs(r,c,tmp, board,word)){
return true;
}
}
}
return false;
}
bool dfs(int i,int j, vector<char> &tmp,vector<vector<char>>& board,string &word){
int len=tmp.size();
int row=board.size();
int col=board[0].size();
if (i==row || j==col || i==-1 || j==-1 ||board[i][j]!=word[len]|| board[i][j]==' '){
return false;
}
if (len==(word.size()-1)){
return true;
}
tmp.push_back(board[i][j]);
board[i][j]=' ';
bool ret=dfs(i,j+1,tmp,board,word) || dfs(i+1,j,tmp,board,word) || dfs(i,j-1,tmp,board,word)||dfs(i-1,j,tmp,board,word) ;
board[i][j]=tmp.back();
tmp.pop_back();
return ret;
}
};
无重叠区间
给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。
注意:
可以认为区间的终点总是大于它的起点。
区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。
示例 1:
输入: [ [1,2], [2,3], [3,4], [1,3] ]
输出: 1
解释: 移除 [1,3] 后,剩下的区间没有重叠。
- 自己的解法(没用排序,超时)
struct node{
node *left;
node *right;
vector<int> *value;
}*root;
class Solution {
public:
int max_num=-100000;
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
if (intervals.size()==0){
return 0;
}
vector<int> root_vector(2,100000);
root=new node{NULL,NULL,&root_vector};
int path=0;
for (int i=0;i<intervals.size();i++){
dfs(intervals,i,path);
}
return intervals.size()-max_num;
}
// (*(cur.value))[0];
void dfs(vector<vector<int>>& intervals,int start, int& path){
int start_left=intervals[start][0];
int start_right=intervals[start][1];
int cur_left;
int cur_right;
node* new_node;
node* cur=root;
node* pre=cur;
int flag=0;
while(cur){
cur_left=(*(cur->value))[0];
cur_right=(*(cur->value))[1];
if(start_left>=cur_right){
pre=cur;
cur=cur->right;
flag=2;
}
else if (start_right<=cur_left){
pre=cur;
cur=cur->left;
flag=1;
}
else{
flag=0;
break;
}
}
if(flag>0){
path++;
if(path>max_num){
max_num=path;
}
if(flag==1){
new_node=new node{NULL,NULL,&(intervals[start])};
pre->left=new_node;
}else{
new_node=new node{NULL,NULL,&(intervals[start])};
pre->right=new_node;
}
for(int i=start+1;i<intervals.size();i++){
dfs(intervals,i,path);
}
path--;
if(flag==1){
pre->left=NULL;
}else{
pre->right=NULL;
}
delete new_node;
return;
}
return;
}
};
思路: 采用回溯法,对于当前的子集建立一颗排序树(区间),如果新的区间能插入就加入子集,求出子集的最大元素个数。
-
动态规划,先按照左或者右端点排序,然后状态转移:以j区间结尾的最大区间数是以1到j-1结尾的最大区间数当中,最大值加1,并且不发生重叠。
-
贪心法,按照右节点排序,每次都从剩下的区间中选右端点最小且不发生重叠的。
正则表达式匹配
请实现一个函数用来匹配包含’. ‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但与"aa.a"和"ab*a"均不匹配。
思路模拟nfa,并且用两个栈来保存新当前到达状态,和下一轮到达状态。
struct State{
bool isEnd;
map<char,int> digitTrans;
vector<int> emptyTrans;
};
class Solution {
public:
vector<State*> stateVector;
void buildState(string p){
// cout<<"ori"<<stateVector.size()<<endl;
int pre=0;
State* startState=new State;
startState->isEnd=false;
stateVector.push_back(startState);
for(int i=0;i<p.size();i++){
char digit=p[i];
if(i+1<p.size() && p[i+1]=='*'){
State* e1=new State;
e1->isEnd=false;
State* e2=new State;
e2->isEnd=false;
State* e3=new State;
e3->isEnd=false;
stateVector.push_back(e1);
stateVector.push_back(e2);
stateVector.push_back(e3);
stateVector[pre]->emptyTrans.push_back(pre+1);
stateVector[pre]->emptyTrans.push_back(pre+3);
e1->digitTrans[digit]=pre+2;
e2->emptyTrans.push_back(pre+1);
e2->emptyTrans.push_back(pre+3);
pre=pre+3;
// cout<<"digit"<<digit<<endl;
}else if(p[i]!='*'){
State* e1=new State;
e1->isEnd=false;
stateVector.push_back(e1);
stateVector[pre]->digitTrans[digit]=pre+1;
pre=pre+1;
}
}
State* e1=new State;
e1->isEnd=true;
stateVector.push_back(e1);
stateVector[pre]->emptyTrans.push_back(pre+1);
return;
}
void addState(vector<int>& alreadyOn, stack<int>& oldState, stack<int>& newState, int stateIndex){
alreadyOn[stateIndex]=1;
State* s=stateVector[stateIndex];
newState.push(stateIndex);
for(int index : s->emptyTrans){
if(alreadyOn[index]!=1){
addState(alreadyOn,oldState,newState,index);
}
}
}
bool isMatch(string s, string p) {
buildState(p);
stack<int> oldState;
stack<int> newState;
vector<int> alreadyOn(100,0);
addState(alreadyOn,oldState,newState,0);
// while(!newState.empty()){
// int s=newState.top();
// cout<<s<<endl;
// newState.pop();
// }
// cout<<"newsize"<<newState.size()<<endl;
bool flag=false;
for(int i=0;i<s.size();i++){
char c=s[i];
while(!newState.empty()){
int s=newState.top();
newState.pop();
alreadyOn[s]=0;
oldState.push(s);
}
// cout<<"oldsize"<<oldState.size()<<endl;
while(!oldState.empty()){
int s=oldState.top();
map<char,int> digitTrans=stateVector[s]->digitTrans;
// map<char, int>::iterator iter = digitTrans.begin();
// cout<<iter->first<<endl;
if(digitTrans.find('.')!=digitTrans.end()){
int t=digitTrans['.'];
if(alreadyOn[t]!=1){
addState(alreadyOn,oldState,newState,t);
}
}else{
if(digitTrans.find(c)!=digitTrans.end()){
cout<<c<<endl;
int t=digitTrans[c];
if(alreadyOn[t]!=1){
addState(alreadyOn,oldState,newState,t);
}
}
}
oldState.pop();
}
// cout<<"newsize"<<newState.size()<<endl;
}
while(!newState.empty()){
int s=newState.top();
// cout<<"final"<<s<<endl;
if(stateVector[s]->isEnd){
flag=true;
}
newState.pop();
}
for(auto state : stateVector){
delete state;
}
return flag;
}
};:
剑指 Offer 20. 表示数值的字符串
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100"、“5e2”、"-123"、“3.1416”、"-1E-16"、“0123"都表示数值,但"12e”、“1a3.14”、“1.2.3”、"±5"及"12e+5.4"都不是。
思路: 小数点左边或者右边不能都为空,所以有小数点左边不为空和为空两种分支。数字中间不能有空格,两边可以有空格,所以把空格当成输入,初始节点和终止节点能接受空格。
struct State{
bool isEnd;
map<char,int> digitTrans;
vector<int> emptyTrans;
};
class Solution {
public:
vector<State*> stateVector;
void buildState(){
State* startState=new State;
startState->isEnd=false;
stateVector.push_back(startState);
State* e1=new State;
e1->isEnd=false;
stateVector.push_back(e1);
startState->emptyTrans.push_back(1);
startState->digitTrans['+']=1;
startState->digitTrans['-']=1;
startState->digitTrans[' ']=0;
State* e2=new State;
e2->isEnd=false;
stateVector.push_back(e2);
e1->digitTrans['d']=2;
e2->digitTrans['d']=2;
State* e3=new State;
e3->isEnd=false;
stateVector.push_back(e3);
e2->digitTrans['.']=3;
State* e4=new State;
e4->isEnd=false;
stateVector.push_back(e4);
e3->emptyTrans.push_back(4);
e4->digitTrans['d']=4;
State* e5=new State;
e5->isEnd=false;
stateVector.push_back(e5);
e4->emptyTrans.push_back(5);
e2->emptyTrans.push_back(5);
e5->emptyTrans.push_back(12);
State* e6=new State;
e6->isEnd=false;
stateVector.push_back(e6);
e5->digitTrans['E']=6;
e5->digitTrans['e']=6;
State* e7=new State;
e7->isEnd=false;
stateVector.push_back(e7);
e6->emptyTrans.push_back(7);
e6->digitTrans['+']=7;
e6->digitTrans['-']=7;
State* e8=new State;
e8->isEnd=false;
stateVector.push_back(e8);
e7->digitTrans['d']=8;
e8->digitTrans['d']=8;
State* e9=new State;
e9->isEnd=false;
stateVector.push_back(e9);
e1->emptyTrans.push_back(9);
e9->digitTrans['d']=9;
e9->emptyTrans.push_back(10);
State* e10=new State;
e10->isEnd=false;
stateVector.push_back(e10);
e9->digitTrans['.']=10;
State* e11=new State;
e11->isEnd=false;
stateVector.push_back(e11);
e10->digitTrans['d']=11;
e11->digitTrans['d']=11;
e11->emptyTrans.push_back(5);
State* e12=new State;
e12->isEnd=true;
stateVector.push_back(e12);
e8->emptyTrans.push_back(12);
e12->digitTrans[' ']=12;
return;
}
void addState(vector<int>& alreadyOn, stack<int>& oldState, stack<int>& newState, int stateIndex){
alreadyOn[stateIndex]=1;
State* s=stateVector[stateIndex];
newState.push(stateIndex);
for(int index : s->emptyTrans){
if(alreadyOn[index]!=1){
addState(alreadyOn,oldState,newState,index);
}
}
}
bool isNumber(string s) {
buildState();
stack<int> oldState;
stack<int> newState;
vector<int> alreadyOn(100,0);
addState(alreadyOn,oldState,newState,0);
bool flag=false;
int count=0;
for(int i=0;i<s.size();i++){
char c=s[i];
bool flag2=false;
while(!newState.empty()){
int s=newState.top();
newState.pop();
alreadyOn[s]=0;
oldState.push(s);
}
while(!oldState.empty()){
int s=oldState.top();
map<char,int> digitTrans=stateVector[s]->digitTrans;
if(digitTrans.find('d')!=digitTrans.end() && c>=48 && c<=57){
flag2=true;
int t=digitTrans['d'];
if(alreadyOn[t]!=1){
addState(alreadyOn,oldState,newState,t);
}
}else if(digitTrans.find(c)!=digitTrans.end() && c!='d'){
flag2=true;
int t=digitTrans[c];
if(alreadyOn[t]!=1){
addState(alreadyOn,oldState,newState,t);
}
}
oldState.pop();
}
if(!flag2){
return false;
}
}
while(!newState.empty()){
int s=newState.top();
if(stateVector[s]->isEnd){
flag=true;
}
newState.pop();
}
for(auto state : stateVector){
delete state;
}
return flag;
}
};
剑指 Offer 26. 树的子结构
输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
B是A的子结构, 即 A中有出现和B相同的结构和节点值。
例如:
给定的树 A:
3
/ \
4 5
/
1 2
给定的树 B:
4
/
1
返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。
示例 1:
输入:A = [1,2,3], B = [3,1]
输出:false
示例 2:
输入:A = [3,4,5,1,2], B = [4,1]
输出:true
注意:==子树的先序遍历不一定是主树先序遍历的子串,所以不能用kmp算法。==例如主树[10,12,6,8,3,11]
和子树
[10,12,6,8]
class Solution {
public:
bool isSubStructure(TreeNode* A, TreeNode* B) {
if(!A || !B) return false;
bool res = false;
// 如果在 A 中匹配到了与 B 的根节点的值一样的节点
if(A -> val == B -> val) res = doesAHaveB(A, B);
// 如果匹配不到,A 往左
if(!res) res = isSubStructure(A -> left, B);
// 还匹配不到,A 往右
if(!res) res = isSubStructure(A -> right, B);
return res;
}
bool doesAHaveB(TreeNode *r1, TreeNode *r2)
{
// 如果 B 已经遍历完了,true
if(!r2) return true;
// 如果 B 还有,但 A 遍历完了,那 B 剩下的就没法匹配了,false
if(!r1) return false;
// 不相等,false
if(r1 -> val != r2 -> val) return false;
return doesAHaveB(r1 -> left, r2 -> left) && doesAHaveB(r1 -> right, r2 -> right);
}
};
剑指 Offer 29. 顺时针打印矩阵
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
示例 2:
输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]
限制:
0 <= matrix.length <= 100
0 <= matrix[i].length <= 100
思路:按照有下做上四个方向循环,如果碰到边界或者已经遍历过的点就改变方向。可以就地保存已遍历的flag.
class Solution {
public:
enum direction {RIGHT,DOWN,LEFT,UP};
int dx[4]={1,0,-1,0};
int dy[4]={0,1,0,-1};
vector<int> spiralOrder(vector<vector<int>>& matrix) {
vector<int> ret;
int place=-9999;
direction cur_dir=RIGHT;
int rows=matrix.size();
if(rows==0){
return ret;
}
int cols=matrix[0].size();
int cur_row=0;
int cur_col=0;
int tmp_row=0;
int tmp_col=0;
while(true){
ret.push_back(matrix[cur_row][cur_col]);
matrix[cur_row][cur_col]=place;
tmp_row=cur_row+dy[cur_dir];
tmp_col=cur_col+dx[cur_dir];
// cout<<"---1"<<endl;
if(tmp_row>-1 && tmp_col>-1 && tmp_row<rows && tmp_col<cols && matrix[tmp_row][tmp_col]!=place){
cur_row=tmp_row;
cur_col=tmp_col;
}else{
cur_dir=direction((cur_dir+1)%4);
tmp_row=cur_row+dy[cur_dir];
tmp_col=cur_col+dx[cur_dir];
if(tmp_row>-1 && tmp_col>-1 && tmp_row<rows && tmp_col<cols && matrix[tmp_row][tmp_col]!=place){
cur_row=tmp_row;
cur_col=tmp_col;
continue;
}else{
return ret;
}
}
}
return ret;
}
};
剑指 Offer 28. 对称的二叉树
请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
1
/ \
2 2
/ \ / \
3 4 4 3
但是 [1,2,2,null,3,null,3] 则不是镜像对称的
思路:和树的子结构这一题有点类似,对比子树1和子树2的时候先比较根节点然后再对比子树1的右子树和子树2的左子树,以及子树1的做子树和子树2的右子树
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if(root==NULL){
return true;
}
return compare(root->left,root->right);
}
bool compare(TreeNode* cur_left,TreeNode* cur_right){
if(cur_left==NULL || cur_right==NULL){
if(cur_left==cur_right){
return true;
}else{
return false;
}
}
if(cur_left->val!=cur_right->val){
return false;
}
return compare(cur_left->left,cur_right->right) && compare(cur_left->right,cur_right->left);
}
};
剑指 Offer 31. 栈的压入、弹出序列
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。
示例 1:
输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出:true
解释:我们可以按以下顺序执行:
push(1), push(2), push(3), push(4), pop() -> 4,
push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1
思路:维护一个栈,开始时把pushed的第一个元素压入栈。每次从poped队首取一个元素x和栈顶比较,如果相等则弹出。不相等就继续把pushed中元素压入栈再比较。pushed内元素都压完后还是找不到和x相等的,就直接返回错误。
class Solution {
public:
bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
stack<int> stack;
if(pushed.size()==0){
return true;
}
int cur_pop=0;
int cur_push=0;
int cur;
bool flag;
stack.push(pushed[cur_push]);
cur_push+=1;
for(cur_pop=0;cur_pop<popped.size();cur_pop++){
cur=popped[cur_pop];
flag=false;
if(!stack.empty() && stack.top()==popped[cur_pop]){
stack.pop();
flag=true;
}else{
while(cur_push<pushed.size()){
stack.push(pushed[cur_push]);
cur_push+=1;
if(stack.top()==popped[cur_pop]){
stack.pop();
flag=true;
break;
}
}
}
if(!flag){
return false;
}
}
return true;
}
};
LCS no.583
动态规划思路:如果string[i]==string[j]
则dp[i][j]=dp[i-1][j-1]
否则为max(dp[][j-1],dp[i-1][j])
class Solution {
public:
int minDistance(string word1, string word2) {
vector<vector<int> > dp(510,vector<int> (510));
int len1=word1.size();
int len2=word2.size();
if(len1==0 || len2==0){
return len1+len2;
}
if(word1[0]==word2[0]){
dp[0][0]=1;
}else{
dp[0][0]=0;
}
for(int i=1;i<len1;i++){
if(dp[i-1][0]==1 || word1[i]==word2[0]){
dp[i][0]=1;
}else{
dp[i][0]=0;
}
}
for(int i=1;i<len2;i++){
if(dp[0][i-1]==1 || word2[i]==word1[0]){
dp[0][i]=1;
}else{
dp[0][i]=0;
}
}
for(int i=1;i<len1;i++){
for(int j=1;j<len2;j++){
if(word1[i]==word2[j]){
dp[i][j]=dp[i-1][j-1]+1;
}else if(dp[i-1][j]>dp[i][j-1]){
dp[i][j]=dp[i-1][j];
}else{
dp[i][j]=dp[i][j-1];
}
}
}
return len1+len2-2*dp[len1-1][len2-1];
}
};
剑指 Offer 33. 二叉搜索树的后序遍历序列
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。
参考以下这颗二叉搜索树:
5
/ \
2 6
/ \
1 3
示例 1:
输入: [1,6,3,2,5]
输出: false
示例 2:
输入: [1,3,2,6,5]
输出: true
思路利用后续遍历序列中最后一个节点是根节点的性质,又因为是搜索树,所以根据节点的大小确定左子树和右子树范围,递归调用find(int left,int index,vector& postorder,int s,int l) 其中left和index时子树序号范围,s和l是子树取值的范围
class Solution {
public:
bool verifyPostorder(vector<int>& postorder) {
int len=postorder.size();
if(len==0){
return true;
}
return find(0,len-1,postorder,-999999,999999);
}
bool find(int left,int index,vector<int>& postorder,int s,int l){
int pivot=postorder[index];
if(pivot>l || pivot<s){
return false;
}
if(index==left){
return true;
}
bool find_right=false;
int new_right=index-1;
bool find_left=false;
int new_left=left-1;
bool right_flag=true;
bool left_flag=true;
for(int i=index-1;i>=left;i--){
if (postorder[i]>pivot){
if(!find_right){
find_right=true;
new_right=i;
}
}
else{
find_left=true;
new_left=i;
break;
}
}
if(find_right){
if(pivot>s){
right_flag=find(new_left+1,new_right,postorder,pivot,l);
}else{
right_flag=find(new_left+1,new_right,postorder,s,l);
}
}
if(find_left){
if(pivot<l){
left_flag=find(left,new_left,postorder,s,pivot);
}else{
left_flag=find(left,new_left,postorder,s,l);
}
}
return left_flag&&right_flag;
}
};
剑指 Offer 39. 数组中出现次数超过一半的数字
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
思路: 先假设第一个元素为target,count=0.如果当前元素等于target,count+1,否则-1 。如果count=0则令当前元素为target。
假设众数为x,如果当前target为y,那么最坏的情况就是:到下一个target之前,遍历的元素中一半为y一半为x。但是剩下元素中的众数还是x。继续遍历下去target肯定还是x。
示例 1:
输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
输出: 2
思路:
限制:
1 <= 数组长度 <= 50000
class Solution {
public:
int majorityElement(vector<int>& nums) {
int x = 0, votes = 0, count = 0;
for(int num : nums){
if(votes == 0) x = num;
votes += num == x ? 1 : -1;
}
// 验证 x 是否为众数
for(int num : nums)
if(num == x) count++;
return count > nums.size() / 2 ? x : 0; // 当无众数时返回 0
}
};
剑指 Offer 41. 数据流中的中位数
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。
示例 1:
输入:
[“MedianFinder”,“addNum”,“addNum”,“findMedian”,“addNum”,“findMedian”]
[[],[1],[2],[],[3],[]]
输出:[null,null,null,1.50000,null,2.00000]
示例 2:
输入:
[“MedianFinder”,“addNum”,“findMedian”,“addNum”,“findMedian”]
[[],[2],[],[3],[]]
输出:[null,null,2.00000,null,2.50000]
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node() {}
Node(int _val) {
val = _val;
left = NULL;
right = NULL;
}
Node(int _val, Node* _left, Node* _right) {
val = _val;
left = _left;
right = _right;
}
};
*/
class Solution {
public:
Node* first;
Node* pre;
Node* last;
Node* treeToDoublyList(Node* root) {
if(root==NULL){
return NULL;
}
pre=NULL;
last=NULL;
first=NULL;
connect(root);
first->left=last;
last->right=first;
return first;
}
void connect(Node* cur){
if(cur->left){
connect(cur->left);
}
if(pre){
pre->right=cur;
cur->left=pre;
pre=cur;
}else{
first=cur;
pre=cur;
}
last=cur;
if(cur->right){
connect(cur->right);
}
}
};
剑指 Offer 43. 1~n 整数中 1 出现的次数
输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。
例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。
示例 1:
输入:n = 12
输出:5
示例 2:
输入:n = 13
输出:6
思路:递归处理首位和后缀,事先计算999…9的情况
限制:
1 <= n < 2^31
class Solution {
public:
vector<long> base;
vector<long> record;
int countDigitOne(int n) {
string s=to_string(n);
return cal(s,0);
}
Solution(){
base=vector(10,long(0));
record=vector(10,long(0));
fill(10);
base.clear();
}
void fill(int size){
base[0]=1;
for(int i=1;i<size;i++){
for(int j=i;j>=0;j--){
if(j==0){
base[0]=9*base[0];
}else{
base[j]=9*base[j]+base[j-1];
}
record[i]+=j*base[j];
}
}
}
long cal(string& number, int left){
int size=number.size();
istringstream is(number.substr(left,1));
int first;
is >> first;
int sub_size=size-1-left;
long result=0;
if(left==size){
return long(0);
}else if(left==size-1){
for(int i=first;i>=0;i--){
if(i==1){
result+=1;
}
}
return result;
}
if(first==1){
int sub=atoi(number.substr(left+1).c_str());
result+=sub+1+cal(number,left+1);
}else{
result+=cal(number,left+1);
}
for(int i=first-1;i>=0;i--){
if(i==1){
result+=record[sub_size]+long(pow(10,sub_size));
}else{
result+=record[sub_size];
}
}
return result;
}
};
剑指 Offer 42. 连续子数组的最大和
输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
要求时间复杂度为O(n)。
示例1:
输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
提示:
1 <= arr.length <= 10^5
-100 <= arr[i] <= 100
class Solution {
public:
int maxSubArray(vector<int>& nums) {
// int maxL,maxR,posL,posR,negL,negR;
int maxNeg,maxPos,curPos,curNeg;
maxNeg=-99999;
maxPos=-1;
bool posFlag=false;
for(int i=0;i<nums.size();i++){
int cur=nums[i];
if(cur>=0){
if(posFlag){
curPos+=cur;
}else{
posFlag=true;
if((curNeg+curPos)>=0){
curPos=curNeg+curPos+cur;
}else{
curPos=cur;
}
}
}else{
if(posFlag){
posFlag=false;
curNeg=cur;
if(curPos>maxPos){
maxPos=curPos;
}
}else{
curNeg+=cur;
}
if(cur>maxNeg){
maxNeg=cur;
}
}
}
if(posFlag){
if(curPos>maxPos){
maxPos=curPos;
}
}
if(maxPos>-1){
return maxPos;
}else{
return maxNeg;
}
}
};
剑指 Offer 44. 数字序列中某一位的数字
数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位是4,等等。
请写一个函数,求任意第n位对应的数字。
示例 1:
输入:n = 3
输出:3
示例 2:
输入:n = 11
输出:0
限制:
0 <= n < 2^31
注意:本题与主站 400 题相同:https://leetcode-cn.com/problems/nth-digit/
class Solution {
public:
vector<long> base;
Solution(){
base=vector(10,long(0));
base[1]=9;
for(int i=2;i<base.size();i++){
base[i]+=9*i*pow(10,i-1)+base[i-1];
}
}
int findNthDigit(int n) {
int b;
int real;
for(int i=1;i<base.size();i++){
if(n<=base[i]){
b=i;
break;
}
}
int qu=(n-base[b-1])/b;
int re=n-base[b-1]-qu*b;
if(re==0){
real=pow(10,b-1)-1+qu;
string number=to_string(real);
istringstream is(number.substr(b-1,1));
int result;
is >> result;
return result;
}else{
real=pow(10,b-1)+qu;
string number=to_string(real);
istringstream is(number.substr(re-1,1));
int result;
is >> result;
return result;
}
}
};
剑指 Offer 46. 把数字翻译成字符串
给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。
示例 1:
输入: 12258
输出: 5
解释: 12258有5种不同的翻译,分别是"bccfi", “bwfi”, “bczi”, “mcfi"和"mzi”
提示:
0 <= num < 231
class Solution {
public:
int result;
int len;
string s;
int translateNum(int num) {
s=to_string(num);
len=s.size();
result=0;
fun(0);
return result;
}
void fun(int left){
int cur_len=len-left;
if(cur_len==1 || cur_len==0){
result+=1;
return;
}
int nt=atoi(s.substr(left,2).c_str());
int ns=atoi(s.substr(left,1).c_str());
fun(left+1);
if(nt<=25 && ns!=0){
fun(left+2);
}
return;
}
};
剑指 Offer 48. 最长不含重复字符的子字符串
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
提示:
s.length <= 40000
class Solution {
public:
int nthUglyNumber(int n) {
int a,b,c;
vector<int> record;
a=b=c=0;
record.push_back(1);
for(int i=1;i<=n-1;i++){
while(record[a]*2 <=record[i-1]){
a++;
}
while(record[b]*3 <=record[i-1]){
b++;
}
while(record[c]*5 <=record[i-1]){
c++;
}
int m=min(min(record[a]*2,record[b]*3),record[c]*5);
record.push_back(m);
}
return record[n-1];
}
};
剑指 Offer 53 - I. 在排序数组中查找数字 I
统计一个数字在排序数组中出现的次数。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: 0
限制:
0 <= 数组长度 <= 50000
思路: 两趟二分查找,一个找做边界,一个找有边界。找左右边界时,中点的求法不同。
class Solution {
public:
int search(vector<int>& nums, int target) {
int len=nums.size();
if(len==0){
return 0;
}
int left=findLeft(nums,target,0,len-1);
int right=findRight(nums,target,0,len-1);
if(nums[left]!=target){
return 0;
}
return right-left+1;
}
int findLeft(vector<int>& nums,int target,int left,int right){
if(left==right){
if(nums[left]==target){
return left;
}else{
return 0;
}
}
int mid=(left+right)/2;
if(nums[mid]>=target){
return findLeft(nums,target,left,mid);
}else{
return findLeft(nums,target,mid+1,right);
}
}
int findRight(vector<int>& nums,int target,int left,int right){
if(left==right){
if(nums[left]==target){
return left;
}else{
return -1;
}
}
int mid=(left+right-1)/2+1;
if(nums[mid]<=target){
return findRight(nums,target,mid,right);
}else{
return findRight(nums,target,left,mid-1);
}
}
};
剑指 Offer 53 - I. 在排序数组中查找数字 I
统计一个数字在排序数组中出现的次数。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: 0
限制:
0 <= 数组长度 <= 50000
思路: 只要是有序就要想到二分法
class Solution {
public:
int missingNumber(vector<int>& nums) {
int len=nums.size();
if((nums[len-1]-nums[0])==(len-1)){
if(nums[0]==0){
return nums[len-1]+1;
}else{
return 0;
}
}else{
return find(nums,0,len-1);
}
}
int find(vector<int>& nums,int left,int right){
cout<<left<<" "<<right<<endl;
if(right==left+1){
return nums[left]+1;
}
int mid=(left+right)/2;
if((nums[mid]-nums[left])!=(mid-left)){
return find(nums,left,mid);
}else{
return find(nums,mid,right);
}
}
};
剑指 Offer 57 - II. 和为s的连续正数序列
输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
示例 1:
输入:target = 9
输出:[[2,3,4],[4,5]]
示例 2:
输入:target = 15
输出:[[1,2,3,4,5],[4,5,6],[7,8]]
限制:
1 <= target <= 10^5
思路:分成奇数个和偶数个的情况。
class Solution {
public:
static bool sortVector(vector<int> &ad1, vector<int> &ad2)
{
return ad1[0] < ad2[0];
}
vector<vector<int>> findContinuousSequence(int target) {
int a;
vector<vector<int>> result;
for(a=target;a>1;a--){
int b=target/a;
if(b>a){
break;
}else{
if((target%a)==0){
if(b%2){
auto tmp1=ifEven(a,b);
auto tmp2=ifOdd(a,b);
if(tmp1.size()>0){
result.push_back(tmp1);
}
if(tmp2.size()>0){
result.push_back(tmp2);
}
}
if(a!=b && a%2){
auto tmp1=ifEven(b,a);
auto tmp2=ifOdd(b,a);
if(tmp1.size()>0){
result.push_back(tmp1);
}
if(tmp2.size()>0){
result.push_back(tmp2);
}
}
}
}
}
sort(result.begin(), result.end(), sortVector);
return result;
}
vector<int> ifEven(int a,int b){
int center=b/2;
vector<int> result;
if(center-a+1>0){
for(int i=center-a+1;i<=center+a;i++){
result.push_back(i);
}
return result;
}else{
return result;
}
}
vector<int> ifOdd(int a, int b){
int count =b/2;
vector<int> result;
if(count>0 && a-count>0){
for(int i=a-count;i<=a+count;i++){
result.push_back(i);
}
return result;
}else{
return result;
}
}
};
剑指 Offer 59 - I. 滑动窗口的最大值
给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
提示:
你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。
思路:在窗口内设置一个左端点,保持左端点一直是窗口内最大值,如果从右边新加入的节点比左端点大,则令这个端点为新的左端点,当旧的左端点被pop掉的时候且新节点比左端点小时,从左到右寻找最大的点作为新左端点。
7 6 7 5
第一个窗口 【767】
第二个窗口 【75】
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int len=nums.size();
int left=0;
vector<int> result;
int right;
if(len==0){
return result;
}
if(k==1){
return nums;
}
for(right=1;right<k;right++){
if(nums[right]>=nums[left]){
left=right;
}
}
result.push_back(nums[left]);
for(right=k;right<len;right++){
if(nums[right]>=nums[left]){
left=right;
}
if(left>right-k){
result.push_back(nums[left]);
}else{
int max=-9999;
int index;
for(left=right-k+1;left<=right;left++){
if(nums[left]>max){
max=nums[left];
index=left;
}
}
left=index;
result.push_back(nums[left]);
}
// cout<<left<<endl;
}
return result;
}
};
剑指 Offer 62. 圆圈中最后剩下的数字
0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。
例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。
示例 1:
输入: n = 5, m = 3
输出: 3
示例 2:
输入: n = 10, m = 17
输出: 2
限制:
1 <= n <= 10^5
1 <= m <= 10^6
思路:利用递归思想,
class Solution {
public:
int lastRemaining(int n, int m) {
if(n==1){
return 0;
}else{
int r=(m-1)%n;
int c=lastRemaining(n-1,m);
return (r+1+c)%n;
}
}
};
剑指 Offer 63. 股票的最大利润
假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少?
示例 1:
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
示例 2:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
限制:
0 <= 数组长度 <= 10^5
class Solution {
public:
int maxProfit(vector<int>& prices) {
int max=0;
int t_l=0,t_r=0;
int len=prices.size();
for(int i=1;i<len;i++){
if(prices[i]>prices[t_r]){
int t_m=prices[i]-prices[t_l];
if(t_m>max){
max=t_m;
}
}else if(prices[i]<prices[t_l]){
t_l=t_r=i;
}
}
return max;
}
};
剑指 Offer 66. 构建乘积数组
给定一个数组 A[0,1,…,n-1],请构建一个数组 B[0,1,…,n-1],其中 B[i] 的值是数组 A 中除了下标 i 以外的元素的积, 即 B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]。不能使用除法。
示例:
输入: [1,2,3,4,5]
输出: [120,60,40,30,24]
提示:
所有元素乘积之和不会溢出 32 位整数
a.length <= 100000
思路: 按表格排列,分别按照上三角和下三角进行计算。
class Solution {
public:
vector<int> constructArr(vector<int>& a) {
vector<int> up;
up.push_back(1);
vector<int> down;
down.push_back(1);
vector<int> result;
int len=a.size();
int t_up=1;
int t_d=1;
for(int i=1;i<len;i++){
t_up*=a[i-1];
up.push_back(t_up);
}
for(int i=len-2;i>=0;i--){
t_d*=a[i+1];
down.push_back(t_d);
}
for(int i=0;i<len;i++){
result.push_back(up[i]*down[len-1-i]);
}
return result;
}
};
剑指 Offer 67. 把字符串转换成整数
写一个函数 StrToInt,实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。
当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。
该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0。
说明:
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。
示例 1:
输入: “42”
输出: 42
示例 2:
输入: " -42"
输出: -42
解释: 第一个非空白字符为 ‘-’, 它是一个负号。
我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。
示例 3:
输入: “4193 with words”
输出: 4193
解释: 转换截止于数字 ‘3’ ,因为它的下一个字符不为数字。
示例 4:
输入: “words and 987”
输出: 0
解释: 第一个非空字符是 ‘w’, 但它不是数字或正、负号。
因此无法执行有效的转换。
示例 5:
输入: “-91283472332”
输出: -2147483648
解释: 数字 “-91283472332” 超过 32 位有符号整数范围。
因此返回 INT_MIN (−231) 。
判断溢出的方法: