剑指offer

剑指offer,第1张

16.实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。


不得使用库函数,同时不需要考虑大数问题。


示例 1:

输入:x = 2.00000, n = 10
输出:1024.00000
示例 2:

输入:x = 2.10000, n = 3
输出:9.26100
示例 3:

输入:x = 2.00000, n = -2
输出:0.25000
解释:2-2 = 1/22 = 1/4 = 0.25
 

提示:

-100.0 < x < 100.0
-231 <= n <= 231-1
-104 <= xn <= 104

先来一个经典超时

class Solution {
    public double myPow(double x, int n) {
        double a=x;
        long b=n;
        if(a==0){return 0;}
        if(b<0){
            a=(double)(1/a);
            b=-b;
        }
        double res=1.0;
        while(b>0){
            res*=a;
            b--;
        }
        return res;
    }
}

 现在时间复杂度是n,快速幂法可以降低到logn;(自己想到的,嘿嘿)

class Solution {
    public double myPow(double x, int n) {
        if(x == 0) return 0;
        long b = n;
        double res = 1.0;
        if(b < 0) {
            x = 1 / x;
            b = -b;
        }
        while(b > 0) {
            if(b%2==1){
                res*=x;
                b--;
            }else{
                x*=x;
                b=b/2;
            }
        }
        return res;
    }
}

7.输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。


假设输入的前序遍历和中序遍历的结果中都不含重复的数字。


示例 1:


Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]
示例 2:

Input: preorder = [-1], inorder = [-1]
Output: [-1]
 

限制:

0 <= 节点个数 <= 5000

想法是递归 

class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        //递归一下
        //前序应该是根左右,中序应该是左根右
        if(preorder.length==0){return null;}
        TreeNode root=new TreeNode(preorder[0]);
        //将inorder按preorder的值分成左右两个子数组,分别是左子树的中序遍历和右子树的中序遍历
        //通过inorder得到的数组长度分割inorder,分别为左子树的前序遍历和右子树的前序遍历
        int k=0;
        for(int i=0;i

很慢。




怎么优化一下呢?检索很慢,考虑哈希映射,快速定位根节点


(get比遍历快)

class Solution {
    private Map indexMap;
    public TreeNode myBuildTree(int[] preorder, int[] inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
        if (preorder_left > preorder_right) {
            return null;
        }

        int preorder_root = preorder_left;
        // 在中序遍历中定位根节点
        int inorder_root = indexMap.get(preorder[preorder_root]);
        TreeNode root = new TreeNode(preorder[preorder_root]);
        int size_left_subtree = inorder_root - inorder_left;
        root.left = myBuildTree(preorder, inorder, preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1);
        root.right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right);
        return root;
    }

    public TreeNode buildTree(int[] preorder, int[] inorder) {
        int n = preorder.length;
        indexMap = new HashMap();
        for (int i = 0; i < n; i++) {
            indexMap.put(inorder[i], i);
        }
        return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
    }
}

答案的迭代法:

关于前序遍历数组里两个邻近的数,a和b,有三种情况:(判断方法是前序和中序数组比较)

1.b是a的左儿子(前存储ab,中存储ba)

2.a没有左儿子

        2.1a有右儿子———b是a的右儿子(前中都存储的ab)

        2.2a没有右儿子——b是a的某个祖先的右儿子(前存储ab,中存储a一堆祖先b)

class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
    if (preorder.length == 0)
        return null;
    Stack s = new Stack<>();
    TreeNode root = new TreeNode(preorder[0]);
    TreeNode cur = root;
    //前序的第一个已经拿到了,接下来一个一个拿
    for (int i = 1, j = 0; i < preorder.length; i++) {
        //符合ab ba,直接a的左指向b
        if (cur.val != inorder[j]) {
            cur.left = new TreeNode(preorder[i]);
            s.push(cur);
            cur = cur.left;
        } else {
            //不符合即a没有左儿子,移动一下j,如果b是a的右儿子,直接指向,否则去找祖先,让祖先的右指向b
            j++;
            //s存储祖先,找到b的祖先,没找到就继续j++;
            while (!s.empty() && s.peek().val == inorder[j]) {
                cur = s.pop();
                j++;
            }
            cur = cur.right = new TreeNode(preorder[i]);
        }
    }
    return root;
   }
}

17.输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。


比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。


示例 1:

输入: n = 1
输出: [1,2,3,4,5,6,7,8,9]
 

说明:

用返回一个整数列表来代替打印
n 为正整数

那必然是阴间遍历啊

class Solution {
    public int[] printNumbers(int n) {
        int length=(int)Math.pow(10,n);
        int[] res=new int[length-1];
        for(int i=0;i

 假设考虑大数溢出?-转成字符串类型。


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


返回删除后的链表的头节点。


注意:此题对比原题有改动

示例 1:

输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
示例 2:

输入: head = [4,5,1,9], val = 1
输出: [4,5,9]
解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.
 

说明:

题目保证链表中节点的值互不相同
若使用 C 或 C++ 语言,你不需要 free 或 delete 被删除的节点

 

class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        ListNode temp=head;
        if(temp.val==val){
            head=head.next;
        }
        while(temp!=null){
            if(temp.next!=null&&temp.next.val==val){
            temp.next=temp.next.next;
            break;
        }else{
            temp=temp.next;
        }
        }
        return head;
    }
}

22.输入一个链表,输出该链表中倒数第k个节点。


为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。


例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。


这个链表的倒数第 3 个节点是值为 4 的节点。


示例:

给定一个链表: 1->2->3->4->5, 和 k = 2.

返回链表 4->5.

 1.顺序查找

2.双指针间隔k

3.反转链表求第k个

4.栈

class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        //栈搞一哈
        Stack stack=new Stack<>();
        ListNode temp=head;
        while(temp!=null){
            stack.push(temp);
            temp=temp.next;
        }
        for(int i=1;i

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


示例:

输入:nums = [1,2,3,4]
输出:[1,3,2,4] 
注:[3,1,2,4] 也是正确的答案之一。



 

提示:

0 <= nums.length <= 50000
0 <= nums[i] <= 10000

 双指针搞一哈

class Solution {
    public int[] exchange(int[] nums) {
        //双指针      
        int length=nums.length;
        if(length==0){
            return nums;
        }
        int right=length-1;
        int left=0;
        while(left=right){
                //已经排好了
                return nums;
            }
            //交换
            int temp=nums[left];
            nums[left]=nums[right];
            nums[right]=temp;
        }
        return nums;
    }
}

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


示例1:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
限制:

0 <= 链表长度 <= 1000

1.递归法

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        //递归法搞一哈
        if(l1==null){
            return l2;
        }else if(l2==null){
            return l1;
        }else if(l1.val<=l2.val){
            l1.next=mergeTwoLists(l1.next,l2);
            return l1;
        }else{
            l2.next=mergeTwoLists(l1,l2.next);
            return l2;
        }
    }
}

2.双指针

42.输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。


求所有子数组的和的最大值。


要求时间复杂度为O(n)。


示例1:

输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。



 

提示:

1 <= arr.length <= 10^5
-100 <= arr[i] <= 100

 改变数组

class Solution {
    public int maxSubArray(int[] nums) {
        //改变数组
        //每次存储与前一个数的和、当前数中的最大值。


int max=nums[0]; for(int i=1;imax){ max=nums[i]; } } return max; } }

动态规划

class Solution {
    public int maxSubArray(int[] nums) {
        //动态规划一下子
        int pre=0;
        int max=nums[0];
        for(int i=0;i

答案有个分治算法,整挺好。


class Solution {
    public class Status {
        public int lSum, rSum, mSum, iSum;

        public Status(int lSum, int rSum, int mSum, int iSum) {
            this.lSum = lSum;
            this.rSum = rSum;
            this.mSum = mSum;
            this.iSum = iSum;
        }
    }

    public int maxSubArray(int[] nums) {
        return getInfo(nums, 0, nums.length - 1).mSum;
    }

    public Status getInfo(int[] a, int l, int r) {
        if (l == r) {
            return new Status(a[l], a[l], a[l], a[l]);
        }
        int m = (l + r) >> 1;
        Status lSub = getInfo(a, l, m);
        Status rSub = getInfo(a, m + 1, r);
        return pushUp(lSub, rSub);
    }

    public Status pushUp(Status l, Status r) {
        int iSum = l.iSum + r.iSum;
        int lSum = Math.max(l.lSum, l.iSum + r.lSum);
        int rSum = Math.max(r.rSum, r.iSum + l.rSum);
        int mSum = Math.max(Math.max(l.mSum, r.mSum), l.rSum + r.lSum);
        return new Status(lSum, rSum, mSum, iSum);
    }
}

39.数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。


你可以假设数组是非空的,并且给定的数组总是存在多数元素。


示例 1:

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

限制:

1 <= 数组长度 <= 50000

1.排序

class Solution {
    public int majorityElement(int[] nums) {
        Arrays.sort(nums);
        return nums[nums.length/2];
    }
}

不改变数组:

2.hashMap

3.摩尔投票

class Solution {
    public int majorityElement(int[] nums) {
        //投票
        int number=0;
        int count=0;
        for(int i=0;i

4.分治

class Solution {
    public int majorityElement(int[] nums) {
        //分治
        int k=merge(nums,0,nums.length-1);
        return k;
    }
    //计算从left到right目标数出现的次数
    public int countLeftNumber(int[] nums,int target,int left,int right){
        int count=0;
        for(int i=left;i<=right;i++){
            if(nums[i]==target){
                count++;
            }
        }
        return count;
    }
    //分而治之
    public int merge(int[] nums,int left,int right){
        if(left==right){
            return nums[left];
        }
        int mid=(left+right)/2;
        int leftNumber=merge(nums,left,mid);
        int rightNumber=merge(nums,mid+1,right);
        if(leftNumber==rightNumber){
            return leftNumber;
        }
        //计算左众数多还是右众数多,谁多听谁的
        int l=countLeftNumber(nums,leftNumber,left,right);
        int r=countLeftNumber(nums,rightNumber,left,right);
        return l>r?leftNumber:rightNumber;
    }
}

5.随机挑选一个数判断是不是众数

40.输入整数数组 arr ,找出其中最小的 k 个数。


例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。


示例 1:

输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]
示例 2:

输入:arr = [0,1,2,1], k = 1
输出:[0]
 

限制:

0 <= k <= arr.length <= 10000
0 <= arr[i] <= 10000

无脑排序 

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        Arrays.sort(arr);
        int[] res=new int[k];
        for(int i=0;i

 答案自建堆:用一个大根堆维护数组的前k个小值,然后从k+1开始遍历,如果遍历到的数比堆顶的数小,就d出堆顶的数,插入当前数,最后将大根堆里的数存入数组返回。


class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        //堆排
        int[] res=new int[k];
        if(k==0){
            return res;
        }
        //创建一个优先队列-难点
        PriorityQueue queue = new PriorityQueue(
            //匿名内部类
            new Comparator() {
            public int compare(Integer num1, Integer num2) {
                return num2 - num1;
            }
        });
        for(int i=0;i 

快排:

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        //快排
       quick(arr,0,arr.length-1);
       int[] r=new int[k];
       for(int i=0;ipivot){
                r--;
            }

            //直到左指针大于等于右指针才结束
            if(l>=r){
                break;
            }
            temp=arr[l];
            arr[l]=arr[r];
            arr[r]=temp;
            //说明pivot已经被换到左边了,所以右指针要左移
            if(arr[l]==pivot){
                r--;
            }
            //说明此时,pivot已经被换到右边了,所以左指针要继续右移
            if(arr[r]==pivot){
                l++;
            }

        }
        //如果左边等于右边,可能会锁死循环
        if (r==l){
            r--;
            l++;
        }
        //
        if(leftl){
            quick(arr,l,right);
        }
    }
}

答案给的快排就是比尚硅谷这个快:9ms和2ms的区别

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        randomizedSelected(arr, 0, arr.length - 1, k);
        int[] vec = new int[k];
        for (int i = 0; i < k; ++i) {
            vec[i] = arr[i];
        }
        return vec;
    }

    private void randomizedSelected(int[] arr, int l, int r, int k) {
        if (l >= r) {
            return;
        }
        int pos = randomizedPartition(arr, l, r);
        int num = pos - l + 1;
        if (k == num) {
            return;
        } else if (k < num) {
            randomizedSelected(arr, l, pos - 1, k);
        } else {
            randomizedSelected(arr, pos + 1, r, k - num);
        }
    }

    // 基于随机的划分
    private int randomizedPartition(int[] nums, int l, int r) {
        int i = new Random().nextInt(r - l + 1) + l;
        swap(nums, r, i);
        return partition(nums, l, r);
    }

    private int partition(int[] nums, int l, int r) {
        int pivot = nums[r];
        int i = l - 1;
        for (int j = l; j <= r - 1; ++j) {
            if (nums[j] <= pivot) {
                i = i + 1;
                swap(nums, i, j);
            }
        }
        swap(nums, i + 1, r);
        return i + 1;
    }

    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

答案二叉搜索树:

 

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        if (k == 0 || arr.length == 0) {
            return new int[0];
        }
        TreeMap map = new TreeMap<>();
        int cnt = 0;
        for (int num: arr) {
            if (cnt < k) {
                map.put(num, map.getOrDefault(num, 0) + 1);
                cnt++;
                continue;
            } 
            Map.Entry entry = map.lastEntry();
            if (entry.getKey() > num) {
                map.put(num, map.getOrDefault(num, 0) + 1);
                if (entry.getValue() == 1) {
                    map.pollLastEntry();
                } else {
                    map.put(entry.getKey(), entry.getValue() - 1);
                }
            }
            
        }
        int[] res = new int[k];
        int idx = 0;
        for (Map.Entry entry: map.entrySet()) {
            int freq = entry.getValue();
            while (freq-- > 0) {
                res[idx++] = entry.getKey();
            }
        }
        return res;
    }
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/577591.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-11
下一篇 2022-04-11

发表评论

登录后才能评论

评论列表(0条)