python算法思想-刷题专用

一、枚举法(循环+判断)

如质数求解、鸡兔同笼、水仙花数(3位数位幂为自身)、最大公约数求解、孪生素数(间隔2的素数对)、完全数(所有真因子和为自身)

for n in range(10,100):
  for i in range(2,n-1):
	if n%i==0:break
	else:print(n,'是质数')
# rabbit:1~34
# chicken:35-rabbit
for rabbit in range(1,35):
  chicken = 35-rabbit
  if 4*rabbit+2*chicken==94:
	print('兔',rabbit,'鸡',chicken)

二、排序算法(快速排序、堆排序、归并排序)

2.1快速排序(Quick Sort)——分治法(递归)——时间复杂度O(nlogn)

使用分治法策略,将一个序列分为两个子序列,递归对两个子序列进行排序。

两个要素:1.选出基准(pivot);2.分区操作(partition)

挑选基准值:从数列中挑出一个元素,称为"基准"(pivot),本例子选择最后面的元素作为基准

分区操作:重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(与基准值相等的数可以到任何一边)。在这个分割结束之后,对基准值的排序就已经完成;返回分割位置。

快排函数:确定分割位置,再对基准值分成两半的区域分别进行快排;递归地将小于基准值元素的子序列和大于基准值元素的子序列排序。

# small,large是顺序
def partition(array,small,large):
	s = (small-1)  # 最小元素的索引
	pivot = array[large]  # 选取最大元素值作为基准pivot
	for j in range(small,large):
		if arr[j]<=pivot:
			s = s+1
	  		arr[s],arr[j] = arr[j],arr[s]
	arr[s+1],arr[large] = arr[large],arr[s+1]
	return s+1
def quicksort(array,small,large):
  	if small < large:
		pi = partition(array,small,large) # 基准值的位置,已经确定最终位置了
		quicksort(array,small,pi-1) # 对基准值左边元素进行排序
		quicksort(array,pi+1,large) # 对基准值右边元素进行排序
arr = [10, 7, 8, 9, 1, 5]
n = len(arr)
quickSort(arr, 0, n - 1)

2.2堆排序(Heap Sort)——递归——时间复杂度O(nlogn)

堆是一颗完全二叉树,适合用数组实现。使用最大堆,通过1.建堆2.堆的维护操作进行排序

def heapify(arr,n,i):
    '''
    :param arr: 存储堆的数组
    :param n: 数组长度
    :param i: 待维护节点的下标
    '''
    largest = i
    #假设父节点最大,找到左节点、右节点下标
    left = i*2+1
    right = i*2+2

    if left<n and arr[largest]<arr[left]:
        largest = left
    if right<n and arr[largest]<arr[right]:
        largest = right
    if largest != i:
        arr[i],arr[largest]=arr[largest],arr[i]
        heapify(arr,n,largest)

堆的维护操作即确保父节点的值要大于它的左子节点和右子节点,将整个数组维护成满足堆的规则

def heapsort(arr):
    #建堆-将arr数组中的元素挨个进行堆的维护
    n = len(arr)
    for i in range(n,-1,-1):
        heapify(arr,n,i)
    #排序
    for i in range(n-1,0,-1):
	    # 将堆中的堆顶元素换到最后一位(即最大值)
        arr[i],arr[0]=arr[0],arr[i]
        heapify(arr,i,0)
        # 重新维护剩余i个元素
    return arr

2.3归并排序(Merge Sort)——分治法(递归)——时间复杂度O(nlogn)

def merge_sort(lists):
    '''
    递归进行归并排序。
    '''
    # 将list所有的元素排序完毕,递归结束
    if len(lists) <= 1:
        return lists
    
    # 分治进行递归
    mid = len(lists)//2
    left = merge_sort(lists[:mid])
    right = merge_sort(lists[mid:])
    
    # 将两个有序数组进行合并
    result = []
    i,j = 0,0
    while i < len(left) and j < len(right):
        # 将较小值放入到result中
        if left[i] < right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1
    
    # 将未被遍历到的直接追加到result后面
    if i == len(left):
        result.extend(right[j:])
    else:
        result.extend(left[i:])
    return result

三、贪心算法(Greedy Algorithm)

在对问题求解时,总是做出在当前看来是最好的选择。也就是说不从整体最优上加以考虑,算法得到的结果是在某种意义上的局部最优解。

解题使用贪心算法有两个条件

  • 问题具有贪心选择性质,即使用当前最优解能够得到全局最优解;
  • 问题的子问题具有最优子结构性质,即原问题的最优解可以通过对子问题的最优解组合而得到。

贪心算法的基本流程如下:

  • 将问题分解为若干子问题;
  • 对每个子问题求解,得到子问题的局部最优解;
  • 将子问题的局部最优解合并成原问题的解。

3.1示例一:找零问题

def greedy_coin_change(coins, amount):
    coins.sort(reverse=True)
    result = []
	if amount==0:
	  return 0
    for coin in coins:
        result += amount//coin 
		amount %= coin 
	return result

# 示例
coins = [25, 10, 5, 1]
amount = 63
print(greedy_coin_change(coins, amount))

贪心算法中的局部最优解就是每次找零使用数值最大的货币全局最优解就是找零数量最少

3.2示例二:背包问题

def knapsack(capacity, weights, values):
    n = len(weights)   # 物品的重量
	# 对所有物品按照单位重量的价值从大到小排序
    value_per_weight = sorted([(values[i] / weights[i], weights[i])   
                               for i in range(n)], reverse=True)
    value = 0
    for v in value_per_weight:
        if capacity == 0:
            break
        weight = min(v[1], capacity)
        value += weight * v[0]
        capacity -= weight
    return value

weights = [4, 3, 5]  # 物品的重量列表weights
values = [4000, 3500, 5000]   # 物品的价值列表values
capacity = 10   # 背包容量C:capacity
print(knapsack(capacity, weights, values))  # 输出最大价值之和:10500.0

贪心算法中的局部最优解就是每次选择价值最大的物品全局最优解就是放入背包的物品的重量之和不超过C并且价值之和最大。具体做法为:对所有物品按照单位重量的价值从大到小排序,然后依次选择单位重量价值最高的物品,直至背包中放不下为止。

def rec_opt(v,n):
    if n==0:
        return v
    elif v==0:
        return 0
    elif v<v_thing[n-1]:#如果箱子的体积小于第n个物品的体积,那就直接判断放不放第n-1个物品
        return rec_opt(v,n-1)
    else:
        A = rec_opt(v-v_thing[n-1],n-1)  # 选第n件物品
        B = rec_opt(v,n-1)  # 不选第n件物品
        return min(A,B)

3.3示例三:活动选择问题

活动选择问题是贪心算法在调度问题中的应用,通过选择结束时间最早的活动,实现最大化可安排活动数量。

def greedy_activity_selection(start_times, finish_times):
    activities = list(zip(start_times, finish_times))
    activities.sort(key=lambda x: x[1])   # 根据结束时间进行排序
    
    selected_activities = [activities[0]]  # 初始化计划单:选择第一个结束的活动
    last_finish_time = activities[0][1]    # 初始化结束时间:第一个活动结束的时间
	# 遍历后续的活动,如果活动的开始时间晚于结束时间:将此次活动添加进计划单selected_activities
    for activity in activities[1:]:        
        if activity[0] >= last_finish_time:
            selected_activities.append(activity)
			# 更新活动结束时间
            last_finish_time = activity[1]

    return selected_activities

# 示例
start_times = [1, 3, 0, 5, 8, 5]
finish_times = [2, 4, 6, 7, 9, 9]
print(greedy_activity_selection(start_times, finish_times))

贪心算法中的局部最优解就是每次选择活动结束最早的活动全局最优解就是安排的活动次数最多。具体做法为:对所有活动按照活动结束时间从早到晚排序,然后在前后活动时间不重复的约束下,依次选择活动结束最早的活动,直至选择完毕。

3.4示例四:拼接数字问题

li = [32, 94, 128, 1286, 6, 71]
from functools import cmp_to_key

# 交换位置 32,94 --> 94 32
def xy_cmp(x, y):
    if x + y < y + x:
        return 1
    elif x + y > y + x:
        return -1
    else:
        return 0

def number_join(li):
    # 将li中的数字转换为string类型
    li = list(map(str, li))
	# 通过函数xy_cmp对li进行排序
    li.sort(key=cmp_to_key(xy_cmp))
	# 返回将li中的字符串起来的数值
    return ''.join(li)

贪心算法中的局部最优解就是每次比较两个值x和y,按照最大的顺序固定两个的位置全局最优解就是最大化所有数字。具体做法为:对所有列表中的元素,按照组合起来最大的方式排序,最终返回整个列表。

四、双指针(Two Pointers

双指针通过两个指针协同完成任务。这些指针可以指向不同的元素,具体应用取决于问题的性质。双指针算法主要包括:对撞指针、快慢指针、滑动窗口、双链遍历。

  • 对撞指针:一左一右向中间逼近。一般左指针初始索引为0,右指针初始位置索引为n-1。
n=len(numbers)  # 数量
l,r=0,n-1  # 左右指针
  • 快慢指针:一快一慢,步长不同。一般用于检测环形链表。
# 快慢指针先指向head节点
slow=fast=head
slow=slow.next  # 移动1步
fast=fast.next.next  # 移动2步
  • 滑动窗口:两个指针,一前一后组成滑动窗口,并计算滑动窗口中的元素的问题。常见问题:字符串匹配问题等,用来解决一些查找满足一定条件的连续区间求值或长度的问题。

4.1对撞双指针

想要用对撞双指针,需要先对数组进行排序,或者本身输入就是有序的。

def twoSum(self, numbers, target):
        """
        时间复杂度O(n),空间复杂度O(1),numbers为非递减排序的列表
        """
        # 输入的已经排序
        n=len(numbers)  # 数量
        l,r=0,n-1  # 左右指针
        while l<r:
            v=numbers[l]+numbers[r]  # 两数之和
            if v>target: # 移动指针
                r-=1
            elif v<target:
                l+=1
            else:
                return [l+1,r+1]
def judgeSquareSum(self, c):
    a, b = 0, floor(sqrt(c))
    while a <= b:
        sum = a ** 2 + b ** 2
        if sum == c:
            return True
        elif sum < c:
            a += 1
        else:
            b -= 1
    return False

八、动态规划(Dynamic Programming

全部评论

相关推荐

想写一篇文章来记录我的找工作过程从寒假回来我还在放假的快乐中没有出来突然看到同门已经开始准备八股和项目了我当时还在想才二月底暑期实习需要准备早嘛然后就是突然看到成堆的互联网厂实习开始了但是🐭🐭都不知道要复习什么要去从事哪方面的职业要去什么地方实习,怎么去找项目这些🐭🐭就仿照同门照猫画虎开始找Java后端开发的东西看然后就是惨淡的暑期实习生活🐭🐭双非本科的bg 导致在大部分的投递中都倒在了笔试环节或者简历环节最后只有tx&nbsp;和mt给了我面试机会mt甚至已经全部面完泡池子了排了一周的序给我挂掉了然后就是五六月两个月的空窗期这两个月我也在不停的投中小厂不断地完善自己的知识储备结果是一个面试也没有那个时候真的很绝望然后刷xhs&nbsp;看到了测开这个岗去了解了职责是什么,感觉好像也没有人们说的这么无趣不过这个时候已经到6月中旬了留给🐭🐭的机会不多了最后的机会就是hw&nbsp;和wy&nbsp;算是拼尽了最后的希望去争取最后的机会想着这两个再没戏直接ALL&nbsp;IN考公吧虽然也很难,但至少又回到备考状态至少不会那么焦虑结果最后hw和wy&nbsp;都通过了🐭🐭秉着对游戏的热爱去了猪厂实习期间组内另一个人bg&nbsp;也比我好而且大概率只会留一个人我还是想争取一下这个机会于是实习期就在加班做需求我想总是要争取才可能会有生还的机会实习还没结束又看到一堆提前批和秋招开始刚被项目需求填满的焦虑又回来了我不知道是要实习还是秋招还是一边实习一边秋招🐭🐭想了好几个晚上决定还是全力把实习弄完先把当前的事情认真做完不管能不能转正,至少给自己一个善始善终转正答辩本来定的是20分钟结果🐭🐭一紧张说的比较慢HR提醒我时间到了也谢谢总监和mentor为我说话让我不要管时间只要能讲清楚,让我慢点理清思绪慢慢讲实习期就这么结束了我不知道自己能不能转在实习的最后一天我开始了自己的秋招生活回校的第一天下午我就投了十几家大厂之后就是长达20天的没有停止过的测评笔试面试但🐭🐭总感觉比找暑期实习好因为之前挂简历的公司也愿意在秋招给机会也可能是测开行业竞争没有后端开发激烈也可能是大厂的实习经历还是有用不管怎样,我在上半年渴望的机会终于一点点来了最后拿到了两个意向两个不错的中厂zj&nbsp;也在不断推进月末某天早晨睡梦中的我突然接到电话告诉我wy&nbsp;转正了我瞬间惊醒了像从不期待幸运降临到我身上一样它就这么发生了现在我不再是像皮球一样被企业踢了踢去了我也有了选择企业的权利这一路的痛苦大概只有我自己懂现在苦尽甘来的感觉确实有一种大结局的感觉也希望没有找到offer&nbsp;的友友们可以一路坎坷过来不放弃希望最终苦尽甘来
投递网易等公司10个岗位
点赞 评论 收藏
分享
评论
1
收藏
分享
牛客网
牛客企业服务