每日leetcode - 剑指 offer

剑指 Offer 03. 数组中重复的数字

找出数组中重复的数字。

在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

方法1:哈希表

class Solution {
    public int findRepeatNumber(int[] nums) {
        HashSet<Integer> set = new HashSet<>();
        for(int i=0;i<nums.length;i++){
            if(set.contains(nums[i])){
                return nums[i];
            }
            set.add(nums[i]);
        }
        return -1;
    }
}

方法2:ArrayList数组,超出时间限制

class Solution {
    public int findRepeatNumber(int[] nums) {
        ArrayList<Integer> list = new ArrayList<>();
        for(int i=0;i<nums.length;i++){
            if(list.contains(nums[i])){
                return nums[i];
            }
            list.add(nums[i]);
        }
        return -1;
    }
}

方法3:暴力法,超出时间限制

class Solution {
    public int findRepeatNumber(int[] nums) {
        for(int i=0;i<nums.length;i++){
            for(int j=i+1;j<nums.length;j++){
                if(nums[i]==nums[j]){
                    return nums[i];
                }
            }
        }
        return -1;
    }
}

方法4:根据题目条件,所有的数字范围都在0~n-1之间,可以让num[i]和索引i一一对应起来。

class Solution {
    public int findRepeatNumber(int[] nums) {
        int i=0;
        while (i<nums.length){
            if(nums[i]==i){
                i++;
                continue;
            }
            if(nums[i] == nums[nums[i]]) return nums[i];
            int temp = nums[i];
            nums[i] = nums[temp];
            nums[temp] = temp;
        }
        return -1;
    }
}

剑指 Offer 05. 替换空格

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

方法:遍历字符串,遇到空格就拼接%20

class Solution {
    public String replaceSpace(String s) {
        StringBuilder sb = new StringBuilder();
        for(int i=0;i<s.length();i++){
            if(s.charAt(i)==' '){
                sb.append("%20");
            }else{
                sb.append(s.charAt(i));
            }
        }
        return sb.toString();
    }
}

剑指 Offer 06. 从尾到头打印链表

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

方法1:递归

class Solution {
    ArrayList<Integer> list = new ArrayList<>();
    public int[] reversePrint(ListNode head) {
        dfs(head);
        int[] arr = new int[list.size()];
        for(int i=0;i<list.size();i++){
            arr[i] = list.get(i);
        }
        return arr;
    }

    private void dfs(ListNode root) {
        if(root==null) return;
        dfs(root.next);
        list.add(root.val);
    }
}

方法2:遍历链表并将值存放在ArrayList中

class Solution {
    ArrayList<Integer> list = new ArrayList<>();
    public int[] reversePrint(ListNode head) {
        ListNode cur = head;
        while (cur!=null){
            list.add(0,cur.val);
            cur = cur.next;
        }
        int[] arr = new int[list.size()];
        for(int i=0;i<arr.length;i++){
            arr[i] = list.get(i);
        }
        return arr;
    }
}

方法3:使用栈先进后出的特点

class Solution {
    ArrayList<Integer> list = new ArrayList<>();
    public int[] reversePrint(ListNode head) {
        Stack<Integer> stack = new Stack<>();
        ListNode cur = head;
        while (cur!=null){
            stack.push(cur.val);
            cur = cur.next;
        }
        int[] arr = new int[stack.size()];
        for(int i=0;i<arr.length;i++){
            arr[i] = stack.pop();
        }
        return arr;
    }
}

剑指 Offer 07. 重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

class Solution {
    HashMap<Integer,Integer> map = new HashMap<>();
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        int preleft = 0;
        int prerigt = preorder.length-1;
        int inleft = 0;
        int inright = inorder.length-1;
        for(int i=0;i<inorder.length;i++){
            map.put(inorder[i],i);
        }
        return rebuild(preleft,prerigt,inleft,inright,preorder);
    }

    private TreeNode rebuild(int preleft, int prerigt, int inleft, int inright, int[] preorder ) {
        if(preleft>prerigt || inleft>inright ) return null;
        int rootVal = preorder[preleft];
        TreeNode root = new TreeNode(rootVal);
        int index = map.get(rootVal);

        root.left = rebuild(preleft+1,preleft+index-inleft,inleft,index-1,preorder);
        root.right = rebuild(preleft+index-inleft+1,prerigt,index+1,inright,preorder);
        return root;
    }
}

剑指 Offer 09. 用两个栈实现队列

用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

方法:两个栈实现队列,只要stackB中有数据就弹出,只有stackB中没有数据才能将A栈数据压进去

class CQueue {
    Stack<Integer> stackA;
    Stack<Integer> stackB;

    public CQueue() {
        stackA = new Stack<>();
        stackB = new Stack<>();
    }

    public void appendTail(int value) {
        stackA.push(value);
    }

    public int deleteHead() {
        if(!stackB.isEmpty()) return stackB.pop();
        if(stackA.isEmpty()) return -1;
        while (!stackA.isEmpty()){
            stackB.push(stackA.pop());
        }
        return stackB.pop();
    }
}

剑指 Offer 10- I. 斐波那契数列

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项。斐波那契数列的定义如下:

F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

方法1:动态规划

class Solution {
    public int fib(int n) {
        if(n==0) return 0;
        if(n==1) return 1;
        if(n==2) return 1;
        int[] dp = new int[n+1];
        dp[0] = 0;
        dp[1] = 1;
        dp[2] = 1;
        for(int i=3;i<=n;i++){
            dp[i] = dp[i-1]+dp[i-2];
            dp[i] = dp[i] % 1000000007;
        }
        return dp[n];
    }
}

方法2:递归,超出时间限制

class Solution {
    public int fib(int n) {
        if(n==0) return 0;
        if(n==1) return 1;
        return (fib(n-1) +fib(n-2))% 1000000007;
    }
}

剑指 Offer 10- II. 青蛙跳台阶问题

一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

class Solution {
    public int numWays(int n) {
        //如果没有这些条件,会导致给dp赋值的时候数组越界
        if(n==0) return 1;
        if(n==1) return 1;
        if(n==2) return 2;

        int[] dp = new int[n+1];
        dp[1] = 1;
        dp[2] = 2;

        for(int i=3;i<=n;i++){
            dp[i] = dp[i-1]+dp[i-2];
            dp[i] = dp[i] % 1000000007;
        }
        return dp[n];
    }
}

剑指 Offer 11. 旋转数组的最小数字

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。

方法:二分查找

class Solution {
    public int minArray(int[] numbers) {
        if(numbers.length==0 || numbers==null) return 0;
        int start = 0;
        int end = numbers.length-1;
        while (start+1<end){
            int mid = start + (end-start)/2;
            if(numbers[mid]<numbers[end]){
                end = mid;
            }else if(numbers[mid]>numbers[end]){
                start = mid;
            }else if(numbers[mid]==numbers[end]){
                end--;
            }
        }
        if(numbers[start]<numbers[end]) return numbers[start];
        else return numbers[end];
    }
}

剑指 Offer 15. 二进制中1的个数

请实现一个函数,输入一个整数(以二进制串形式),输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2

public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        int res = 0;
        while (n!=0){
            res += n & 1;
            n = n>>>1;
        }
        return res;
    }
}

剑指 Offer 18. 删除链表的节点

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。返回删除后的链表的头节点。

方法1:

class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode cur = dummy;
        while (cur!=null && cur.next!=null){
            if(cur.next.val==val){
                cur.next = cur.next.next;
            }
            //为了删除重复节点,所以不要加else
            cur = cur.next;
        }
        return dummy.next;
    }
}

方法2:

class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode cur = dummy;
        while (true){
            if(cur.next.val==val){
                cur.next = cur.next.next;
                //为了删除重复节点,所以加break
                break;
            }
            cur = cur.next;
        }
        return dummy.next;
    }
}

剑指 Offer 21. 调整数组顺序使奇数位于偶数前面

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。

方法1:插入排序算法

class Solution {
    public int[] exchange(int[] nums) {
        if(nums.length==0 || nums==null) return new int[0];
        for(int i=1;i<nums.length;i++){
            int insertNum = nums[i];
            if(nums[i]%2==1){
                int insertIndex = i;
                while (insertIndex>0 && nums[insertIndex-1]%2==0){
                    nums[insertIndex] = nums[insertIndex-1];
                    insertIndex--;
                }
                nums[insertIndex] = insertNum;
            }
        }
        return nums;
    }
}

方法2:双指针

class Solution {
    public int[] exchange(int[] nums) {
        if(nums.length==0) return new int[0];
        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 tmp = nums[left];
            nums[left] = nums[right];
            nums[right] = tmp;
        }
        return nums;
    }
}

剑指 Offer 22. 链表中倒数第k个节点

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。

方法:快慢指针,先让快指针走k步,然后让快慢指针同时走

class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode fast = head;
        ListNode slow = head;
        for(int i=0;i<k;i++){
           fast = fast.next;
        }
        while (fast!=null){
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }
}

剑指 Offer 24. 反转链表

定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

class Solution {
    public ListNode reverseList(ListNode head) {
        if(head==null) return null;
        ListNode pre = null;
        ListNode cur = head;
        //注意是cur!=null,每个数都要cur.next=pre
        while (cur!=null){
            ListNode temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;
    }
}

剑指 Offer 25. 合并两个排序的链表

输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode(0);
        ListNode cur = dummy;
        while (l1!=null && l2!=null){
            if(l1.val<l2.val){
                cur.next = l1;
                l1 = l1.next;
            }else{
                cur.next = l2;
                l2 = l2.next;
            }
            cur = cur.next;
        }
        if(l1!=null){
            cur.next = l1;
        }else if(l2!=null){
            cur.next = l2;
        }
        return dummy.next;
    }
}

剑指 Offer 26. 树的子结构

输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)。B是A的子结构, 即 A中有出现和B相同的结构和节点值。

class Solution {
    public boolean isSubStructure(TreeNode A, TreeNode B) {
        if(B == null) return false;
        if(A == null) return false;
        return dfs(A,B) || isSubStructure(A.left,B) || isSubStructure(A.right,B);
    }

    private boolean dfs(TreeNode A, TreeNode B) {
        // 如果B==null说明B这颗树的节点比较完了,返回true
        if(B==null) return true;
        // 如果B!=null,但是A==null,说明两颗子树不相等,返回false
        if(A==null) return false;

        return A.val==B.val && dfs(A.left,B.left) && dfs(A.right,B.right);
    }
}

剑指 Offer 27. 二叉树的镜像

请完成一个函数,输入一个二叉树,该函数输出它的镜像。

class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        if(root==null) return null;
        return dfs(root);
    }

    private TreeNode dfs(TreeNode root) {
        if(root==null) return null;
        
        //递归的交换左右子树的节点
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;

        dfs(root.left);
        dfs(root.right);

        return root;
    }
}

剑指 Offer 28. 对称的二叉树

请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。

class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root==null) return true;
        return dfs(root.left,root.right);
    }

    private boolean dfs(TreeNode A, TreeNode B) {
        if(A==null && B==null) return true;
//        if(A==null && B!=null) return false;
//        if(A!=null && B==null) return false;
        if(A==null || B==null) return false;

        return A.val==B.val && dfs(A.left,B.right) && dfs(A.right,B.left);
    }
}

剑指 Offer 30. 包含min函数的栈

定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。

class MinStack {
    Stack<Integer> stackA ;
    Stack<Integer> stackB;

    /** initialize your data structure here. */
    public MinStack() {
        stackA = new Stack<>();
        stackB = new Stack<>();
    }

    public void push(int x) {
        stackA.push(x);
        if(stackB.isEmpty() || stackB.peek()>=x){
            stackB.push(x);
        }
    }

    public void pop() {
        //注意比较弹出的值相等时使用equals
        if(stackA.pop().equals(stackB.peek())){
            stackB.pop();
        }
    }

    public int top() {
        return stackA.peek();
    }

    public int min() {
        return stackB.peek();
    }
}

剑指 Offer 31. 栈的压入、弹出序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。

class Solution {
    public boolean validateStackSequences(int[] pushed, int[] popped) {
        Stack<Integer> stack = new Stack<>();
        int index = 0;
        for(int i=0;i<pushed.length;i++){
            stack.push(pushed[i]);
            while (!stack.isEmpty() && stack.peek()==popped[index] && index<popped.length){
                stack.pop();
                index++;
            }
        }
        return stack.isEmpty();
    }
}

剑指 Offer 32 - I. 从上到下打印二叉树

从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。

class Solution {
    ArrayList<Integer> list = new ArrayList<>();
    public int[] levelOrder(TreeNode root) {
        if(root==null) return new int[0];
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()){
            TreeNode node = queue.pop();
            list.add(node.val);
            if(node.left!=null) queue.add(node.left);
            if(node.right!=null) queue.add(node.right);
        }
        int[] arr = new int[list.size()];
        for(int i=0;i<arr.length;i++){
            arr[i] = list.get(i);
        }
        return arr;
    }
}

剑指 Offer 32 - II. 从上到下打印二叉树 II

从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。

class Solution {
    List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> levelOrder(TreeNode root) {
        if(root==null) return res;
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()){
            ArrayList<Integer> list = new ArrayList<>();
            int len = queue.size();
            for(int i=0;i<len;i++){
                TreeNode node = queue.pop();
                list.add(node.val);
                if(node.left!=null) queue.add(node.left);
                if(node.right!=null) queue.add(node.right);
            }
            res.add(list);
        }
        return res;
    }
}

剑指 Offer 32 - III. 从上到下打印二叉树 III

请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。

class Solution {
    List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> levelOrder(TreeNode root) {
        if(root==null) return res;
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        int level = 1;
        while (!queue.isEmpty()){
            ArrayList<Integer> list = new ArrayList<>();
            int len = queue.size();
            for(int i=0;i<len;i++){
                TreeNode node = queue.pop();
                if(level%2==1){
                    list.add(node.val);
                }else{
                    list.add(0,node.val);
                }
                if(node.left!=null) queue.add(node.left);
                if(node.right!=null) queue.add(node.right);
            }
            level++;
            res.add(list);
        }
        return res;
    }
}
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 酷酷鲨 设计师:CSDN官方博客 返回首页