算法之数组题型总结

1.寻找最小的K个数

解法一:使用堆来代替

把维护K个元素的数组用最大堆来进行替代。时间变为O(NlogK)!

解法二:快排划分元素

分割元素确定了左边和右边的元素数量,左边的个数大于k那么前k个元素就在左数组;左边的个数小于k那么只需要在右边寻找

K-m-1个元素即可;如果等于k那么就直接找到了k个元素;

2.寻找和为定值的两个数

解法一:暴力法

找出所有的两个数对,然后求值进行比较。两个for循环即可。时间O(N平方)

解法二:散列表法

把所有数构建一个散列表,然后查找每个数对应的差,看是否在散列表中。

解法三:双指针法(先说一句,如果未排序,先进行排序)

更好的方式是使用头尾双指针法,如果sum>val,说明值需要减小end--即可,sum<val,值需要增加,start++即可。

引申扩展:三数和/四数和的问题,

全都转化为两数之和的问题即可。

#数组排序,先确定一个指针,问题转化为双指针找固定数!
class Solution:
    def threeSum(self, nums):
        nums.sort()
        n = len(nums)
        res = []
        for k in range(n-2):
            #-4 -1 -1 0 1 2有重复的-1是可能会得到重复结果的
            if k==0 or nums[k-1]<nums[k]:  #判重
                i = k+1
                j = n-1
                while i<j:
                    if nums[i]+nums[j]==-nums[k]:
                        arr = [nums[i],nums[j],nums[k]]
                        res.append(arr)
                        i+=1
                        j-=1
                        #在这里再次判断重复[-2,0,0,2,2]
                        while i<j and nums[i]==nums[i-1]:  #判重
                            i+=1
                        while j+1<n and i<j and nums[j]==nums[j+1]:  #判重
                            j-=1
                    elif nums[i]+nums[j]>-nums[k]:
                        j-=1
                    else:
                        i+=1
        return res

3.寻找和为定值的多个数

输入两个整数n和sum,从数列1,2,3,4...n中随意取几个数使和为sum,列出所有组合。

解法一:暴力递归

对每个数字有两种选择,选与不选的问题,无重复不用判断重复的问题。时间O(2的N次方)

ps:加一些判断条件,进行递归剪支;

扩展:最典型的01背包问题,n个物品一个容量为v的背包,矩阵的值代表第i个物品放入背包耗费的容量是Ci,得到的价值是W,怎样选择让背包中的价值最大?

构建矩阵f(i,j)代表前i个物品放入容量为j的背包中最大的价值。这就引到了放与不放f(i,j)=max(f(i-1,j),f(i-1,j-ci)+wi)状态矩阵。

4.最大连续子数组和

注意子数组是连续的,数组里的值有正,有负,有零。

解法一:分治法

最大的子数组可能在中间位置(从当前元素向左和向右扩展,找到最大的即可),可能在左边可能在右边,对子问题进行递归即可。时间O(NlogN)

解法二:动态规划

设f(i)为前i个数的最大子数组的和,dp(i)以第i个元素结尾的最大子数组和;f(i)= max(f(i-1),dp(i-1)+arr(i)),dp(i)=arr(i)+dp(i-1)if dp(i-1)>0 else arr(i) 

扩展:如果要求出连续子数组的最大乘积?

设f(i)为以第i个数结尾的最大子数组的乘积,f(i)等于第i个数乘f(i-1)中的最小或者最大的数,因为第i个数可能为正/负。

 一维环形子数组 最大和?(数组的首尾可以跨越)

划分子问题,跨越计算一遍,不跨越计算一遍,跨越的计算一遍(需要从头和尾元素为起点向外扩);

5.跳台阶问题

解法一:递归(自顶向下)

分解子问题,f(n)=f(n-1)+f(n-2),f(1)=1,f(2)=2;时间O(2的N次方)

解法二:动态规划(自下向上)

存在大量重复的计算,把计算出来的结果都存储下来,然后就可以避免重复的计算。时间O(N)

6.奇偶数排序(奇数放在偶数的前面)

可以改变相对位置(随机交换不具有稳定性):

解法一:两个指针向后扫

p1指向奇数边界的下一个元素,p2每次向前遍历,if p2为奇数  则p1与p2交换,然后p1++。

不能改变相对位置(稳定性的排序):

解法二:不使用额外空间,使用冒泡排序,每次在尾部确定一个偶数;使用插入排序也可以。平方时间!

7.荷兰国旗

分三色的问题,红(0),白(1),黑(2)。

解法:三指针处理

p1指向0的边界的下一个元素,p2指向2边界的下一个元素,cur是遍历指针。

p1和cur起始都是指向首元素,p2起始指向尾元素。

if cur指向0,那么与p1的元素交换,然后p1++,cur++,需要特别注意cur与p1之间只会是1

if cur指向1,那么不用交换,cur++即可。

if cur指向2,那么与p2元素交换,然后p2--,但是cur不能动,因为交换到cur的元素可能会是0/1。

    def point_three(self,arr):
        cur = p1 = 0
        p2 = len(arr)-1
        while(cur<=p2):
            if(arr[cur]==0):
                arr[p1],arr[cur] = arr[cur],arr[p1]
                p1+=1
                cur+=1 #p1和cur之间的元素只能是1
            elif(arr[cur]==1):
                cur+=1
            else:
                arr[p2],arr[cur] = arr[cur],arr[p2]
                p2-=1
        print(arr)

扩展一:找出只由 a,b,c 组成的字符串中包含 abc 的个数

思路:分别找出a,b,c的个数然后相乘,荷兰国旗问题,遍历一遍即可知道。

扩展二:三路快排

思路:大于分割元素,小于分割元素,等于分割元素;

8.数组中重复的数字

所有数字都在0-n-1范围内,长度为n的数组,需要找出数组中重复的一个数字。

思路一:哈希表,不改变原数组

思路二:遍历一遍,当第i个元素等于下标就++,不等于下标就放到对应位置,如果相等那就是该值,如果不等就交换元素,改变原数组

9.数组中数字出现的次数

找出数组中只出现一次的两个数字,其他每个数字只出现2次。

思路:先异或一边,得到两个数的异或值val,在找出val最右边的1位记为第k位,因为异或是不同会生成1,可以根据第k位的1把数组的数分开,然后在把一半的数组进行异或那么会得到两个数字的其中一个,然后再把这个数字跟val进行异或会得到最后一个。

扩展:数组中唯一出现过一次的数,其他数字出现过三次。

思路:可以把每个数的二进制位进行对应相加,然后把res结果的每一位与3进行取余,余数为1那么target对应位的数为1否则为0。(最后再转化成十进制即可)

class Solution {
public:
    int singleNumber(int A[], int n) {
        int bit_time[32] = {0};
        int i = 0;
        int j = 0;
        int result = 0;
        for(i = 0; i < 32; i++){
            for(j = 0; j < n; j++){
                bit_time[i] += (A[j] >> i) & 0x01;
            }
            result |=  (bit_time[i] % 3) << i;
        }
        return result;       
    }
};

10.数组中0~n-1中缺失的数字

思路一:可以计算0-n-1的加和再减去数组的和得到的差。可能会溢出!

思路二:如果有序,使用二分查找,if 中间的数与下标对应那么在右半区查找,if  不对应 且上一个数也不对应则在左半区找,否则前一个数对应的下标就是target。

思路三:使用异或法,因为知道了范围中的数字而且缺了一个,可以在异或数组的基础上在异或一遍数字。

扩展:如果找0~n-1缺失两个数,

异或法,跟0~n-1进行异或的得到缺失数异或的结果,根据结果找到一个二进制为1的位置然后会把s1和s2分开,然后在异或一半数组就可以得到一个结果。

11.如何在不排序的情况下求数组的中位数(重要)

思路:利用pratition函数进行划分,将划分元素的位置与中位数的位置进行比较,要么在左半区继续找,要么在右半区继续找。

class Solution:
    def find(self,arr):
        #偶数找第(n-1)/2和n/2个取平均值,奇数找第n/2和(n-1)/2取平均,计算的值恰好为对应索引
        n = len(arr)
        left =  0
        right = n-1
        first = 0
        while left<=right:
            l = left
            r = right
            val = arr[l]
            start = l
            while l<r:
                while l<r and arr[r]>val:
                    r = r-1
                while l<r and arr[l]<=val:#左指针的区间是小于等于val的
                    l = l+1
                arr[l],arr[r] = arr[r],arr[l]
            arr[start],arr[l] = arr[l],val
            #进行中位数查值
            if(l==int((n-1)/2)):
                first = arr[l]
                break
            elif(l<int((n-1)/2)):
                left = l+1
            else:
                right = l-1

        left = 0
        right = n - 1
        second = 0
        while left <= right:
            l = left
            r = right
            val = arr[l]
            start = l
            while l < r:
                while l < r and arr[r] > val:
                    r = r - 1
                while l < r and arr[l] <= val:  # 左指针的区间是小于等于val的
                    l = l + 1
                arr[l], arr[r] = arr[r], arr[l]
            arr[start], arr[l] = arr[l], val
            # 进行中位数查值
            if (l == int((n) / 2)):
                second = arr[l]
                break
            elif (l < int((n) / 2)):
                left = l + 1
            else:
                right = l - 1
        print(first,second)

        return

12.三个有序数组中找出公共元素

思路:因为有序,直接进行3路归并进行判断即可,找出最小的/两个相等的/三个相等的。

13.如何对大量重复数字的数组进行排序

思路:(不知道区间无法进行计数排序)利用字典进行计数统计,然后再对这个字典的key遍历到数组进行快排,在利用数组和字典确定每个元素的开始位置并且用字典存起来,然后就可以进行计数排序了。

ps:利用了计数排序的思想。

14.两个数组的交集

给定两个数组,编写一个函数来计算它们的交集。

示例 1:
    输入: nums1 = [1,2,2,1], nums2 = [2,2]
    输出: [2]

示例 2:
    输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
    输出: [9,4]

思路一:分别排序,然后双指针进行处理。

思路二:使用hash表,先把第一个数组hash一遍,在用第二个数组进行查找(注意有重复元素的情况)。

15.找出超过一半的数?

思路一:遍历一遍,如果当前元素等于计数器元素就加一,否则减一;如果计数器为0,则更新为当前元素;

思路二:排序法;

int search(int *A,int size)
{
    int count=0;
    int current;
    for(int i=0;i<size;i++)
    {
        if(count==0)
        {
            current=A[i];
            count=1;
        }
        else
        {
            if(A[i]==current)
                count++;
            else
                count--;
        }
    }
    return current;
}

扩展:如果找出等于一半的数?

思路:如果该数在最后,那么先进行检测一下;如果不在最后正常方法

16.求一个数组b, b[i] = a[0] * a[1] *…*a[i – 1] * a[i + 1] * …*a[n – 1],不能使用除法,不允许再开数组

思路:正向遍历初始化1,index从1~n-1乘一遍;反向遍历初始化1,index从n-2~1乘一遍(前缀和)

int main()
{
    int a[]={2,3,7,23,6,5,1,23,89,23};
    int *b=(int*)malloc(sizeof(a));
    b[0]=1;
    int len=sizeof(a)/sizeof(int);
    int j,i;
    for( i=1;i<len;i++)
    {
        b[i]=b[i-1]*a[i-1];
    }
    int tmp=1;
    for(j=len-2;j>=1;j--)
    {
        tmp*=a[j+1];
        b[j]*=tmp;
    }
    pr_arr(b,len);
    return 0;
}

17.给定一个01串,求它一个最长的子串满足0和1的个数相等。

思路:把0换成-1,求和为0即可;dp(i)代表前i个元素最长的子串和为0;

18.给定n * n的01方阵,每一行都是降序的(即先连续的一段1,再连续的一段0),求1最多的那行中1的个数?

思路:如果某个位置时1,则向右,是0则向下 (我们只需要找到比本行更多的1才有意义!)

19.A[i]是一个严格递增的整数数组,其中所有的数字都不相等,请设计一种算法,求出其中所有的A[i]=i的数字

思路:使用二分查找,如果mid=arr[mid],需要继续二分左边和右边,如果mid<arr[mid]继续二分左边,否则二分右边,将结果累加起来即可。

20.将 k 个有序数组合并为一个有序数组

思路:递归分治,后序遍历;先将左边归并,再将右边归并,在进行merge的方法。

class Solution(object):
    def mergeKLists(self, lists):
        """
        :type lists: List[ListNode]
        :rtype: ListNode
        """
        if(lists==None or lists==[]):
            return None
        elif(len(lists)==1):
            return lists[0]
        r = len(lists)-1
        l = 0
        mid = (l+r)/2
        self.merge_sort(l,r,lists)
        return lists[0]
    
    def merge_sort(self,l,r,lists):
        if(r==l):
            return
        else:
            mid = (l+r)>>1
            self.merge_sort(l,mid,lists)
            self.merge_sort(mid+1,r,lists)
            lists[l] = self.merge(lists[l],lists[mid+1])
    
    def merge(self,l1,l2):
        if(l1==None):
            return l2
        if(l2==None):
            return l1
        head = ListNode(0)
        head.next = l1
        pre = head
        while(l1 and l2):
            if(l1.val==l2.val):
                tmp = l2
                l2 = l2.next
                tmp.next = l1.next
                l1.next = tmp
                pre = tmp
                l1 = pre.next
            elif(l1.val>l2.val):
                tmp = l2
                l2 = l2.next
                pre.next = tmp
                tmp.next = l1
                pre = tmp
            else:
                pre = l1
                l1 = l1.next
        if(l2):
            pre.next = l2
        return head.next

21.给定一个整数数组,你需要寻找一个连续的子数组,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。你找到的子数组应是最短的,请输出它的长度。

思路:clone原数组然后进行排序,使用头尾指针,如果值对应相等指针移动,两边都不相等就停止

22.给一个词典的集合,一组不重复字母,问这些字母可以组成几个单词

思路:先 Hash 一下字母,然后遍历这个词典,对于每个词语去 Hash 里面查一下,如果都有就能组成。

23.旋转数组;

无重复元素找最小值和无重复元素找目标值,

使用二分查找进行比较,只需要确定左边有序(mid>=R)还是右边有序(mid<R);

有重复元素单独进行处理:arr[L]=arr[m];相等,特殊处理,++L

class Solution:
    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        if nums==[]:
            return -1
        n = len(nums)
        self.target = target
        #找到旋转数组的最小值
        l = 0
        r = n-1
        min_pos = 0
        while l<r:
            mid = (l+r)//2
            if r-l==1: //解决全部都是相同数值
                min_pos = l if nums[l]<nums[r] else r
                break
            if nums[mid]<nums[mid+1] and nums[mid]<nums[mid-1]:
                min_pos = mid
                break
            #左边不等于中间
            if nums[r]<nums[mid]:
                l=mid
            #右边不等于中间
            elif nums[r]>nums[mid]:
                r= mid
            elif nums[r]==nums[mid]:#不会有2  7   2这种情况在上面处理了
                if nums[mid]>nums[l] or (nums[mid]==nums[l] and nums[mid]==nums[mid-1]):
                    l = mid
                elif nums[mid]<nums[l] or (nums[mid]==nums[l] and nums[mid]==nums[mid+1]):
                    r = mid
        #在两个有序数组里找target
        num1 = self.bin_search(nums[:min_pos]) 
        if num1!=-1:
            return num1
        num2 = self.bin_search(nums[min_pos:])
        if num2!=-1:
            return num2+min_pos
        return -1
           
    def bin_search(self,arr):
        if arr==[]:
            return -1
        l = 0
        r = len(arr)-1
        #注意有等于
        while l <= r:
            mid = (l + r) // 2
            if arr[mid] == self.target:
                return mid
            elif arr[mid] < self.target:
                l = mid+1 #注意必须有
            else:
                r = mid-1 #注意必须有
        return -1

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值