目录
剑指 Offer 03. 数组中重复的数字
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
//方法一:hash表
class Solution {
public int findRepeatNumber(int[] nums) {
// set中没有重复的元素
Set<Integer> set = new HashSet<Integer>();
int res = -1;
for(int num :nums){
if(!set.add(num)){ //add失败,找到了某个重复元素
res = num;
break;
}
}
return res; //没有找到重复的元素返回-1
}
}
//方法二:
class Solution {
public void swap(int[] nums, int i, int j){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
public int findRepeatNumber(int[] nums) {
int n = nums.length;
//1~ n-1
for(int num : nums){
if(num < 0 || num > n - 1){
return -1;
}
}
//利用下标交换,保证下标 == 元素值
for(int i = 0;i < n;i++){
while(nums[i] != i && nums[nums[i]] != nums[i]){
swap(nums, i, nums[i]); //交换nums[i] nums[nums[i]]
}
if(nums[i] != i && nums[nums[i]] == nums[i])
return nums[i];
}
return -1; //没有找到重复的元素返回-1
}
}
//方法三:排序
class Solution {
public int findRepeatNumber(int[] nums) {
Array.sort(nums);
for(int i = 0; i < nums.length - 1;i++){
if(nums[i] == nums[i + 1])
return nums[i];
}
return -1;
}
}
剑指 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]
]
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
//判断数组空
if((matrix == null || matrix.length ==0) || (matrix.length ==1 && matrix[0].length ==0))
return false;
int i = 0; int j = matrix[0].length - 1;//右上角
while(i <= matrix.length -1 && j >=0){
//一定要先判断是否相等
if(target == matrix[i][j]) return true;
if(target < matrix[i][j]) j--;
if(target > matrix[i][j]) i++;
}
return false;
}
}
剑指 Offer 05. 替换空格
请实现一个函数,把字符串 s
中的每个空格替换成"%20"。
示例 1:
输入:s = "We are happy." 输出:"We%20are%20happy."
//方法一:
class Solution {
public String replaceSpace(String s) {
int n = s.length();
char[] array = new char[n * 3];
int size = 0;
for(int i = 0; i < n; i++){
char c = s.charAt(i);
if(c == ' '){
array[size ++] = '%';
array[size ++] = '2';
array[size ++] = '0';
}
else{
array[size ++] = c;
}
}
String news = new String(array, 0, size);
return news;
}
}
//方法二
class Solution {
public String replaceSpace(String s) {
return s.replace(" ","%20");
}
}
剑指 Offer 06. 从尾到头打印链表
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
示例 1:
输入:head = [1,3,2] 输出:[2,3,1]
//方法一:栈
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public int[] reversePrint(ListNode head) {
//压栈
Stack<ListNode> stk = new Stack<ListNode>();
ListNode temp = head;
while(temp != null) //一直指向最后一个节点
{
stk.push(temp);
temp = temp.next;
}
int size = stk.size();
int[] res = new int[stk.size()];
for(int i = 0; i < size; i++){
res[i] = stk.pop().val;
}
return res;
}
}
//方法二:
class Solution {
public int[] reversePrint(ListNode head) {
ListNode temp = head;
int size = 0;
while(temp != null){
size++;
temp = temp.next;
}
int res[] = new int[size];
temp = head;
for(int i = size - 1; i >= 0; i--){
res[i] = temp.val;
temp = temp.next;
}
return res;
}
}
剑指 Offer 07. 重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:3
/ \
9 20
/ \
15 7
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
// 前序遍历
// (preLIndex)根 (preLIndex+1)左子树(preLIndex+pIndex-inLIndex) (preLIndex+pIndex-inLIndex)右子树(preRIndex)
// 中序遍历
// (inLIndex)左子树(pIndex-1) (pIndex)根 (pIndex+1)右子树(inRIndex)
//将中序遍历数组中的值放入map中,这样不需要遍历,就能找到根节点。
Map<Integer,Integer> map = new HashMap();
for(int i = 0; i < inorder.length; i++){
map.put(inorder[i],i);
}
return buildTree(preorder,0,preorder.length-1,inorder,0,inorder.length-1,map);
}
//递归
private TreeNode buildTree(int[] preorder,int preLIndex,int preRIndex,int[] inorder,int inLIndex,int inRIndex,Map<Integer,Integer> map){
if(preLIndex > preRIndex || inLIndex > inRIndex){
return null;
}
int pIndex = map.get(preorder[preLIndex]); //拿到中序遍历的根坐标
TreeNode root = new TreeNode(preorder[preLIndex]); // 根节点,前序遍历中的第一个值
//构建左子树
root.left = buildTree(preorder,preLIndex+1,preLIndex+pIndex-inLIndex,inorder,inLIndex,pIndex-1,map);
//构建右子树
root.right = buildTree(preorder,preLIndex+pIndex-inLIndex+1,preRIndex,inorder,pIndex+1,inRIndex,map);
return root;
}
}
剑指 Offer 09. 用两个栈实现队列
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
示例 1:
输入:
["CQueue","appendTail","deleteHead","deleteHead"]
[[],[3],[],[]]
输出:[null,null,3,-1]
示例 2:输入:
["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]
class CQueue {
private Stack<Integer> stack1;
private Stack<Integer> stack2;
public CQueue() {
stack1 = new Stack();
stack2 = new Stack();
}
public void appendTail(int value) {
stack1.push(value);
}
public int deleteHead() {
if(stack1.isEmpty() && stack2.isEmpty()){
return -1;
}else{
if(stack2.isEmpty()){
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
}
return stack2.pop();
}
}
/**
* Your CQueue object will be instantiated and called as such:
* CQueue obj = new CQueue();
* obj.appendTail(value);
* int param_2 = obj.deleteHead();
*/
剑指 Offer 10- I. 斐波那契数列
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
示例 1:
输入:n = 2 输出:1
示例 2:
输入:n = 5 输出:5
class Solution {
public int fib(int n) {
int f0 = 0;
int f1 = 1;
if(n == 0){
return f0;
}
if(n == 1){
return f1;
}
int fn = 0;
for(int i = 0; i < n - 1; i++){
fn = (f0 + f1)%1000000007;
f0 = f1;
f1 = fn;
}
return fn;
}
}
剑指 Offer 10- II. 青蛙跳台阶问题
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
示例 1:
输入:n = 2
输出:2
/*第0级:一种方式,就是原地不动;
第1级:1种方式,即从0走一步到第1级;
第2级:2种方式,即从0到1到2,或者直接从0到2;
第3级:考虑每次只能上1或者2级,那么到第三级只有两种情况:从第1级上两步到第3级,或者从第2级走一步
到第三级。上面我们又计算了,到第1级只有一种方式,到第二级有2种方式,所以到第三级的方式就有:
1*1+2*1=3。每一次从前一级或者前二级到当前级都只有一种方式,所以也可以写成:1+2=3。
... ...
第n级:同上,到第n级只有两种方式:从n-2级走两步到第n级,或者从n-1级走一步到第n级。假设到第n-2级
的方式有 f(n-2)种,到第n-1级的方式有f(n-1)种,则到第n级的方式有:
f(n-2)*1+f(n-1)*1=f(n-2)+f(n-1)。
*/
class Solution {
public int numWays(int n) {
int f1 = 1;
int f2 = 2;
if(n == 0 || n == 1){
return 1;
}
if(n == 2){
return 2;
}
int fn = 0;
//1 1 2 3
for(int i = 1; i < n - 1; i++){
fn = (f1 + f2)%1000000007; //一,八个零,七
f1 = f2;
f2 = fn;
}
return fn;
}
}
剑指 Offer 11. 旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
示例 1:
输入:[3,4,5,1,2]
输出:1
class Solution {
//二分法
public int minArray(int[] numbers) {
int left = 0;
int right = numbers.length - 1;
while(left < right){
int mid = (left + right) / 2;
if(numbers[mid] > numbers[right]){
left = mid + 1;
}else if(numbers[mid] < numbers[right]){
right = mid;
}else{
right --;
}
}
return numbers[left];
}
}
剑指 Offer 12. 矩阵中的路径
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true
class Solution {
public boolean exist(char[][] board, String word) {
if(board == null || board.length == 0 || board[0].length == 0){
return false;
}
char[] chars = word.toCharArray(); //转换为字符数组
boolean[][] vis = new boolean[board.length][board[0].length]; //记录是否遍历过
for(int i = 0; i < board.length; i++){
for(int j = 0;j < board[0].length; j++){
// 从(0,0)开始进行dfs
// 如果以(0,0)点没有对应的路径,那么就从(0,1)去找
if(dfs(board,chars,vis,i,j,0)){
return true;
}
}
}
return false;
}
private boolean dfs(char[][] board, char[] chars, boolean[][] vis, int i, int j, int start){
if(i < 0 || i >= board.length || j < 0 || j >= board[0].length || chars[start] != board[i][j] || vis[i][j]){
return false;
}
if(start == chars.length - 1){
return true;
}
vis[i][j] = true;
boolean ans = false;
ans = dfs(board,chars,vis,i + 1,j,start + 1) || dfs(board,chars,vis,i - 1,j,start + 1) || dfs(board,chars,vis,i,j + 1,start + 1) || dfs(board,chars,vis,i,j - 1,start + 1);
vis[i][j] = false;
return ans;
}
}
剑指 Offer 13. 机器人的运动范围
地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?
示例 1:
输入:m = 2, n = 3, k = 1 输出:3
class Solution {
public int movingCount(int m, int n, int k) {
//深搜
//记录是否走过
boolean[][] vis = new boolean[m][n];
return dfs(0, 0, m, n, k, vis);
}
private int dfs(int x, int y, int m, int n, int k, boolean[][] vis){
if(x < 0 || y < 0|| x > m - 1 || y > n - 1 || x / 10 + x % 10 + y / 10 + y % 10 > k || vis[x][y]){
return 0;
}
vis[x][y] =true;
return dfs(x - 1, y, m, n, k, vis) + dfs(x + 1, y, m, n, k, vis) + dfs(x, y + 1, m, n, k, vis)+ dfs(x, y - 1, m, n, k,vis) + 1;
}
}
剑指 Offer 14- I. 剪绳子
给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m-1] 。请问 k[0]*k[1]*...*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
示例 1:
输入: 10 输出: 36 解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36
提示:
2 <= n <= 58
//动态规划
class Solution {
public int cuttingRope(int n) {
// n>3时,所有数都可以是2、3的组合后乘积,max(dp[n-x]*dp[x]) dp[n] 实现最大值的动态转移
if(n < 2){
return 1;
}
if(n == 2){
return 1;
}
if(n == 3){
return 2;
}
int[] dp = new int[n + 1];
// >=4的值,2、3不切分
dp[1] = 1;
dp[2] = 2;
dp[3] = 3;
for(int i = 4; i <= n; i++){
for(int j =1; j <= i/2; j++){ //j是在哪里分割
dp[i] = Math.max(dp[j] * dp[i - j], dp[i]);
}
}
return dp[n];
}
}
剑指 Offer 14- II. 剪绳子 II
给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m - 1] 。请问 k[0]*k[1]*...*k[m - 1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
示例 2:
输入: 10 输出: 36 解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36
提示:
2 <= n <= 1000
// 贪心算法
// 3越多,乘积越大
class Solution {
public int cuttingRope(int n) {
if(n == 2){
return 1;
}
if(n == 3){
return 2;
}
if(n == 4){
return 4;
}
long res = 1;
while(n > 4){
res *= 3;
res = res % 1000000007;
n -= 3;
}
return (int)(res * n % 1000000007);
}
}
剑指 Offer 15. 二进制中1的个数
请实现一个函数,输入一个整数(以二进制串形式),输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。
示例 1:
输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。
// 与运算,int是32位
public class Solution {
// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int res = 0;
for(int i = 0; i < 32; i++){
res += n & 1;
n = n >>> 1;
}
return res;
}
}
剑指 Offer 16. 数值的整数次方
实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,同时不需要考虑大数问题。
示例 1:
输入:x = 2.00000, n = 10
输出:1024.00000
class Solution {
//快速幂
//例如:求a^11
//11 --> 1011
//11 = 1*2^3+0*2^2+1*2^1+1*2^0;
//a^11 = a^(2^0+2^1+2^3)
//不用再遍历11次,而是遍历3次就可以
public double myPow(double x, int n) {
long b = n;
if(n < 0){
b = -b;
x = 1/x;
}
double res = 1.0;
while(b != 0){ //1011 = 2^0+2^1+2^3
if((b & 1) == 1){
res *= x; // res = a, a*a^2, a*a^2, a*a^2*a^8
}
b >>= 1; // 0101, 0010, 0001, 0000
x*=x; // x = a^2, a^4, a^8, a^16
}
return res; //a*a^2*a^8
}
}
剑指 Offer 17. 打印从1到最大的n位数
输入数字 n
,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。
示例 1:
输入: n = 1 输出: [1,2,3,4,5,6,7,8,9]
class Solution {
public int[] printNumbers(int n) {
//求10^n - 1的值
//快速幂
int x = 10; //底数
int res = 1;
while(n != 0){
if((n & 1) == 1){
res *= x;
}
x *= x;
n >>= 1;
}
int[] resArr = new int[res - 1];
for(int i = 0; i < resArr.length; i++){
resArr[i] = i + 1;
}
return resArr;
}
}
剑指 Offer 18. 删除链表的节点
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。返回删除后的链表的头节点。
示例 1:
输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode deleteNode(ListNode head, int val) {
//如果头结点就是要找的值,那么直接摘链就可以了
if(head.val == val){
head = head.next;
}
//否则
ListNode temp = head;
ListNode pre = null;
while(temp != null){
if(temp.val == val){
pre.next = temp.next; // 摘链
}
pre = temp;
temp = temp.next;
}
return head;
}
}
剑指 Offer 19. 正则表达式匹配
请实现一个函数用来匹配包含'. '和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但与"aa.a"和"ab*a"均不匹配。
示例 1:
输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。示例 2:
输入:
s = "aa"
p = "a*"
输出: true
解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。
示例 3:输入:
s = "ab"
p = ".*"
输出: true
解释: ".*" 表示可匹配零个或多个('*')任意字符('.')。
示例 4:输入:
s = "aab"
p = "c*a*b"
输出: true
解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。
示例 5:输入:
s = "mississippi"
p = "mis*is*p*."
输出: false
s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母以及字符 . 和 *,无连续的 '*'。
//方法一:递归
class Solution {
public boolean isMatch(String s, String p) { //s为字符串,p为正则表达式
if(p.isEmpty()){ //如果正则表达式为空
return s.isEmpty(); //s也为空,则匹配成功
}
//记录第一位是否相等
boolean headMatch = !s.isEmpty() && (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.');
//如果下一个节点是“*”
//s = ac*a; p = b*ac*a; p = ac*a
//s = bb*ac*a; p = b*ac*a; s = b*ac*a;
if(p.length() >= 2 && p.charAt(1) == '*'){
// 截掉p || 截掉s
return isMatch(s,p.substring(2)) || (headMatch && isMatch(s.substring(1),p));
}else if(headMatch){
return isMatch(s.substring(1),p.substring(1));
}else{
return false;
}
}
}
//方法二:动态规划
class Solution {
int[][] dp; //dp[i][j] s.substring(i) p.substring(j) 是否匹配,匹配1,不匹配0
String s;
String p;
int n,m; // n代表s的长度,m代表p的长度
public boolean isMatch(String s, String p) { //s为字符串,p为正则表达式
n = s.length();
m = p.length();
this.s = s;
this.p = p;
dp = new int[n + 1][m + 1];
for(int i = 0; i <= n; i++){
Arrays.fill(dp[i], -1); //先将数组值全赋值为-1
}
return solve(0,0) == 1;
}
private int solve(int i, int j){
if(i == n && j == m) return 1; // n = 0, m = 0,说明为两个空串,相等
if(j == m) return 0; // 没有完全匹配原串,p为空串
if(dp[i][j] != -1) return dp[i][j]; //说明已经匹配过了
int res = 0;
if(j + 1 < m && p.charAt(j + 1) == '*'){
res = solve(i, j + 2); // 包含0次
for(int k = i; k < n && res != 1; k++){
if(s.charAt(k) != p.charAt(j) && p.charAt(j) != '.') break;
res = solve(k + 1,j + 2);
}
}else if(i != n){
if(s.charAt(i) == p.charAt(j) || p.charAt(j) == '.'){
res = solve(i + 1,j +1 );
}
}
return dp[i][j] = res;
}
}
剑指 Offer 20. 表示数值的字符串
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。
数值(按顺序)可以分成以下几个部分:
若干空格
一个 小数 或者 整数
(可选)一个 'e' 或 'E' ,后面跟着一个 整数
若干空格
小数(按顺序)可以分成以下几个部分:
(可选)一个符号字符('+' 或 '-')
下述格式之一:
至少一位数字,后面跟着一个点 '.'
至少一位数字,后面跟着一个点 '.' ,后面再跟着至少一位数字
一个点 '.' ,后面跟着至少一位数字
整数(按顺序)可以分成以下几个部分:
(可选)一个符号字符('+' 或 '-')
至少一位数字
部分数值列举如下:
["+100", "5e2", "-123", "3.1416", "-1E-16", "0123"]
部分非数值列举如下:
["12e", "1a3.14", "1.2.3", "+-5", "12e+5.4"]
示例 1:
输入:s = "0"
输出:true
示例 2:输入:s = "e"
输出:false
示例 3:输入:s = "."
输出:false
示例 4:输入:s = " .1 "
输出:true
class Solution {
public boolean isNumber(String s) {
// .之前不能出现. 或 e
// e之前不能出现e,且必须出现数字
// +-出现在0位置或者e/E的后面第一个位置才是合法的
boolean seeNum = false;
boolean seeDot = false;
boolean seeE = false;
s = s.trim();
for(int i = 0; i < s.length(); i++){
char c = s.charAt(i);
if(c >= '0' && c <= '9'){
seeNum = true;
}else if(c == '.'){
if(seeDot || seeE){
return false;
}
seeDot = true;
}else if(c == 'e' || c == 'E'){
if(seeE || !seeNum){
return false;
}
seeE = true;
seeNum = false; // 避免出现12e这种情况
}else if(c == '-' || c == '+'){
if(i != 0 && s.charAt(i - 1) != 'e' && s.charAt(i - 1) != 'E'){
return false;
}
}else{
return false;
}
}
return seeNum;
}
}
剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。
示例:
输入:nums = [1,2,3,4] 输出:[1,3,2,4] 注:[3,1,2,4] 也是正确的答案之一。
class Solution {
public int[] exchange(int[] nums) {
//双指针
int left = 0;
int right = nums.length - 1;
while(left < right){
while(left < right && nums[left] % 2 == 1){
left++;
}
while(left < right && nums[right] % 2 == 0){
right--;
}
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
}
return nums;
}
}
剑指 Offer 22. 链表中倒数第k个节点
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。
示例:
给定一个链表: 1->2->3->4->5, 和 k = 2. 返回链表 4->5.
//head指向的是头结点 即第一个点
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
// 双指针
ListNode first = head;
ListNode second = head;
for(int i = 0; i < k; i++){
first = first.next;
}
while(first != null){
first = first.next;
second = second.next;
}
return second;
}
}
剑指 Offer 24. 反转链表
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
示例:
输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
// 单向链表
class Solution {
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode temp = curr.next; //保存下一个节点
curr.next = prev; //当前节点指向前一个节点
prev = curr; //当前为下一轮的前一个节点
curr = temp; // curr修改为下一轮的当前节点
}
return prev;
}
}
剑指 Offer 25. 合并两个排序的链表
输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
示例1:
输入:1->2->4, 1->3->4 输出:1->1->2->3->4->4
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode res = new ListNode(0);
ListNode cur = res;
while(l1 != null && l2 != null){
if(l1.val < l2.val ){
cur.next = l1;
cur = cur.next;
l1 = l1.next;
}else{
cur.next = l2;
cur = cur.next;
l2 = l2.next;
}
}
if(l1 != null){
cur.next = l1;
}else if(l2 != null){
cur.next = l2;
}
return res.next;
}
}
剑指 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
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
// 一般树的题目都是用递归解决
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
if(A == null || B == null){
return false;
}
return isSubTree(A,B) || isSubStructure(A.left,B) || isSubStructure(A.right,B);
}
private boolean isSubTree(TreeNode A, TreeNode B){
if(B == null){ //B已经遍历结束
return true;
}
if(A == null || A.val != B.val){ //A已经nullB还未匹配完;AB值不同
return false;
}
return isSubTree(A.left,B.left) && isSubTree(A.right,B.right);
}
}
剑指 Offer 27. 二叉树的镜像
请完成一个函数,输入一个二叉树,该函数输出它的镜像。
例如输入:
4
/ \
2 7
/ \ / \
1 3 6 9
镜像输出:
4
/ \
7 2
/ \ / \
9 6 3 1
示例 1:
输入:root = [4,2,7,1,3,6,9] 输出:[4,7,2,9,6,3,1]
限制:
0 <= 节点个数 <= 1000
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if(root == null){
return null;
}
TreeNode temp = root.left;
root.left = mirrorTree(root.right);
root.right = mirrorTree(temp);
return root;
}
}
剑指 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 2
\ \
3 3
示例 1:
输入:root = [1,2,2,3,4,4,3]
输出:true
示例 2:输入:root = [1,2,2,null,3,null,3]
输出:false
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null){
return true;
}
return isMirrorTree(root.left,root.right);
}
private boolean isMirrorTree(TreeNode left,TreeNode right){
if(left == null && right == null){
return true;
}
if(left == null || right == null || left.val != right.val){
return false;
}
return isMirrorTree(left.right,right.left) && isMirrorTree(left.left,right.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]
class Solution {
public int[] spiralOrder(int[][] matrix) {
if(matrix.length ==0 ){
return new int[]{}; //写成int[][]是错的!!
}
int left = 0;
int right = matrix[0].length - 1;
int top = 0;
int bottom = matrix.length -1;
int[] res = new int[(right + 1) * (bottom + 1)];
int x = 0;
while(true){
//向左
for(int i = left; i <= right; i++){
res[x] = matrix[top][i];
x++;
}
if(++top > bottom){
break;
}
//向下
for(int i = top; i <= bottom; i++){
res[x] = matrix[i][right];
x++;
}
if(--right < left){
break;
}
//向左
for(int i = right; i >= left; i--){
res[x] = matrix[bottom][i];
x++;
}
if(--bottom < top ){
break;
}
//向上
for(int i = bottom; i >= top; i--){
res[x] = matrix[i][left];
x++;
}
if(++left > right){
break;
}
}
return res;
}
}
剑指 Offer 30. 包含min函数的栈
定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。
示例:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.min(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.min(); --> 返回 -2.
class MinStack {
private Stack<Integer> a;
private Stack<Integer> b;
/** initialize your data structure here. */
public MinStack() {
a = new Stack();
b = new Stack();
}
public void push(int x) {
a.push(x);
if(b.isEmpty() || b.peek() >= x){
b.push(x);
}
}
public void pop() {
//此题如果用==将会无法通过 Integer的equals重写过,
//比较的是内部value的值, ==如果在[-128,127]会被cache缓存
//,超过这个范围则比较的是对象是否相同
if(b.peek().equals(a.pop())){
b.pop();
}
}
public int top() {
return a.peek();
}
public int min() {
return b.peek();
}
}
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(x);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.min();
*/
剑指 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
class Solution {
public boolean validateStackSequences(int[] pushed, int[] popped) {
if(pushed.length != popped.length) return false;
if(pushed.length < 1) return true; // 入栈是空,则出栈也是空
Stack<Integer> stack = new Stack();
stack.push(pushed[0]);
int i = 1;
for(int j = 0; j < popped.length; j++){
int num = popped[j];
// 一开始这里没有加stack.isEmpty()
// 输入[0,1][0,1]时报错,栈为空
while(stack.isEmpty() || (stack.peek() != num && i < pushed.length)){
stack.push(pushed[i++]);
}
if(stack.peek() == num){
stack.pop();
continue;
}
return false;
}
return true;
}
}