文章目录
-
- 剑指offer_编程题
- 【1.二维数组中的查找】
- 【2.替换空格】
- 【3.从尾到头打印链表】
- 【4.重建二叉树】
- 【5.用两个栈实现队列】
- 【6.旋转数组的最小数字】
- 【7.斐波那契数列】
- 【8.跳台阶】
- 【9.变态跳台阶】
- 【10.矩形覆盖】
- 【11.二进制中1的个数】
- 【12.数值的整数次方】
- 【13.调整数组顺序使其位于偶数前面】
- 【14.链表倒数第k个节点】
- 【15.反转链表】
- 【16.合并两个有序链表】
- 【17.树的子结构】
- 【17.二叉树的镜像】
- 【18.顺时针打印矩阵】
- 【19.包含min的栈】
- 【20.栈的压入、弹出序列】
- 【21.从上向下打印二叉树】
- 【22.二叉搜索树的后序遍历序列】
- 【23.二叉树中和为某一值的路径】
- 【24.复杂链表的复制】
- 【25.二叉搜索树与双向链表】
- 【26.字符串的排列】
- 【27.数组中出现次数超过一半的数字】
- 【28.最小的k个数】
- 【29.连续数组的最大和】
- 【30.整数中1出现的次数】
- 【31.把数组排成最小的数】
- 【32.丑数】
- 【33.第一个只出现一次的字符】
- 【34.数组中的逆序对】
- 【35.两个链表的第一个公共节点】
- 【36.数字在排序数组中出现的次数】
- 【37.二叉树的深度】
- 【38.平衡二叉树】
- 【39.数组中只出现一次的数字】
- 【40.和为S的连续正整数序列】
- 【41.和为S的两个数字】
- 【42.左旋转字符串】
- 【43.翻转单词序列】
- 【44.扑克牌顺子】
- 【45.孩子们的游戏(圆圈中剩下的数)】
- 【46.求1 + 2 + 3 + ... + n】
- 【47.不用加减乘除做加法】
- 【48.把字符串转换成整数】
- 【49.数组中重复的数字】
- 【50.构造乘积数组】
- 【51.正则表达式匹配】
- 【52.表示数值的字符串】
- 【53.字符流中第一个不重复的字符】
- 【54.链表中环的入口节点】
- 【55.删除链表中重复的节点】
- 【56.二叉树的下一个节点】
- 【57.对称的二叉树】
- 【58.按之字形打印二叉树】
- 【59.将二叉树打印成多行】
- 【60.序列化二叉树】
- 【61.二叉搜索树的第k个节点】
- 【62.数据流中的中位数】
- 【63.滑动窗口的最大值】
- 【64.矩阵中的路径】
- 【65.机器人的运动范围】
- 【66.剪绳子】
剑指offer_编程题
【1.二维数组中的查找】
【题目描述】
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
【解题思路】
对于每一行使用二分查找,可用lower_bound函数
【AC代码】
class Solution {
public:
bool Find(int target, vector<vector<int> > array) {
for(auto& v : array) {
auto temp = lower_bound(v.begin(), v.end(), target);
if(temp == v.end()) continue; //注意要判断是否为end,不然会越界
if(*temp == target) return true;
}
return false;
}
};
【2.替换空格】
【题目描述】
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
【解题思路】
创建一个副本,遍历源字符串,如果是空格将其替换为"%20",否则将其等于原字符
或者用Java中的自带函数replace
【AC代码】
C++
class Solution {
public:
void replaceSpace(char *str,int length) {
if(str == NULL) return;
char temp[110];
int cnt = -1;
for(int i = 0; i < length; ++i) {
if(str[i] == ' ') {
temp[++cnt] = '%';
temp[++cnt] = '2';
temp[++cnt] = '0';
}
else temp[++cnt] = str[i];
}
temp[++cnt] = '\0'; //别忘了‘\0’
strcpy(str, temp);
}
};
Java
public class Solution {
public String replaceSpace(StringBuffer str) {
return str.toString().replace(" ", "%20");
}
}
【3.从尾到头打印链表】
【题目描述】
输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
【解题思路】
顺序遍历链表,用vector存储其值,最后reverse
【AC代码】
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) :
* val(x), next(NULL) {
* }
* };
*/
class Solution {
public:
vector<int> printListFromTailToHead(ListNode* head) {
vector<int> v;
if(head == NULL) return v;
while(head != NULL) {
v.push_back(head -> val);
head = head -> next;
}
reverse(v.begin(), v.end());
return v;
}
};
【4.重建二叉树】
【题目描述】
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
【解题思路】
模拟人工重建二叉树方法即可
【AC代码】
/**
* Definition for binary tree
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
int l = pre.size();
return rebuild(pre, vin, 0, l - 1, 0, l - 1);
}
TreeNode* rebuild(vector<int>& pre, vector<int>& vin, int l1, int r1, int l2, int r2) {
//l1,r1表示该子树在pre中的下标,l2,r2表示在vin中的下标
if(l1 > r1 || l2 > r2) return NULL;
int index = -1;
for(int i = l2; i <= r2; ++i) {
if(vin[i] == pre[l1]) {
index = i; //找到根节点
break;
}
}
TreeNode* root = new TreeNode(pre[l1]); //新建根节点
root -> left = rebuild(pre, vin, l1 + 1, l1 + index - l2, l2, index - 1);
root -> right = rebuild(pre, vin, l1 + index - l2 + 1, r1, index + 1, r2);
return root;
}
};
【5.用两个栈实现队列】
【题目描述】
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
【解题思路】
栈和队列的不同在于pop顺序不同,push顺序相同
第一个栈保存所有的入队元素,在pop时第一个栈所有元素出栈,将所有元素保存至栈二,此时栈二栈顶元素即为队头元素,将其出栈,再将剩余元素全部存入栈一,保持其顺序不变
【AC代码】
class Solution {
public:
void push(int node) {
stack1.push(node);
}
int pop() {
while(!stack1.empty()) {
stack2.push(stack1.top());
stack1.pop();
}
int u = stack2.top();
stack2.pop();
while(!stack2.empty()) {
stack1.push(stack2.top());
stack2.pop();
}
return u;
}
private:
stack<int> stack1;
stack<int> stack2;
};
【6.旋转数组的最小数字】
【题目描述】
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
【解题思路】
法一:暴力遍历加上一丝剪枝
法二:先sort然后返回开头
法三:二分查找
【AC代码】
暴力
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
if(rotateArray.size() == 0) return 0;
int pre = -1;
for(auto& it : rotateArray) {
if(it < pre) return it;
pre = it;
}
return rotateArray[0];
}
};
Sort
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
if(rotateArray.size() == 0) return 0;
sort(rotateArray.begin(), rotateArray.end());
return rotateArray[0];
}
};
二分法
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
if(rotateArray.size() == 0) return 0;
int left = 0, right = rotateArray.size() - 1;
int ans = rotateArray[left];
while(left <= right) {
int mid = (left + right) >> 1;
ans = min(ans, rotateArray[mid]);
if(rotateArray[mid] >= rotateArray[left]) {
left = mid + 1;
ans = min(ans, rotateArray[mid + 1]);
}
if(rotateArray[mid] <= rotateArray[right]) {
right = mid - 1;
ans = min(ans, rotateArray[mid - 1]);
}
}
return ans;
}
};
【7.斐波那契数列】
【题目描述】
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0,n<=39)。
【解题思路】
F(n) = F(n - 1) + F(n - 2)
采用记忆化递归求解,注意结束条件
【AC代码】
class Solution {
public:
int F[40];
int Fibonacci(int n) {
if(n == 0) return 0;
if(n == 1 || n == 2) return 1;
if(F[n]) return F[n];
return F[n] = Fibonacci(n - 1) + Fibonacci(n - 2);
}
};
【8.跳台阶】
【题目描述】
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
【解题思路】
动态规划,考虑假设当前已经跳了n阶台阶,那么可以从n - 1跳一阶到达n,也可以从n - 2跳两阶到达n,故递推式为斐波那契递推式
【AC代码】
class Solution {
public:
int F[100];
int jumpFloor(int number) {
if(number == 0 ||number == 1) return 1;
if(F[number]) return F[number];
return F[number] = jumpFloor(number - 1) + jumpFloor(number - 2);
}
};
【9.变态跳台阶】
【题目描述】
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
【解题思路】
和上一题一样,考虑当前跳到了n阶台阶,可以从0 - (n - 1)分别跳(n) -(1)次,所以有
F(n) = F(0) + F(1) + F(2) + … + F(n - 1)
F(n + 1) = F(0) + F(1) + F(2) + … + F(n)
得到F(n + 1) = 2 * F(n)
得到F(n) = 2(n-1)
【AC代码】
class Solution {
public:
int jumpFloorII(int number) {
if(number == 0 ||number == 1) return 1;
return pow(2, number - 1);
}
};
【10.矩形覆盖】
【题目描述】
我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
【解题思路】
还是斐波那契数列,依旧是动态规划,假设当前已经覆盖好了2 * n的矩形,那么现在的状态可以由2 * (n - 1)铺一块2 * 1,也可以由2 * (n - 2)铺两块2 * 1得来,所以F[n] = F[n - 1] + F[n - 2]
这题有坑的地方是0的时候答案是0
【AC代码】
class Solution {
public:
int F[100];
int rectCover(int number) {
if(number == 0) return 0;
return solve(number);
}
int solve(int n) {
if(n == 0 || n == 1) return 1;
if(F[n]) return F[n];
return F[n] = solve(n - 1) + solve(n - 2);
}
};
【11.二进制中1的个数】
【题目描述】
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
【解题思路】
法一:Java将int转成二进制字符后统计1的个数
法二:调用Java中的>>>运算符
法三:找规律
法三:
将n表示为二进制后,若其不为0,那么至少含有一个1,那么n - 1就会将n中最右边的1变成0,然后将从右至该位的所有0变为1,此时n & (n - 1)就可以将该位变为0
例如:
n = 12 = 1100
n - 1 = 11 = 1011
n = n & (n - 1) = 1000 = 8
n - 1 = 7 = 0111
n & (n - 1) = 0000 = 0
【AC代码】
暴力
public class Solution {
public int NumberOf1(int n) {
char[] s = Integer.toBinaryString(n).toCharArray();
int len = s.length, ans = 0;
for(int i = 0; i < len; ++i) {
if(s[i] == '1') ++ans;
}
return ans;
}
};
>>>
public class Solution {
public int NumberOf1(int n) {
int ans = 0;
while(n != 0) {
ans += (n & 1);
n >>>= 1;
}
return ans;
}
};
规律
class Solution {
public:
int NumberOf1(int n) {
int ans = 0;
while(n) {
++ans;
n &= (n - 1);
}
return ans;
}
};
【12.数值的整数次方】
【题目描述】
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
保证base和exponent不同时为0
【解题思路】
考虑用快速幂,如果不理解快速幂可以看下面这张图
注意指数可能是负数
【AC代码】
class Solution {
public:
double Power(double base, int exponent) {
if(base == 0) return 0.0;
double res = 1.0;
int n = abs(exponent);
while(n) {
if(n & 1) res *= base;
base *= base;
n >>= 1;
}
return exponent > 0 ? res : 1 / res;
}
};
【13.调整数组顺序使其位于偶数前面】
【题目描述】
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
【解题思路】
法一:创立一个临时数组,遍历array两次,第一次找奇数,第二次找偶数
法二:借鉴插入排序,每遇到一个奇数就将其与前面的偶数交换
法一牺牲空间,节省时间,法二节省空间,消耗时间
【AC代码】
创建临时数组
class Solution {
public:
void reOrderArray(vector<int> &array) {
vector<int> temp;
for(auto& it : array) {
if(it & 1) temp.push_back(it);
}
for(auto& it : array) {
if(it & 1) continue;
temp.push_back(it);
}
array = temp;
}
};
借鉴插入排序
class Solution {
public:
void reOrderArray(vector<int> &array) {
int l = array.size();
for(int i = 0; i < l; ++i) {
if(array[i] & 1) {
int j = i;
while(j > 0 && (array[j - 1] & 1) == 0) {
swap(array[j], array[j - 1]);
--j;
}
}
}
}
};
【14.链表倒数第k个节点】
【题目描述】
输入一个链表,输出该链表中倒数第k个结点。
【解题思路】
可以用vector或stack存放所有的链表节点,或者先求得链表总长度,重新遍历
【AC代码】
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
int cnt = 1;
ListNode* pHead = pListHead;
while(pHead != NULL) {
pHead = pHead -> next;
++cnt;
}
if(k >= cnt) return NULL;
for(int i = 1; i < cnt - k; ++i) pListHead = pListHead -> next;
return pListHead;
}
};
【15.反转链表】
【题目描述】
输入一个链表,反转链表后,输出新链表的表头。
【解题思路】
模拟即可
可以画个图模拟一下
【AC代码】
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {