注:所有题目来源于leetcode官网
- 剑指offer-03.数组中重复的数组
难度:简单
题目:找出数组中重复的数字。在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
方法一:
//用一个map,记录所有数字出现的个数,然后遍历map,找到个数第一次大于1的数字。
class Solution {
private:
unordered_map<int,int> mp;
public:
int findRepeatNumber(vector<int>& nums) {
if(nums.size() == 0) return 0;
for(int i=0;i<nums.size();i++)
{
if(mp.find(nums[i]) == mp.end())
{
pair<int,int> pr(nums[i],1);
mp.insert(pr);
}
else
{
mp[nums[i]]++;
}
}
unordered_map<int,int>::iterator ite = mp.begin();
int nMax = 0;
int num;
while(ite != mp.end())
{
if(ite->second > nMax)
{
num = ite->first;
nMax = ite->second;
}
ite++;
}
return num;
}
};
方法二:
//遍历一遍数组即可,因为数字范围刚好是下标范围,根据这个特点。
//看每个数组里元素里的值,把它代表的下标里的元素进行对比,如果不相等,调换位置;若相等,说明找到重复得了,直接返回即可。O(n)
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
int nSize = nums.size();
for(int i=0;i<nSize;i++)
{
if(i == nums[i]) continue;//这里指的注意,不判断的话,即使出现一次也会返回。
if(nums[i] == nums[nums[i]])
{
return nums[i];
}
else
{
nums[i] = nums[i] ^ nums[nums[i]];
nums[nums[i]] = nums[i] ^ nums[nums[i]];
nums[i] = nums[i] ^ nums[nums[i]];
}
}
return -1;
}
};
- 剑指offer-66.构建乘积数组
难度:简单
题目:给定一个数组 A[0,1,…,n-1],请构建一个数组 B[0,1,…,n-1],其中 B 中的元素 B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]。不能使用除法。
代码:
//第一次遍历,从前往后,每个元素前半部分的乘积值为:前一个的左乘积,再乘以前一个的元素的值即可。
//第二次遍历,从后往前,每个元素后半部分的乘积值为:后一个的右乘积值,再乘以后一个的元素值即可。
//两个数组乘积即最后结果。
class Solution {
public:
vector<int> constructArr(vector<int>& a) {
int nSize = a.size();
vector<int> leftvec(nSize,1);
vector<int> rightvec(nSize,1);
for(int i=0;i<nSize;i++)
{
if(i==0) continue;
leftvec[i] = leftvec[i-1]*a[i-1];
}
for(int i=nSize-1;i>=0;i--)
{
if(i==nSize-1) continue;
rightvec[i] = rightvec[i+1]*a[i+1];
}
for(int i=0;i<nSize;i++)
{
leftvec[i] = leftvec[i]*rightvec[i];
}
return leftvec;
}
};
- 剑指offer-20.表示数值的字符串
难度:中等
题目:请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100"、“5e2”、"-123"、“3.1416”、"-1E-16"、“0123"都表示数值,但"12e”、“1a3.14”、“1.2.3”、"±5"及"12e+5.4"都不是。
代码:
//1.判断是否有符号,即'+'和'-',要么没有要么只能有一个,并且一定是第一个
//2.从第一个非符号数开始判断,一直往后看,直到\0。如果遇到E/e,不能遇到.
//3.小数点最多只能有一个。
//4.e/E和后面的数字,要么都有,要么都没有,也就是说e/E不能结尾,后面的数字可有符号
//leetcode的测试用例有的有点无语,按着用例改了好久....
class Solution {
public:
bool isNumber(string s) {
int nSize = s.size();
if(nSize == 0) return false;
//注:如果前面和后面有空格,排除
int j=0;
while(j<nSize && s[j]==' ')
j++;
int k=nSize-1;
while(k>=0 && s[k]==' ')
k--;
s = s.substr(j,k-j+1);
//去掉符号位
if(s[0] == '+' || s[0]=='-')
s = s.substr(1,nSize-1);
//开始匹配剩下的
int flag1 = 0;//e/E是否出现
int flag2 = 0;//小数点是否出现
int i;
nSize = s.size();
if(nSize == 0) return false;
//'.'可以开头或结尾,但是不能单独存在
if(0==nSize-1 && s[i]=='.') return false;
for(i=0;i<nSize;i++)
{
if(s[i] == '.')//2.小数点
{
//if(i==0 || i==nSize-1) break;
if(flag1==1 || flag2==1) break;
flag2 = 1;
}
else if(s[i]=='E' || s[i]=='e')
{
if(flag1 == 1) break;// || flag2==1
if(i==0 || i == nSize-1) break;
if(s[i+1] == '.') break;//e后面不能接'.'
if(i == 1 && s[0] == '.') break;
if(s[i+1]=='+' || s[i+1]=='-')
{
if(i+2==nSize) return false;//如果出现运算符,后面必须加数字。
i++;
}
flag1 = 1;
}
else if(s[i]-'0'>=0 && s[i]-'0'<=9)
{
continue;
}
else
break;
}
if(i >= nSize) return true;
return false;
}
};
- 剑指offer-50.第一个只出现一次的字符
难度:简单
题目:在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。
代码:
//创建字符容器;
//第一次遍历,记录每个字符的个数。
//第二次遍历,找到第一个出现个数为1的字符,返回即可。
class Solution {
public:
char firstUniqChar(string s) {
int nSize = s.size();
unordered_map<char,int> mp;
for(int i=0;i<nSize;i++)
{
mp[s[i]]++;
}
for(int i=0;i<nSize;i++)
if(mp[s[i]] == 1)
return s[i];
return ' ';
}
};
- 剑指offer-12.矩阵中的路径
题型:回溯、递归
难度:中等
题目:请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。
[[“a”,“b”,“c”,“e”],
[“s”,“f”,“c”,“s”],
[“a”,“d”,“e”,“e”]]
但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。
代码:
class Solution {
public:
bool FindPath(vector<vector<char>>& board, string word,int index,int x,int y,int maxx,int maxy){
if(x>=maxx || y>=maxy || x<0 || y<0 || board[x][y]!=word[index])
return false;
if(index == word.size()-1) return true;
//把当前已经用过的字符记录为特殊标记'*',但记得标记上一次的
char temp = board[x][y];
board[x][y] = '*';
//每一步可以在矩阵中向左、右、上、下移动一格
bool res = ( FindPath(board,word,index+1,x-1,y,maxx,maxy)||
FindPath(board,word,index+1,x+1,y,maxx,maxy)||
FindPath(board,word,index+1,x,y-1,maxx,maxy)||
FindPath(board,word,index+1,x,y+1,maxx,maxy) );
//把原数组归位,因为此时的结果只是从当前位置出发。
board[x][y] = temp;
return res;
}
bool exist(vector<vector<char>>& board, string word) {
if(board.size() == 0) return false;
int maxx = board.size();
int maxy = board[0].size();
//从每一个节点出发,去看是否存在满足条件的路径
for(int i=0;i<maxx;i++)
for(int j=0;j<maxy;j++)
if(FindPath(board,word,0,i,j,maxx,maxy) )
return true;
return false;
}
};
- 剑指offer49-丑数
难度:中等
题目:我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。
代码:
class Solution {
public:
int nthUglyNumber(int n) {
if(n==0 ) return 0;
int p2=0,p3=0,p5=0;
vector<int> vec(n,0);
vec[0] = 1;
for(int i=1;i<n;i++)
{
int nMin;
nMin = vec[p2]*2;
if(nMin > vec[p3]*3)
nMin = vec[p3]*3;
if(nMin > vec[p5]*5)
nMin = vec[p5]*5;
if(nMin == vec[p2]*2)
{
vec[i] = nMin;
p2++;
}
if(nMin == vec[p3]*3)
{
vec[i] = nMin;
p3++;
}
if(nMin == vec[p5]*5)
{
vec[i] = nMin;
p5++;
}
}
return vec[n-1];
}
};
- 剑指offer-68(1)-二叉搜索树的最近公共祖先
题型:树,递归
难度:简单
题目:给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
代码:
/**
* 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:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root == nullptr) return nullptr;
if(p == nullptr) return q;
if(q==nullptr || p==q) return p;
if(root==p || root==q) return root;
if(root->val>p->val && root->val<q->val)
return root;
if(root->val<p->val && root->val>q->val)
return root;
if(root->val>p->val && root->val>q->val)
{
return lowestCommonAncestor(root->left,p,q);
}
if(root->val<p->val && root->val<q->val)
return lowestCommonAncestor(root->right,p,q);
return nullptr;
}
};
- 剑指offer-68(2)-二叉树的最近公共祖先
题型:树、递归
题目:给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
//与BST寻找公共祖先不同的是,这棵树无序,分别找最左和最右,直到找到这个节点或一边为nullptr结束
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root==nullptr || p==nullptr || q==nullptr) return nullptr;
if(root==p || root==q) return root;
TreeNode *pLeft = lowestCommonAncestor(root->left,p,q);
TreeNode *pRight = lowestCommonAncestor(root->right,p,q);
if(pLeft == nullptr) return pRight;
if(pRight == nullptr) return pLeft;
return root;
}
};
- 剑指offer-18删除链表的节点
题型:链表
难度:简单
题目:给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。返回删除后的链表的头节点。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* deleteNode(ListNode* head, int val) {
if(head == nullptr) return head;
if(head->val == val) return head->next;
head->next = deleteNode(head->next,val);
return head;
}
};
- 剑指offer-10青蛙跳台阶问题
题型:动规、递归
难度:简单
题目:一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
代码:
//递归做会超时
class Solution {
public:
int numWays(int n) {
if(n==0) return 1;
if(n <= 2) return n;
int n1=1,n2=2;
for(int i=3;i<=n;i++)
{
int temp = n2;
n2 = n1%1000000007+n2%1000000007;
n1 = temp;
}
return n2%1000000007;
}
};
- 剑指 Offer 11. 旋转数组的最小数字
题型:数组、二分
难度:简单
题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
代码:
class Solution {
public:
int minArray(vector<int>& numbers) {
int n = numbers.size();
if(n == 0) return -1;
int low = 0,high = n-1;
int mid = low;
while(low < high)
{
if(numbers[low] < numbers[high]) return numbers[low];
mid = low+(high-low)/2;
if(numbers[low] > numbers[mid])
{
high = mid;
low++;
}
else if(numbers[mid] > numbers[high])
{
low = mid+1;
}
else
{
low++;
}
}
return numbers[low];
}
};
- 剑指 Offer 26. 树的子结构
题型:递归、树
难度:中等
题目:输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
B是A的子结构, 即 A中有出现和B相同的结构和节点值。
代码:
/**
* 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 dfs(TreeNode* A, TreeNode* B){
if(B == nullptr) return true;
if(A == nullptr) return false;
if(A->val == B->val)
{
return dfs(A->left,B->left)&&dfs(A->right,B->right);
}
else
return false;
}
bool isSubStructure(TreeNode* A, TreeNode* B) {
if(A==nullptr || B==nullptr) return false;
return dfs(A,B)||isSubStructure(A->left,B)||isSubStructure(A->right,B);
}
};
13.剑指offer-32-1从上到下打印二叉树1
题型:树、 广优
难度:中等
题目:从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。
代码:
/**
* 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:
vector<int> levelOrder(TreeNode* root) {
vector<int> vec;
if(root == nullptr) return vec;
queue<TreeNode *> que;
que.push(root);
while(!que.empty())
{
TreeNode *pTemp = que.front();
que.pop();
vec.push_back(pTemp->val);
if(pTemp->left)
que.push(pTemp->left);
if(pTemp->right)
que.push(pTemp->right);
}
return vec;
}
};
- 剑指offer-32-2从上到下打印二叉树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:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int> > vec;
if(root == nullptr) return vec;
queue<TreeNode *> que;
vector<int> tempvec;
que.push(root);
while(!que.empty())
{
int len = que.size();
for(int i=0;i<len;i++)
{
TreeNode *pTemp = que.front();
que.pop();
tempvec.push_back(pTemp->val);
if(pTemp->left)
que.push(pTemp->left);
if(pTemp->right)
que.push(pTemp->right);
}
vec.push_back(tempvec);
tempvec.clear();
}
return vec;
}
};
- 剑指offer32-3-从上到下打印二叉树3
题型:树
难度:中等
题目:请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。
代码:
/**
* 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:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int> > vec;
if(root == nullptr) return vec;
deque<TreeNode *> que1;//头出
deque<TreeNode *> que2;//尾出
que1.push_back(root);
vector<int> tempvec;
while(!que1.empty())
{
int len = que1.size();
//第一次是奇数行,处理、放左、放右
for(int i=0;i<len;i++)
{
TreeNode *pTemp = que1.front();
que1.pop_front();
tempvec.push_back(pTemp->val);
if(pTemp->left)
que2.push_back(pTemp->left);
if(pTemp->right)
que2.push_back(pTemp->right);
}
//把奇数行放到大数组里
if(tempvec.size() > 0)
vec.push_back(tempvec);
tempvec.clear();
//下一次是偶数行,把刚才装进队列里的节点,从后往前处理
while(!que2.empty())
{
int len = que2.size();
for(int i=0;i<len;i++)
{
TreeNode *pTemp = que2.back();
que2.pop_back();
tempvec.push_back(pTemp->val);
if(pTemp->right)
que1.push_front(pTemp->right);
if(pTemp->left)
que1.push_front(pTemp->left);
}
}
if(tempvec.size() > 0)
vec.push_back(tempvec);
tempvec.clear();
}
return vec;
}
};
- 剑指offer55-1-二叉树的深度
题型:树
难度:简单
题目:输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。
代码:
(1)递归法
/**
* 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:
int maxDepth(TreeNode* root) {
if(root == nullptr) return 0;
int llen = maxDepth(root->left)+1;
int rlen = maxDepth(root->right)+1;
return llen>rlen?llen:rlen;
}
};
(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:
int maxDepth(TreeNode* root) {
if(root == nullptr) return 0;
queue<TreeNode *> sk;
sk.push(root);
int res = 0;
while(!sk.empty())
{
int len = sk.size();
while(len > 0)
{
TreeNode *pTemp = sk.front();
sk.pop();
if(pTemp->left)
sk.push(pTemp->left);
if(pTemp->right)
sk.push(pTemp->right);
len--;
}
res++;
}
return res;
}
};
- 剑指offer-24-反转链表
题型:链表
难度:简单
题目:定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head == nullptr) return nullptr;
ListNode *pNewHead = nullptr;
ListNode *p1 = head;
ListNode *p2 = head->next;
while(p1 && p2)
{
p1->next = pNewHead;
pNewHead = p1;
p1 = p2;
p2 = p2->next;
}
if(p1)
p1->next = pNewHead;
return p1;
}
};
- 剑指offer-48-最长不含重复子串
题型:双指针
难度:中等
题目:请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
代码:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int n = s.size();
if(n == 0) return 0;
int left=0;
unordered_map<char,int> mp;
int res =1;
mp[s[0]]++;
for(int i=1;i<n;i++)
{
mp[s[i]]++;
while(mp[s[i]] > 1)
{
mp[s[left]]--;
left++;
}
if(i-left+1 > res)
res = i-left+1;
}
return res;
}
};