通用算法-[数组] -数组常考问题

1、前言

数组、链表、二叉树、动态规划、栈与队列是面试中常考的知识点,而在这几个知识点中,数组形式虽然简单,但是很灵活,在它基础上能衍生出很多问题,非常能考察人的编程思维,因此,本文对数组中常见的一些问题进行了总结,包括:
(1)递增排序数组旋转的最小数字;
(2)数字在排序数组中出现的次数;
(2)调整数组顺序,使数组中的奇数为与偶数的前面;
(3)数组中第K大的数;
(4)数组中最小的K个数;
(5)数组中出现次数超过一半的数字;
(7)把数组排成最小的数;
(8)数组中的逆序对;
(9)K-sum问题;
(10)数组中只出现一次的数字;

2、数组中常考的问题

将一个数组的开头若干个元素搬到数组的末尾,我们称之为数组的旋转,输入递增排序数组的一个旋转,比如{5,6,1,2,3},求旋转数组的最小元素。
(1)旋转数组的最小数字
基本思路:这种问题的解法有很多,方法1是你可以直接对数组进行排序,然后找出最小的数字即可,这种方法的时间复杂度为O(nlogn);方法2是对数组进行扫描,找到数组的最小元素,这种方法的时间复杂度为O(n);
还有一种方法,能在O(log)的时间解决这个问题。
由于原数组是递增的,旋转之后,部分有序,但只要是有序的,问题就会简单很多。
根据最小元素所在的位置,我们可以将数组划分成两个部分A和B,A、B都是递增的数组,且A的开头元素(数组的第一个元素)必定大于或等于B的结尾元素(数组的最后一个元素),(如果A和B长度都不为0的情况下)。我们可以借助二分查找的思想,
1)每次取出数组中间位置的元素middle,那么middle要么落在A中,要么落在B中;
2)如果middle落在A中,那么middle一定大于或等于第一个元素,这时最小元素在middle的后面部分;
3)如果middle落在B中,那么middle一定小于或等于最后一个元素,这时最小元素在middle的前面部分;

需要特别注意的是,
A、当middle同时等于A的开头元素、和B的结尾元素时,无法确定middle是落在A还是B,比如{1,0,1,1,1}和{1,1,1,0,1},这时,只能采用顺序查找的方法;
B、当旋转数组是原递增排序数组旋转0个元素得到时,数组的第一个元素小于最后一个元素,最小的元素是数组的第一个元素。

代码实现

def MinNumInRoateArray(array):

    if len(array) <= 0:

        return 

    start = 0

    end = len(array) - 1

    if array[start] < array[end]:

        return array[start]

    min_num = None

    while array[start] >= array[end]:

        if end == (start + 1):

            min_num = array[end]

            break

        middle = (start + end) // 2

        if array[start] == array[end] and array[middle] == array[end]:

            min_num = MinOrderSearch(array,start,end)

        if array[middle] <= array[end]:

            end = middle

        elif array[middle] >= array[start]:

            start = middle

    return min_num



def MinOrderSearch(array,start,end):

    min_num = float("inf")

    for i in range(start,end + 1):

        if array[i] < min_num:

            min_num = array[i]

    return min_num

(2)数字在排序数组中出现的次数
在一个非严格递增排序的数组中,求某个数字出现的次数,比如排序数组{1,2,3,3,4,5}中3出现的次数是2。

基本思路:假设要查找的数是number,我们可以在排序数组中找到第一个number出现的位置first,再找到最后一个number出现的位置end,end - first +1即是number出现的次数。所以问题转化成查找数组中第一个number的位置和最后一个number的位置,可以使用两次二分查找,

在查找第一个number时,取中间位置的元素middle和number比较,

如果middle小于number,则在middle的前半部分查找,如果小于middle,则在后半部分查找,

如果等于number,继续看middle前面一个元素是否等于number,如果等于,则说明middle不是第一个number,继续在middle的前半部分查找;

查找第二个number的方法类似。
代码实现

def Number_Count(array,number):

    if len(array) <= 0:

        return 0

    frequency = 0
    
    first_index = BinarySearch(array,number,first=True)

    last_index = BinarySearch(array,number,first=False)

    if first_index != None and last_index != None:

        frequency = last_index - first_index + 1


    return frequency

    

    


def BinarySearch(array,number,first):

    start = 0

    end = len(array) - 1

    number_index = None

    while start < end:

        middle = (start + end) // 2

        if array[middle] < number:

            start = middle + 1

        elif array[middle] > number:

            end = middle - 1

        else:

            if first == True:

                if middle - 1 >= start and array[middle - 1] != number or (middle == start):
                    
                    number_index = middle

                    break 
                        
                else:
                    
                    end = middle - 1

            else:

                if middle + 1 <= end and array[middle + 1] != number or (middle == end):

                    number_index = middle

                    break

                else:

                    start = middle + 1
                

                    
    if number_index == None and array[end] == number:

        number_index = end

    return number_index

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Albert_YuHan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值