算法准备-4.15

本文介绍了如何使用摩尔投票法找出数组中超过一半的数字,并探讨了最小的k个数和连续子数组最大和的算法解决方案。内容包括摩尔投票法的原理和应用,以及快速选择和动态规划在解决数组问题中的作用。
摘要由CSDN通过智能技术生成

算法准备-4.15

1. 数组中超过一半的数字(摩尔投票法)

  1. 描述:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字

  2. 未解思路:此题采用分治算法,在原数组中超过一半的数字,在等分的左右两数组中至少有一个中也超过了一半,而且因为是超过一半,所以两个等分数组中都存在此元素

  3. 新学思路:摩尔投票法:摩尔投票法基于当一个数的重复次数超过数组长度的一半,每次将两个不相同的元素删除,最终剩下的就是要找的数。用votes表示票数,初始化为0,当votes为0时,将当前元素设为众数,遍历数组,与其相等的,votes加一,否则减一。

  4. 题解:

    class Solution {
        public int majorityElement(int[] nums) {
            int x=0;
            int votes=0;
            for(int i=0;i<nums.length;i++)
            {
                if(votes==0)
                {
                    x=nums[i];
                }
                votes+=x==nums[i]?1:-1;
            }
            return x;
        }
    }
    
  5. 扩展:此题还有一种比较常见的解法就是先对数组进行排序,得到新数组的中位数即为众数

    class Solution {
        public int majorityElement(int[] nums) {
            sort(nums);
            int mid=(nums.length-1)/2;
            return nums[mid];
        }
        public void sort(int[] nums){
            for(int i=0;i<nums.length;i++)
            {
                for(int j=0;j<nums.length-i-1;j++)
                {
                    if(nums[j+1]<nums[j])
                    {
                        int temp=nums[j];
                        nums[j]=nums[j+1];
                        nums[j+1]=temp;
                    }
                }
            }
        }
    }
    

2. 最小的k个数

  1. 描述:输入n个整数,找出其中最小的k个数

  2. 思路1:比较普通,就手撕一遍快速排序,然后选择前k个数字就可以了

  3. 题解1:

    class Solution {
        public int[] getLeastNumbers(int[] arr, int k) {
            quicksort(arr,0,arr.length-1);
            return Arrays.copyOf(arr,k);
        }
        public void quicksort(int[] arr,int low,int high){
            if(low>high) return;
            int i,j,temp;
            i=low;
            j=high;
            temp=arr[low];
            while(i<j)
            {
                while(temp<=arr[j]&&i<j)
                {
                    j--;
                }
                while(temp>=arr[i]&&i<j)
                {
                    i++;
                }
                if(i<j)
                {
                    int m=arr[i];
                    arr[i]=arr[j];
                    arr[j]=m;
                }
            }
            arr[low]=arr[i];
            arr[i]=temp;
            quicksort(arr,low,i-1);
            quicksort(arr,i+1,high);
        }
    }
    

    优化:此题可以借鉴快排的划分思想,做快速选择,在每次切分的时候只处理一边,比较返回的位数与k-1的大小,区别就在于最后一部分

    class Solution {
        public int[] getLeastNumbers(int[] arr, int k) {
            quicksort(arr,0,arr.length-1,k);
            return Arrays.copyOf(arr,k);
        }
        public void quicksort(int[] arr,int low,int high,int k){
            if(low>high) return;
            int i,j,temp;
            i=low;
            j=high;
            temp=arr[low];
            while(i<j)
            {
                while(temp<=arr[j]&&i<j)
                {
                    j--;
                }
                while(temp>=arr[i]&&i<j)
                {
                    i++;
                }
                if(i<j)
                {
                    int m=arr[i];
                    arr[i]=arr[j];
                    arr[j]=m;
                }
            }
            arr[low]=arr[i];
            arr[i]=temp;
            if(i==k-1)
            {
                return;
            }
            if(i<k-1)
            {
                quicksort(arr,i+1,high,k);
            }
            else
            {
                quicksort(arr,low,i-1,k);
            }
        }
    }
    

3. 连续子数组的最大和

  1. 描述:输入一个整型数组,数组里有正数也有负数。数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。

  2. 思路:这是分治算法的典型,最大连续子数组有三种情况,存在于其左半区间,存在于其右半区间,两半区间都有,分别找出三种情况中的最大值在进行比较即可

  3. 题解:

    class Solution {
        int MIN=-100000;
        public int maxSubArray(int[] nums) {
            return findarray(nums,0,nums.length-1);
        }
        public int findarray(int[] nums,int low,int high){//递归寻找左右两端的最大子数组
            if(low==high) return nums[low];
            int mid=(low+high)/2;
            int leftsum=findarray(nums,low,mid);
            int rightsum=findarray(nums,mid+1,high);
            int crosssum=findcrossarray(nums,low,high);
            return max(leftsum,rightsum,crosssum);
        }
        public int findcrossarray(int[] nums,int low,int high){//寻找跨越中点的最大子数组
            int leftsum=MIN;
            int rightsum=MIN;
            int sum1=0;
            int sum2=0;
            int mid=(low+high)/2;
            for(int i=mid;i>=low;i--)//找出左端的最大值
            {
                sum1+=nums[i];
                if(sum1>leftsum)
                {
                    leftsum=sum1;
                }
            }
            for(int j=mid+1;j<=high;j++)//找出右端的最大值
            {
                sum2+=nums[j];
                if(sum2>rightsum)
                {
                    rightsum=sum2;
                }
            }
            return leftsum+rightsum;
        }
        public int max(int a,int b,int c){
            if(a>=b&&a>=c)
            {
                return a;
            }
            if(b>=a&&b>=c)
            {
                return b;
            }
            return c;
        }
    }
    

    但是这题分治算法的时间复杂度超出了题目的要求,分治算法的时间复杂度为O(nlgn),下面的动态规划是我参考评论区大佬的算法

  4. 动态规划思路:设动态规划列表dp,dp[i]代表以元素nums[i]为结尾的连续子数组最大和;转移时:若dp[i-1]<=0,说明dp[i-1]对dp[i]产生了负贡献,即dp[i-1]+nums[i]还不如nums[i]本身

  5. 动态规划题解:

    class Solution {
        public int maxSubArray(int[] nums) {
            int res = nums[0];
            for(int i = 1; i < nums.length; i++) {
                nums[i] += Math.max(nums[i - 1], 0);
                res = Math.max(res, nums[i]);
            }
            return res;
        }
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值