算法与数据结构(一) --- 数组


import os
import torch
import numpy as np
import pandas as pd
import math 
import random
from collections import OrderedDict, deque
from copy import deepcopy

案例1: 二分查找


def half_find(li, aim, start=0):
    l = 0
    r = len(li)
    m = int(r/2)

    if aim < li[m]:
        # 此时是左边递增,最小值位于右边
        return half_find(li[m+1:r], aim, start=m)
    elif aim > li[m]:
        # 最小值位于mid左面,包含mid
        return half_find(li[l:m+1], aim, start=start)
    else:
        return m, li[m]

if __name__ == '__main__':
    a  =  [12, 51, 81, 94, 111, 165, 198]
    print( half_find(a, 94) )

案例2: 零和数组

【描述】 元素和最接近0的子数组
【思路】 记1—n元素和为s(n), 那么有两种情形, S(n)和S(m)-S(n)。其中第二种可以进行排序,取相邻两项做差。排序复杂度nlog(n).


def get_mini_sublist(li):
    part1_index = {}
    part2_index = {}
    
    # 第一部分,前n项求和
    temp = 0
    for i in range(1, len(li)):
        part1_index[(0, i)] = sum(li[:i])
    part1_index = OrderedDict(sorted(part1_index.items(), key=lambda x:x[1] ))
    
    # 第二部分,排序,做差
    t1, t2 = deepcopy(part1_index), deepcopy(part1_index)
    t1.popitem(last=True),
    t2.popitem(last=False)
    
    for i,j in zip(t1.items(), t2.items()):
        part2_index[(i[0][1], j[0][1])] = j[1]-i[1]
    
    # 融合,然后取值最小值    
    part2_index.update(part1_index)         
    key_min = min(part2_index, key=lambda x:abs(part2_index[x]))
    return key_min, part2_index[key_min]

if __name__ == '__main__':
    a = [-1, 32, -21, 22, 12, 11, -2, -12]
    print(get_mini_sublist(a))

3. 旋转数组最小值

【描述】旋转数组,如[ -2, 3, 5, 6, 9, -10, -5], 特点是把拍好序的数组左边一部分移动到了右边。
【思路】二分查找


def find_min(li, posotion=0):
    l = 0
    r = len(li)
    m = int(r/2)

    if li[l] < li[m]:
        # 此时是左边递增,最小值位于右边
        return find_min(li[m+1:r], posotion=m)
    elif li[l] > li[m]:
        # 最小值位于mid左面,包含mid
        return find_min(li[l:m], posotion=posotion)
    else:
        return li[m], m

if __name__ == '__main__':
    li = [ -2, 3, 5, 6, 9, -10, -5]
    print(find_min(li))

4. 调整数组顺序使奇数位于偶数前面

【描述】输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
【思路】创建双向队列,遍历数组,奇数前插入,偶数后插入。


# 双向队列实现
def reOrderArray1( array):
        # write code here
        odd = deque()
        l = len(array)
        for i in range(l):
            if array[l-i-1] % 2 != 0:
                odd.appendleft(array[-i-1])
            if array[i] % 2 == 0:
                odd.append(array[i])
        return list(odd)

# 不开辟新空间
def reOrderArray2(li):
    temp = 0 # 找出pop的偶数位数, 相应索引左移
    for i in range(len(li)):
        index = i - temp
        if li[index] % 2 == 0:
            li.append(li.pop(index))
            temp += 1
    return li
            
if __name__ == '__main__':
    li = [ -2, 3, 5, 6, 9, -10, -5]
    print( reOrderArray1(li) )
    print( reOrderArray2(li) )

5. 快速找出数组中出现次数超过一半的数字

【思路】遍历数组,同时存储两个数,{数字值, 数组出现次数}, 出现相同数则次数+1, 不用-1, 若次数为0,则更换数字值。
考虑到次数超过一般, 那么相同次数应该为正, 保存的数字值应该是最多出现次数


def more_than_half_numb(li):
    num, time, temp = 0, 0, 0
    for i in li:
        if not time:
            temp = num = i
        else:
            if i == temp:
                time += 1
            else:
                time -= 1
            
            time +=1
            
            if time <= 0:
                num = i
    return num

if __name__ == '__main__':
    li = [1,2,3,2,2,2,5,4,2]
    print(more_than_half_numb(li))
        

6. 最大和连续子数组

【思路】: 动态规划, 每次都比较 当前值 与 累加当前值


def find_max_sub_array(li):
    store = {}
    temp = li[0]
    start, end, sum = 0, 0, temp

    for i in range(1, len(li)):
        current = temp + li[i]
        if current >= temp:
            end += 1
            sum += current
        else:
            store[sum] = (start, end)
            start, end = i, i
            sum = li[i]
    result = max(store)
    return result, store[result]


if __name__ == '__main__':
    li = [1, 2, 4, 5, -100, 1, -1]
    print(find_max_sub_array(li))

7. 切绳子(可切可不切)

【思路】: 动态规划, 逐步计算
【贪心算法】: n>=5 时有 3(n-3)>=2(n-2),优先多3, 最后处理剩余的4, 5


# 动态规划
def cut(length):
    product = {}
    product[1] = 1
    product[2] = 2 # 必需
    product[3] = 3

    for i in range(4, length+1):
        result = max([product[i-j] * product[j] for j in range(1, i)]) #如果必须切, 此处(2, n)
        product[i] = result
    return product

if __name__ == '__main__':
    print(cut(20))

8. 把数组排成最小的数

【思路】: 应用字符串比较最为方便

def join2max(li):
    result = sorted(map(lambda x: str(x), a), reverse=True)
    return int(''.join(result))

if __name__ == '__main__':
    a = np.random.randint(1000,size=10)
    print(join2max(a))

9. 数组中的逆序对

【题目】在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。
【思路】归并排序

class MergeSort:
    def __init__(self):
        self.num = 0
    def _merge(self, a, b):
        result = []
        while a and b:
            if a[0]<=b[0]:
                result.append( a.pop(0) )
            else:
                result.append( b.pop(0) )
                self.num += len(a)
        result.extend(a if a else b)
        return result
    
    def __call__(self, li):
        length = len(li)
        if length == 1:
            return li
        else: 
            mid = int(len(li) / 2)
            left = self.__call__(li[:mid])
            right = self.__call__(li[mid:])    
            return self._merge(left, right)
        
if __name__ == '__main__':
    a = list(np.random.randint(1, 100, 10))
    sort = MergeSort()
    print(sort([1, 3, 5, 2, 7]))
    print('逆序对数量为: {}'.format(sort.num))
原文地址:https://www.cnblogs.com/geoffreyone/p/11506019.html