剑指Offer(Python实现)系列更新中。。。

目录

【面试题3】:数组中重复的数字

【面试题4】二维数组的查找

【面试题11】:旋转数组的最小数字

【面试题21】:调整数组顺序使奇数位于偶数前面

【面试题22】:链表中倒数第k个结点

【面试题25】:合并两个排序的链表

【面试题39】数组中出现次数超过一半的数字

【面试题42】连续子数组的最大和

【面试题45】把数组排成最小的数

【面试题51】数组中的逆序对

【面试题53】数字在排序数组中的次数



【面试题3】:数组中重复的数字

题目描述:

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

解题思路:

1.充分利用题干信息。

首先判断当前值与下标是否相等。

    相等的话,continue。不等的话,判断number[i] 和 number[number[i]]是否相等。

    相等的话,就认为找到了。不等的话,就交换number[i] 和 number[number[i]],直到当前位置的值与下标相等位置。

时间复杂度:O(n)

# -*- coding:utf-8 -*-
class Solution:
    # 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
    # 函数返回True/False
    def duplicate(self, numbers, duplication):
        # write code here
        for i in range(len(numbers)):
            while numbers[i] != i:
                if numbers[i] == numbers[numbers[i]]:
                    duplication[0] = numbers[i]
                    return True
                #swap the numbers[i] and numbers[numbers[i]]
                temp = numbers[i]
                numbers[i] = numbers[temp]
                numbers[temp] = temp

        return False

2.哈希表

可以利用collections库中的Counter 函数,建立哈希表。

时间复杂度:O(n)    空间复杂度:O(n)

# -*- coding:utf-8 -*-
import collections
class Solution:
    # 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
    # 函数返回True/False
    def duplicate(self, numbers, duplication):
        # write code here
        flag = False
        hashTable = collections.Counter(numbers)
        for item,times in hashTable.items():
            if times > 1:
                duplication[0] = item
                return True
        return False

【面试题4】二维数组的查找

题目描述:

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

解题思路:因为二维数组是有序的,因此假设从右上角开始查找,如果该值小于target,就在下一行寻找,如果该值大于target,就在上一列中寻找。直到找到,或者满足退出条件为止

时间复杂度:O(n)

# -*- coding:utf-8 -*-
class Solution:
    # array 二维列表
    def Find(self, target, array):
        # write code here
        row = 0
        col = len(array[0]) - 1
        while row < len(array) and col >= 0:
            if array[row][col] > target:
                col -= 1
            elif array[row][col] < target:
                row += 1
            else:
                return True
        else:
            return False

【面试题11】:旋转数组的最小数字

题目描述:

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

解题思路:

因为存在两个有序的子数组,因此可以考虑二分查找。

定义left和right,分别表示前一个数组最大的元素和后一个数组最小的元素。

若arr[left] 小于arr[mid],说明mid之前都是单调递增的数组,不会有最小值,因此在[mid,right]之间查找

若arr[left] 大于arr[mid],说明在[left,mid]之间存在两个数组的交界处,肯定存在最小值,因此在[mid,right]中寻找

当right和left相差1的时候,right对应的值就是最小值时,

时间复杂度:O(n)

# -*- coding:utf-8 -*-
class Solution:
    def minNumberInRotateArray(self, rotateArray):
        # write code here
        length = len(rotateArray)
        left = mid = 0
        right = length - 1
        while rotateArray[left]>rotateArray[right]:
            if (right - left) == 1:
                mid = right
                break
            mid = left + (right-left)//2
            if rotateArray[left] == rotateArray[right] and rotateArray[left] == rotateArray[mid]:
                return self.minOrder(rotateArray,left,right);
            if rotateArray[mid] >= rotateArray[left]:
                left = mid
            else:
                right = mid
        return rotateArray[mid]
    def minOrder(rotateArray,left,right):
        for i in range(left,right+1):
            if rotateArray[i] > rotateArray[i+1]:
                return rotateArray[i+1]
                
        

【面试题21】:调整数组顺序使奇数位于偶数前面

题目描述:

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

解题思路:

新建一个数组,先遍历一遍把奇数放进去,在遍历一遍把偶数放进去

时间复杂度:O(n) ,空间复杂度为O(n)

class Solution:
    def reOrderArray(self, array):
        # write code here
         return [i for i in array if i%2 != 0] + [i for i in array if i%2 ==0]
       

Python的列表解析式好用的可怕

【面试题22】:链表中倒数第k个结点

题目描述:输入一个链表,输出该链表中倒数第k个结点。

解题思路:开始时让两个指针指向头指针,然后让一个指针先走k-1步,然后在一起走,当先走的指针到达最后一个结点时,后走的指针指向的结点就是导数第k个结点

时间复杂度:O(n)

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def FindKthToTail(self, head, k):
        # write code here
        pSlow = pFast = head
        i = 0
        while pFast != None:
            if i >= k:
                pSlow = pSlow.next
            pFast = pFast.next
            i+=1
        return pSlow if i >= k else None

【面试题25】:合并两个排序的链表

题目描述:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

解题思路:递归思路。

先判断两个链表link1和link2的第一个结点的值的大小,如果link1的结点值更小,就放在新链表link的头结点。

然后对link1->next和link2递归。反之,就把link2的头结点放在link的头结点。对link1和link2->next递归

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    # 返回合并后列表
    def Merge(self, pHead1, pHead2):
        # write code here
        if pHead1 == None: return pHead2
        if pHead2 == None: return pHead1
        pMergedHead = None
        if pHead1.val < pHead2.val:
            pMergedHead = pHead1
            pMergedHead.next = self.Merge(pHead1.next,pHead2)
        else:
            pMergedHead = pHead2
            pMergedHead.next = self.Merge(pHead1,pHead2.next)
            
        return pMergedHead

【面试题39】数组中出现次数超过一半的数字

题目描述:

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

解题思路:

因为有一个数字出现的字数比其他所有字数出现的次数之和还要多。因此遍历数组时记录数组中一个数字lastNum和次数。

当 遍历下一个数字时,如果与lastNum相等,count加1.如果不相等,count--。当count等于时,lastNum保存下一个数字,并count等于1

因此最后一次使count设为1的数就是要的解。

时间复杂度:O(n)

# -*- coding:utf-8 -*-
import collections
class Solution:
    def MoreThanHalfNum_Solution(self, numbers):
        # write code here
        lastNum = numbers[0]
        count = 1
        for num in numbers[1:]:
            if num == lastNum:
                count += 1
            else:
                count -= 1
            if count == 0:
                lastNum = num
                count = 1
            print(num,lastNum,count)   
        if  self.checkMoreThanHalf(numbers,lastNum):
             return lastNum
        else:
             return 0
    def checkMoreThanHalf(self,numbers,num):
        c = collections.Counter(numbers)
        if c[num] > len(numbers)//2:
            return True
        else:
            return False
            

【面试题42】连续子数组的最大和

题目描述:

连续子数组的最大和。例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8

解题思路:

定义一个累计和cumsum和最大和maxsum。遍历数组,如果累计和大于最大和的话,更新最大和。如果累计和小于等于0的话,加上任何数都会使累积和减小,因此抛弃之前的子数组,从下一个数开始累积。

时间复杂度:O(n)

class Solution:
    def FindGreatestSumOfSubArray(self, array):
        # write code here
        if len(array)<=0: return 0
        cumSum = 0
        maxSum = -float('inf') 

        for i in array:
            cumSum += i
            if cumSum > maxSum:
                maxSum = cumSum
            if cumSum <= 0:
                cumSum = 0
                continue 
        return maxSum

【面试题45】把数组排成最小的数

题目描述:

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

解题思路:

所以在这里自定义一个比较大小的函数,比较两个字符串s1, s2大小的时候,先将它们拼接起来,比较s1+s2,和s2+s1那个大,如果s1+s2大,那说明s2应该放前面,所以按这个规则,s2就应该排在s1前面。

时间复杂度:O(nlogn)

class Solution:
    def PrintMinNumber(self, numbers):

        Str = list(map(str,numbers)) #convert int to str
        Str.sort(cmp=lambda a,b :cmp(a+b,b+a)) #按照自定义规则排序
        return ''.join(Str) #拼接数组的值,后返回

 

【面试题51】数组中的逆序对

题目描述:

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007

解题思路:

如果直接遍历数组,然后与后面所有的数字作比较的话,好理解,但是时间复杂度是O(n^2),因此需要寻找更快的方法。
归并思路:先把数组分割成子数组,先统计出子数组内部的逆序对的数目,然后再统计出两个相邻子数组之间的逆序对的数目。在统计逆序对的过程中,还需要对数组进行排序。如果对排序算法很熟悉,我们不难发现这个过程实际上就是归并排序。

时间复杂度:O(nlogn)

class Solution:
    def InversePairs(self, data):
        # write code here
        if not data:
            return 0
        temp = [i for i in data]
        return self.mergeSort(temp, data, 0, len(data)-1) % 1000000007
       
    def mergeSort(self, temp, data, low, high):
        if low >= high:
            temp[low] = data[low]
            return 0
        mid = (low + high) / 2
        left = self.mergeSort(data, temp, low, mid)
        right = self.mergeSort(data, temp, mid+1, high)
           
        count = 0
        i = low
        j = mid+1
        index = low
        while i <= mid and j <= high:
            if data[i] <= data[j]:
                temp[index] = data[i]
                i += 1
            else:
                temp[index] = data[j]
                count += mid-i+1
                j += 1
            index += 1
        while i <= mid:
            temp[index] = data[i]
            i += 1
            index += 1
        while j <= high:
            temp[index] = data[j]
            j += 1
            index += 1
        return count + left + right

【面试题53】数字在排序数组中的次数

题目描述:

统计一个数字在排序数组中出现的次数。

解题思路:

利用二分查找,找到这个数字的第一次出现和最后一次出现的位置

如果没找到,就返回0

时间复杂度:O(logn)

class Solution:
    def GetNumberOfK(self, data, k):
        # write code here
        if len(data) <= 0:
            return 0
        start = 0
        end = len(data)-1
        firstIndex = self.findFirstIndex(data,start,end,k)
        endIndex = self.findEndIndex(data,start,end,k)
        if firstIndex == -1 and endIndex == -1:
            return 0
        return endIndex - firstIndex + 1
    def findFirstIndex(self,data,start,end,k):
        if start > end:
            return -1
        Mid = (start+end)//2
        if data[Mid] == k:
            if (Mid == start) or (data[Mid-1]!=k):
                return Mid
            else:
                end = Mid - 1
        elif data[Mid] > k:
            end  = Mid - 1
        else:
            start = Mid + 1
        return self.findFirstIndex(data,start,end,k)
    def findEndIndex(self,data,start,end,k):
        if start > end:
            return -1
        Mid = (start+end)//2
        if data[Mid] == k:
            if (Mid == end) or (data[Mid+1]!=k):
                return Mid
            else:
                start = Mid + 1
        elif data[Mid] > k:
            end  = Mid - 1
        else:
            start = Mid + 1
        return self.findEndIndex(data,start,end,k)
    

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值