面试中的算法 [ 基于Python语言 ] 佬们,要求不多,咱点个赞也行!持续更新中!!!

本文主要介绍了面试中常见的算法,也是基于一本书籍上的内容--漫画算法(小灰的算法之旅)还是很不错的书籍,至少漫画可以看得下去

主要内容有经典问题关于判断链表有环,最小栈的实现,最大公约数的几种算法思想,2的幂(这个我学习的时候很是惊讶可以有那么简单的算法!一行搞定,牛逼!)用栈实现队列的操作(这个我感觉有点小bug)... ...

期待大家的交流,互相学习!!!点赞收藏+关注~~~拜托拜托哞哞

目录

5.1如何判断链表有环

5.1.1 有一个单向链表,链表中可能出现“环”,那么如何判断该列表是否为有环链表呢?

5.1.2 如果链表有环,如何求出环的长度

5.1.3 如果链表有环,如何求出入环节点

5.2 最小栈的实现

5.2.1 算法实现思想

5.2.2 代码实现

5.3 最大公约数

欧几里得算法

辗转相减法

5.4 2的幂

如何判断一个数是否为2的整数次幂

5.5 无序数组排序后的最大相邻差

5.5.1 算法思想:

5.6 如何利用栈实现队列

5.6.1 算法思想

5.6.2 代码实现(图绘)

5.7 寻找全排列的下一个数

5.7.1算法思想

5.7.2 代码实现


5.1如何判断链表有环

5.1.1 有一个单向链表,链表中可能出现“环”,那么如何判断该列表是否为有环链表呢?

算法思想:

首先创建两个指针p1、p2(在Python中就是两个对象引用),让它们同时指向这个链表的头结点。然后开始一个大循环,在循环体中,让指针p1每次向后移动1个节点,让指针p2每次向后移动2个节点,然后比较两个指针指向的节点是否相同。如果相同,则可以判断出链表有环,如果不同,则继续进行下一次循环。

节点数量为n,则时间复杂度为O(n),空间复杂度为O(1)。

代码表示:
# 判断链表是否有环问题
class Node:
    def __init__(self,data):
        self.data = data
        self.next = None
​
def is_cycle(head):
    p1 = head
    p2 = head
    while p2 is not None and p2.next is not None:
        p1 = p1.next
        p2 = p2.next.next
        if p1 == p2:
            return True
    return False
​
node1 = Node(5)
node2 = Node(3)
node3 = Node(7)
node4 = Node(2)
node5 = Node(6)
node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5
node5.next = node2
print(is_cycle(node1))
5.1.2 如果链表有环,如何求出环的长度

两个指针首次相遇,证明链表有环,让两个指针从相遇点继续前进,并统计循环的次数,直到两个指针第二次相遇。此时,统计出来的前进的次数就是环长。

P1走一步,P2走两步,所以再次相遇的时候,P2走的路程是P1的两倍,多走了整整一圈。

因此,环长 = 每一次速度差 x 前进次数 = 前进次数

5.1.3 如果链表有环,如何求出入环节点

当两个指针首次相遇时,各自走的路程是多少呢?

指针P1一次走一步,所走的距离是D+S1

指针P2一次走两步,多走了n(n>=1)圈,所走的距离是D+S1+n(S1+S2)

因为P2的速度是P1的两倍,所以走的路程也应该是P1的两倍:

2(D+S1) = D+S1+n(S1+S2) ——> D = (n-1)(S1+S2)+S2

方法就是,把其中一个指针放回到头结点位置,另一个指针保持在首次相遇点,两个指针都每次向前走一步。那么,他们最终相遇的节点,就是入环节点。

5.2 最小栈的实现

5.2.1 算法实现思想

题目:实现一个栈,该栈带有出栈(pop)、入栈(push)、取最小元素(get_min)3个方法。要保证这三个方法的时间复杂度都是O(1)。

5.2.2 代码实现
最小栈
class MinStack:
    def __init__(self):
        """
        初始化栈对象
        """
        self.stack = []  # 主栈,用于存储所有元素
        self.min_stack = []  # 最小栈,用于存储当前栈中的最小值
​
    def push(self, x: int) -> None:
        """
        将元素x压入栈中
        """
        self.stack.append(x)
        # 如果最小栈为空,或者x小于等于最小栈的栈顶元素,则将x也压入最小栈
        if not self.min_stack or x <= self.min_stack[-1]:
            self.min_stack.append(x)
​
    def pop(self) -> None:
        """
        弹出栈顶元素
        """
        if not self.stack:
            return None  # 或者抛出异常,视具体需求而定
        # 如果弹出的元素等于最小栈的栈顶元素,则最小栈也需要弹出
        if self.stack[-1] == self.min_stack[-1]:
            self.min_stack.pop()
        self.stack.pop()
​
    def top(self) -> int:
        """
        获取栈顶元素,题目未直接要求,但通常栈会提供此方法
        """
        if not self.stack:
            return None  # 或者抛出异常
        return self.stack[-1]
​
    def get_min(self) -> int:
        """
        获取栈中的最小元素
        """
        if not self.min_stack:
            return None  # 或者抛出异常,表示栈为空
        return self.min_stack[-1]
​
    # 测试代码
​
​
if __name__ == "__main__":
    minStack = MinStack()
    minStack.push(-2)
    minStack.push(0)
    minStack.push(-3)
    print(minStack.get_min())  # 输出: -3
    minStack.pop()
    print(minStack.top())  # 输出: 0
    print(minStack.get_min())  # 输出: -2

5.3 最大公约数

文心一言

欧几里得算法
# 欧几里得算法
'''
def func1(x,y):
    while y > 0:
        (x,y) = (y,x%y)
    return x
print(func1(12,8))
'''
辗转相减法
# 辗转相减法
'''
def func2(x,y):
    if x == y:
        return x
    big = max(x,y)
    small = min(x,y)
    return func2(big-small,small)
print(func2(12,8))
'''

5.4 2的幂

如何判断一个数是否为2的整数次幂

给定一个非负整数n,请问是否存在一个x满足2^x=n,

如果有,则返回True,否则返回false

# 方法一 利用按位与运算来解决
# 0100(4)   1000(8)
# 0011(3)   0111(7)
'''
def func(n):
    return n>0 and n & (n-1) == 0
​
print(func(32))
print(func(31))
'''
# 方法二 除以2
'''
def func(n):
    while n>0 and n%2 != 1:
        n /= 2
        return True
    return False
print(func(32))
print(func(31))
'''

5.5 无序数组排序后的最大相邻差

5.5.1 算法思想:

运用计数排序的思想

区间长度K = max - min + 1

偏移量D = min

建立一个长度为K的数组Array,其值全部为0

还有一种优化思想桶排序,但是我没看懂... ...

5.5.2 代码表示

文心一言

def maximumGap(nums):
    if len(nums) < 2:
        return 0
​
        # 找到数组中的最大值和最小值
    min_val = min(nums)
    max_val = max(nums)
​
    # 如果最大值和最小值相同,则没有差值
    if min_val == max_val:
        return 0
​
        # 初始化计数数组,长度为最大值和最小值之间的差值加1
    # 并将所有元素初始化为0
    bucket_size = max(1, (max_val - min_val) // (len(nums) - 1))  # 保证至少有一个元素在每个桶中
    bucket_count = (max_val - min_val) // bucket_size + 1
    bucket_min = [float('inf')] * bucket_count
    bucket_max = [float('-inf')] * bucket_count
​
    # 填充计数数组
    for num in nums:
        idx = (num - min_val) // bucket_size
        bucket_min[idx] = min(bucket_min[idx], num)
        bucket_max[idx] = max(bucket_max[idx], num)
​
        # 找到非空桶之间的最大差值
    previous_max = bucket_max[0]  # 第一个桶的最大值(已排序数组的第一个元素)
    max_gap = 0
    for i in range(1, bucket_count):
        if bucket_min[i] == float('inf'):  # 如果桶是空的,则跳过
            continue
        max_gap = max(max_gap, bucket_min[i] - previous_max)
        previous_max = bucket_max[i]
​
    return max_gap
​
​
# 示例
nums = [3, 6, 9, 1]
print(maximumGap(nums))  # 输出应为3,因为排序后的数组为[1, 3, 6, 9],最大相邻差为6-3=3

5.6 如何利用栈实现队列

5.6.1 算法思想

栈是一种线性数据结构,先进后出(FILO)。栈底(bottom)栈顶(top)

队列是一种线性数据结构,先入先出(FIFO)。队头(front)队尾(rear)

1.入队(enqueue)注:队尾位置规定为最后入队元素的下一位置,只允许在队尾的位置放入元素,新元素的下一位置将会成为新的队尾。

2.出栈(dequeue)只允许在队头一侧移出元素,出队元素的最后一个元素将成为新的队头。

我们创建两个栈分别用来存放原始栈、即原始队列(A)和原始栈的pop操作进入下一个栈(B),即实现原始栈的出栈,让元素进行进栈操作完毕之后进行出站进入到栈B,可以实现栈B的出栈,从而实现了队列先入先出的特点。

5.6.2 代码实现(图绘)
# 栈实现队列
class StackQueue:
    def __init__(self):
        self.stackA = []
        self.stackB = []
    def en_queue(self,e):
        self.stackA.append(e)
​
    def de_queue(self):
        if len(self.stackB) == 0:
            if len(self.stackA) == 0:
                print('栈已空')
            self.transfer()
        return self.stackB.pop()
    def transfer(self):
        while len(self.stackA) > 0:
            self.stackB.append(self.stackA.pop())
​
stack_queue = StackQueue()
stack_queue.en_queue(1)
stack_queue.en_queue(2)
stack_queue.en_queue(3)
print(stack_queue.de_queue())
print(stack_queue.de_queue())
stack_queue.en_queue(4)
print(stack_queue.de_queue())
print(stack_queue.de_queue())

5.7 寻找全排列的下一个数

5.7.1算法思想

题目:给出一个正整数,找出这个正整数所有数字全排列的下一个数。

在一个整数所包含数字的全部组合中,找到一个大于且仅大于原数的新整数。

例:输入12345,返回12354

输入12354,返回12435

输入12435,返回12453

尽量保持高位不变,低位在最小范围内变换顺序。至于变换顺序的范围大小,则取决于当前整数的逆序区域。

步骤:

  1. 从后向前查看逆序区域,找到逆序区域的前一位,也就是数字置换的边界。

  2. 让逆序区域的前一位和逆序区域中大于它的最小数字交换位置。

  3. 把原来的逆序区域转换为顺序状态。

(书上这样写的但是我感觉有bug,132546->132564,但照着它思想就是162543)

5.7.2 代码实现
def next_permutation(num):
    # 将整数转换为列表以便操作
    digits = list(str(num))
    n = len(digits)
​
    # 步骤1: 从后向前找到逆序区域的开始
    i = n - 2
    while i >= 0 and digits[i] >= digits[i + 1]:
        i -= 1
​
        # 如果找不到逆序区域(即整个序列是降序的),则无法生成更大的排列
    if i < 0:
        return -1  # 或者可以根据需要抛出异常或返回原数
​
    # 步骤2: 在逆序区域中找到比digits[i]大的最小数字
    j = n - 1
    while digits[j] <= digits[i]:
        j -= 1
​
        # 交换digits[i]和digits[j]
    digits[i], digits[j] = digits[j], digits[i]
​
    # 步骤3: 反转逆序区域(i+1到末尾)以形成升序
    left, right = i + 1, n - 1
    while left < right:
        digits[left], digits[right] = digits[right], digits[left]
        left += 1
        right -= 1
​
        # 将列表转换回整数并返回
    return int(''.join(digits))
​
​
# 测试代码
print(next_permutation(132546))  # 输出: 12354
print(next_permutation(12354))  # 输出: 12435
print(next_permutation(12435))  # 输出: 12453
print(next_permutation(98765))  # 输出: -1 或者抛出异常,因为没有更大的排列

还可以哈,有几个算法我总感觉有点问题,不知道你们发现了没有,芜湖...

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值