剑指offer刷题记录--java

2022年2月16日,大三开始刷剑指offer。不知道为毛,几个网站上都是从第三题开始,不懂。

目录

第三题--数组中重复的数字

描述

示例1

个人思路:

第四题--二维数组中的查找

描述

示例1

示例2

示例3

个人思路

第五题--替换空格

描述

示例1

示例2

个人思路

第六题--从尾到头打印链表

描述

示例1

示例2

个人思路


第三题--数组中重复的数字

描述

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组[2,3,1,0,2,5,3],那么对应的输出是2或者3。存在不合法的输入的话输出-1

数据范围:0 <= n <= 10000

进阶:时间复杂度O(n),空间复杂度O(n)

示例1

输入:

[2, 3, 1, 0, 2, 5, 3]

返回值:

2

说明:

2或者3都对

个人思路:

暴力法:数组排序,然后循环比较每一位数与后一位数是否相等,相等则直接返回该值。

进阶法:使用hashset,判断数字是否重复。(我没想到这方法比第一个方法还慢)

高级法:使用一个同等长度Boolean数组,循环遍历数组,判断同数组值相同的索引位置Boolean值是否为true(默认为false),若为true代表重复,则直接返回。否则表示首次出现,将该Boolean值改为true。

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param numbers int整型一维数组 
     * @return int整型
     */
    public int duplicate (int[] numbers) {

        //同等长度boolean数组,默认值为false
        boolean[] t=new boolean[numbers.length];
        
        for(int i=0;i<numbers.length;i++){
            //同数组值相同的Boolean数组索引位置的值
            if(t[numbers[i]]){
                //已经存在该值,则重复
                return numbers[i];
            }else{
                //首次出现,记录该值
                t[numbers[i]]=true;
            }
        }
        
        //无重复值,返回-1
        return -1;
    }
}

之前只能想到hashset,没想到手写代码比原生方法还快,可能hashset的优势不在这里。

第四题--二维数组中的查找

描述

在一个二维数组array中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

[

[1,2,8,9],
[2,4,9,12],
[4,7,10,13],
[6,8,11,15]

]

给定 target = 7,返回 true。

给定 target = 3,返回 false。

数据范围:矩阵的长宽满足 0 <= n,m <= 5000矩阵中的值满 0<=val<=10^9
进阶:空间复杂度 O(1) ,时间复杂度 O(n+m)

示例1

输入:        7,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]

返回值:        true

说明:        存在7,返回true

示例2

输入:        1,[[2]]

返回值:        false

示例3

输入:        3,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]

返回值:        false

说明:        不存在3,返回false

个人思路

暴力法:预估超时

个人优化思路:先判断每一行第一列值,找到一个小于目标值的最大值。然后从那一行开始遍历,若发现更大值则向上一行搜索。(最后发现这个方法有些麻烦,而且只能通过大概80%的案例,其他案例会超时)

线性搜索法:直接从左下角开始搜索,若当前值更小则向右,若更大则向上(因为左边的值小于目标值,而左边的上边的值只会更小,所以当一个值因为小于目标值被排除时。那它左,上方全部值都被排除。这样一来遍历只会向右上方进行),相同则返回。

public class Solution {
    public boolean Find(int target, int [][] array) {
        
        //获取数组长宽
        int n = array.length, m = array[0].length;

        //排除空数组
        if(n == 0 || m == 0){
            return false;
        }
        
        //获取左下角数组坐标
        int index=n-1, i=0;

        while(index>=0&&i<m){

            //获取当前位置数组值
            int t = array[index][i];

            
            if(t==target){

                //相同则返回true
                return true;
            }else if(t<target){

                //小于目标值则向右方遍历
                i++;
            }else{

                //大于目标值则向上方遍历
                index--;
            }
        }
        
        //找不到目标值
        return false;
    }
}

这道题一开始理解错了,以为只有第一列是从小到大的,后面才发现每一列都是。如果看懂了题应该可以自己写出线性搜索,所以要注意审题。

第五题--替换空格

描述

请实现一个函数,将一个字符串s中的每个空格替换成“%20”。

例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

数据范围:0≤len(s)≤1000 。保证字符串中的字符为大写英文字母、小写英文字母和空格中的一种。

进阶:时间复杂度 O(n), 空间复杂度 O(n)

示例1

输入:        "We Are Happy"

返回值:        "We%20Are%20Happy"

示例2

输入:        " "

返回值:        "%20"

个人思路

1.调用string的replace(str,str)方法直接替换

2.使用stringbuilder构造

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param s string字符串 
     * @return string字符串
     */

    /*public String replaceSpace (String s) {
        return s.replace(" ", "%20");
    }*/
    
    //这道题没啥好说的
    public String replaceSpace(String s) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == ' ')
                stringBuilder.append("%20");
            else
                stringBuilder.append(s.charAt(i));
        }
        return stringBuilder.toString();
    }
}

第六题--从尾到头打印链表

描述

输入一个链表的头节点,按链表从尾到头的顺序返回每个节点的值(用数组返回)。

如输入{1,2,3}的链表如下图:

返回一个数组为[3,2,1]

0 <= 链表长度 <= 10000

示例1

输入:        {1,2,3}

返回值:        [3,2,1]

示例2

输入:        {67,0,24,58}

返回值:        [58,24,0,67]

个人思路

1.        Collections.reverse(new ArrayList<>());

/**
*    public class ListNode {
*        int val;
*        ListNode next = null;
*
*        ListNode(int val) {
*            this.val = val;
*        }
*    }
*
*/


//1.递归写法

import java.util.*;
public class Solution {
    
    ArrayList<Integer> list = new ArrayList<>();
    
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        
       if(listNode != null){
           
            printListFromTailToHead(listNode.next);
            list.add(listNode.val);
        
       }
        return list;
        
    }
}

//2.栈
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        
        ArrayList list = new ArrayList();
        
        Stack s = new Stack();
        
        //全部进栈
        while(listNode != null){
            s.push(listNode.val);
            listNode = listNode.next;
        }

        //全部出栈,顺序翻转        
        while(!s.empty()){
            list.add(s.pop());
        }
        
        return list;
        
}

//3.list集合每一次插入都插入在第一位
public class Solution {

    public ArrayList printListFromTailToHead(ListNode listNode) {

        ArrayList list = new ArrayList();

        while (listNode != null) {
            
            //每一次都插入到一位,自然会翻转
            list.add(0, listNode.val);
            listNode = listNode.next;
        }
        return list;
    }
}

这道题目这么多解法我居然都没想到。

第七题--重建二叉树

描述

给定节点数为 n 的二叉树的前序遍历和中序遍历结果,请重建出该二叉树并返回它的头结点。

例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出如下图所示。

提示:

1.vin.length == pre.length

2.pre 和 vin 均无重复元素

3.vin出现的元素均出现在 pre里

4.只需要返回根结点,系统会自动输出整颗树做答案对比

数据范围:n≤2000,节点的值 −10000≤val≤10000

要求:空间复杂度O(n),时间复杂度 O(n)

示例1

输入:[1,2,4,7,3,5,6,8],[4,7,2,1,5,3,8,6]

返回值:{1,2,3,4,#,5,6,#,7,#,#,8}

说明:返回根节点,系统会输出整颗二叉树对比结果,重建结果如题面图示

示例2

输入:[1],[1]

返回值:{1}

示例3

输入:[1,2,3,4,5,6,7],[3,2,4,1,6,5,7]

返回值:{1,2,5,3,4,6,7}

import java.util.*;
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] vin) {
        
        if(pre.length == 0 || vin.length == 0){
            return null;
        }
        
        //找到根节点
        TreeNode tree = new TreeNode(pre[0]);
        
        //在中序中找根节点
        for(int i=0;i<vin.length;i++){
            //找到根节点(数字不重复)
            if(vin[i] == pre[0]){
                //递归
                tree.left = reConstructBinaryTree(Arrays.copyOfRange(pre, 1, i+1), Arrays.copyOfRange(vin, 0, i));
                tree.right = reConstructBinaryTree(Arrays.copyOfRange(pre, i+1, pre.length), Arrays.copyOfRange(vin, i+1, vin.length));
                break;
            }
            
        }
        
        return tree;
        
    }
}

这道题目一开始就想到了递归,可是不知道具体的写法,看了大佬的,真简单,但是怕是记不住。

第九题--用两个栈实现队列

描述

用两个栈来实现一个队列,使用n个元素来完成 n 次在队列尾部插入整数(push)和n次在队列头部删除整数(pop)的功能。 队列中的元素为int类型。保证操作合法,即保证pop操作时队列内已有元素。

数据范围:n≤1000

要求:存储n个元素的空间复杂度为 O(n)O(n) ,插入与删除的时间复杂度都是 O(1)O(1)

示例1

输入:["PSH1","PSH2","POP","POP"]

返回值:1,2

说明:"PSH1":代表将1插入队列尾部 "PSH2":代表将2插入队列尾部 "POP“:代表删除一个元素,先进先出=>返回1 "POP“:代表删除一个元素,先进先出=>返回2

示例2

输入:["PSH2","POP","PSH1","POP"]

返回值:2,1

import java.util.Stack;

public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
    
    public void push(int node) {
        stack1.push(node);
    }
    
    public int pop() {
        //这个判空很重要,非空意味着还有前面还有未出队数据,所以初始是空的时候再一次性全进
        if(stack2.empty()){
            while(!stack1.empty()){
                stack2.push(stack1.pop());
            }
        }
        
        return stack2.pop();
    }
}

这道题一开始就想到了写法,但是没有在pop中对satck2判空,导致部分出错。

第十二题--矩阵中的路径

描述

请设计一个函数,用来判断在一个n乘m的矩阵中是否存在一条包含某长度为len的字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如 

矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

数据范围:0≤n,m≤20 ,1≤len≤25 

进阶:时间复杂度O(n^2) ,空间复杂度O(n^2 )

示例1

输入:[[a,b,c,e],[s,f,c,s],[a,d,e,e]],"abcced"

返回值:true

示例2

输入:[[a,b,c,e],[s,f,c,s],[a,d,e,e]],"abcb"

返回值:false

备注:

0 <= matrix.length <= 200

0 <= matrix[i].length <= 200

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param matrix char字符型二维数组 
     * @param word string字符串 
     * @return bool布尔型
     */
    public boolean hasPath (char[][] matrix, String word) {
        // write code here
        char[] chars = word.toCharArray();
        
        for(int i=0;i<matrix.length;i++){
            for(int j=0;j<matrix[0].length;j++){
                //有一个成立则全部成立
                if(dfs(matrix, chars, i, j, 0)){
                    return true;
                }
            }
        }
        //找不到成立的则不成立
        return false;
        
    }
    
    private boolean dfs (char[][] matrix, char[] word, int i, int j, int len){
        
        //边界 或者 字符不对则停止递归
        if(i<0||j<0||i>=matrix.length||j>=matrix[0].length||matrix[i][j]!=word[len]){
            return false;
        }
        
        //长度达到要求字符串
        if(len == word.length-1){
            return true;
        }
        
        //记录坐标值
        char tmp = matrix[i][j];
        //标记为已经走过
        matrix[i][j] = '.';
        
        //判断四周
        boolean res = dfs(matrix, word, i+1,j,len+1) ||
            dfs(matrix, word, i,j+1,len+1) ||
            dfs(matrix, word, i-1,j,len+1) ||
            dfs(matrix, word, i,j-1,len+1);
        //回复坐标
        matrix[i][j] = tmp;
        
        //返回结果
        return res;
        
    }
}

这道题目的方法一开始就想到了,但是搜索的细节没想到,看的解析。

第二十一题--调整数组顺序使奇数位于偶数前面(一)

描述

输入一个长度为 n 整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前面部分,所有的偶数位于数组的后面部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

数据范围:0≤n≤5000,数组中每个数的值 0≤val≤10000

要求:时间复杂度 O(n),空间复杂度 O(n)

进阶:时间复杂度 O(n^2),空间复杂度 O(1)

示例1

输入:[1,2,3,4]

返回值:[1,3,2,4]

示例2

输入:[2,4,6,5,7]

返回值:[5,7,2,4,6]

示例3

输入:[1,3,5,6,7]

返回值:[1,3,5,7,6]

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param array int整型一维数组 
     * @return int整型一维数组
     */
    public int[] reOrderArray (int[] array) {
        //先准备两数组记录奇数和偶数
        int[] answer = new int[array.length], tmp = new int[array.length];
        //s和t分别记录奇数和偶数数量
        int len = array.length, s = 0, t=0;
        //分别将奇数和偶数按顺序存入对应数组
        for(int i=0;i<len;i++){
            if(array[i]%2==1){
                answer[s++] = array[i];
            }else{
                tmp[t++] = array[i];
            }
        }
        
        //将偶数数组接到奇数数组后
        for(int i=0;i<t;i++){
            answer[s++] = tmp[i];
        }
        return answer;
    }
    
    public int[] reOrderArray3 (int[] array) {
       //所给数组的长度
        int len = array.length;
        //辅助数组
        int [] nums = new int[len];
        //双指针:left right并初始化
        int left=0;
        int right = len-1;
        int tp_left=left;
        int tp_right = right;
        //循环条件:left<len && right>=0
        while(left<len && right>=0){
            //处理奇数情况
            if(array[left] % 2==1){
                nums[tp_left] = array[left];
                tp_left++;
            }
            left++;
            //处理偶数情况
            if(array[right] % 2==0){
                nums[tp_right] = array[right];
                tp_right--;
            }
            //向左移动指针right
            right--;
        }
        return nums;
    }

}

两种方法都是自己想出来的,但是那个双指针脑子抽了一下,没有想到还可以用数组辅助指针,因为数组长度是固定的,所以可以从最后面开始插入,又学到了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值