剑指Offer(一)

剑指 Offer 39. 数组中出现次数超过一半的数字

算法思想:

 如果一个数组中有一个数字出现的次数,超过数组长度的一半,那么只要一次删除两个数
 最后剩下的那个数就是出现次数超过数组长度一半的数
 规定两个变量: cand 候选,HP 候选人的血量
 当HP == 0 的时候,证明没有候选人,
 去遍历数组,如果当前的HP是0 ,那就将遍历的数设为候选
 如果HP不为0 ,那么就比较,当前遍历到的数是不是和cnad 相等。
 如果不相等,当前候选人失去一个血量,
 如果相等,当前候选人增加一个血量
 当数组变量完毕,就找到了要找的数

代码实现:

public int majorityElement2(int[] nums) {
        if(nums == null){
            return 0;
        }
        if(nums.length == 1){
            return nums[0];
        }
        int cand = 0;
        int HP = 0;
        for(int i = 0; i < nums.length ; i++){
            if(HP == 0){
                cand = nums[i];
                HP++;
            }else {
                if(cand == nums[i]){
                    HP++;
                }else {
                    HP--;
                }
            }
        }
        return cand ;
    }

剑指 Offer 40. 最小的k个数

算法思想:

利用小根堆来实现

首先将数组中的元素都加入到小根堆里去

然后在小根堆中连续取出k个数

时间复杂度O(k *logN)

代码实现:

   public static int[] getLeastNumbers(int[] arr, int k) {
        int []ans = new int[k];
        if(arr == null || arr.length == 0){
            return ans;
        }
        PriorityQueue<Integer> heap = new PriorityQueue<>();
        for(int i = 0; i < arr.length ; i++){
            heap.add(arr[i]);
        }
        for(int i = 0; i < k ; i++){
            ans[i] = heap.poll();
        }
        return ans;
    }

剑指 Offer 42. 连续子数组的最大和

算法思想

在求解子数组子串这类题的时候,我们比较容易想到求解以每一个位置为结尾的时候得到结果

对于本题来说,要求解最大子数组的累加和

那么我们就创建一个dp数组,数组的每个位置的值,表示原数组中以每一个位置为结尾的时候所得到的的最大的子数组累加和

很容易可以得到 dp[0] = nums[0]

那么对于任意一个普遍位置而言,以该位置为结尾的子数组的累加和就有以下两种情况

(1)已该位置的上一个位置为结束的数组的最大累加和加上该位置本身的数

(2)就是该位置的数

这样就可以得到以每个位置为结尾的最大累加和了

那么最大的子子数组就是dp数组中的最大值

代码实现

    public int maxSubArray(int[] nums) {
        if(nums == null || nums.length == 0){
            return 0;
        }
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        int max = dp[0];
        for(int i = 1; i < nums.length ; i++){
            dp[i] = Math.max(dp[i - 1] + nums[i],nums[i]);
            max = Math.max(max,dp[i]);
        }
        return max;
    }

算法的优化

在上面的分析中,我们可以看出其实对于一个普遍位置来说,只依赖于它的前一个位置的

那么我们就可以用一个变量来替换dp数组

代码实现

    public int maxSubArray2(int[] nums) {
        if(nums == null || nums.length == 0){
            return 0;
        }
        int pre = nums[0];
        int max = pre;
        for(int i = 1; i < nums.length ; i++){
            pre = Math.max(pre+ nums[i],nums[i]);
            max = Math.max(max,pre);
        }
        return max;
    }

剑指 Offer 50. 第一个只出现一次的字符

算法思想

因为题目里说只有小写字符,那么我就可以用一个长度为26的数组来存储每个字符出现的次数

然后遍历字符串,把每个字符出现的次数储存在数组中

最后遍历字符串,过程中去数组中查询当前的字符出现的次数

如果次数为一就直接返回当前的数,否则继续遍历

如果字符串遍历完还没有找到出现次数为一的字符,就返回‘ ’

代码实现

    public char firstUniqChar(String s) {
        if(s == null || s.length()== 0){
            return ' ';
        }
        int[]count = new int[26];
        for (int i = 0; i< s.length() ; i++){
            count[s.charAt(i) - 'a']++;
        }
        for (int i = 0; i < s.length();i++){
            if(count[s.charAt(i) - 'a'] == 1){
                return s.charAt(i);
            }
        }
        return ' ';
    }

剑指 Offer 52. 两个链表的第一个公共节点

算法思想

首先求出每个链表的长度

然后重定位长短链表,也就是找出长链表和短链表

然后长链表首先走长链表的长度-短链表的长度步

再长链表和短链表同时走

初次相遇的节点,就是两个链表的相交的节点

代码实现

    ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA == null || headB == null){
            return null;
        }
        int len1 = getLen(headA);
        int len2 = getLen(headB);
        ListNode longList = len1 > len2 ? headA : headB;
        ListNode shortList = longList == headA ? headB : headA;
        int k = Math.abs(len1 - len2);
        for (int i = 0; i < k ; i++){
            longList = longList.next;
        }
        while (shortList != longList){
            shortList = shortList.next;
            longList = longList.next;
        }
        return longList;
    }
    private int getLen(ListNode head){
        ListNode moveNode = head;
        int len = 0;
        while (moveNode != null){
            moveNode = moveNode.next;
            len++;
        }
        return len ;
    }

剑指 Offer 46. 把数字翻译成字符串

算法思想

这个题可以利用递归的思想去解

首先把数字转换为数组

对于数组中的每个一字符来说,都有两种翻译的方式

第一种:自己作为一个整体进行翻译

第二种:自己和自己的下一个位置一起翻译

那么最终得到的翻译的种类就是第一种和第二种情况的和 

代码实现

  public static int translateNum(int num) {
        LinkedList<Integer> arr = new LinkedList<>();
        while (num != 0){
            arr.addFirst(num %10);
            num = num / 10;
        }
        return process(arr,0);
    }

    /**
     *递归的含义:在数组arr中,现在来到了index位置,
     * 从index位置开始,往后有多少种翻译的结果
     */
    public static int process(List<Integer>arr,int index){
        if(index >= arr.size()){//说明翻译出来一种结果
            return  1;
        }
        //第一种情况,就是当前字符单独翻译
        int p1 = process(arr,index + 1);
        //第二种情况,当前字符和下一个字符一起翻译
        int p2 = 0;
        if(index < arr.size() -1 && arr.get(index) > 0){
            if(arr.get(index) * 10 + arr.get(index + 1) < 26){
                p2 = process(arr,index +2);
            }
        }
        return p1 + p2;
    }

    public static int translateNum2(int num) {
        LinkedList<Integer> arr = new LinkedList<>();
        while (num != 0){
            arr.addFirst(num %10);
            num = num / 10;
        }
        if(arr.size() <= 1){
            return 1;
        }
        int n = arr.size();
        int[]dp = new int[n + 1];
        dp[n] = 1;
        dp[ n -1] = 1;
        for(int i = n - 2 ; i >= 0; i--){
            int p1 = dp[i + 1];
            int p2 = 0;
            if(i < n - 1 && arr.get(i) > 0){
                if(arr.get(i) * 10 + arr.get(i + 1) < 26){
                    p2 = dp[i + 2];
                }
            }
            dp[i] = p1 + p2;
        }
        return dp[0];
    }

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值