剑指Offer and Leetcode刷题总结之xx:其他面试题

目录

 

1. n个[0,n)的数,求每个数的出现次数(不能开辟额外空间)

2. 约瑟夫环问题(题解参考)

3. 图的BFS and DFS遍历(题解参考 and 题解参考2)

3. 圆环回原点问题(题解参考)

4. Leetcode164:最大间距

5. 有A和B两个有序数组(数组元素不重复),给出sum,请找到A和B中所有相加和为sum的序列对

6. 找出数组中的k数

7. 重写Equals

8. Leetcode50:Pow(x, n)

9. AUC计算

10. 二分查找和牛顿迭代求平方根

12. 列表构建二叉树

13. 保留有序链表中的重复元素,并且只保留一次

14. 快排(递归and非递归)||Leetcode75:颜色分类(荷兰国旗问题)||剑指 Offer 45. 把数组排成最小的数

17. 从n个元素中等概率地抽取m个元素(蓄水池抽样)(参考题解1)(参考题解2)

18. 归并排序(题解参考)

19. Leetcode148:排序链表(未调试成功)

20. 单例(题解参考1)(题解参考2)

21. threading实现多线程(题解参考)

22. 找出100以内的所有质数(题解参考)

23. 随机洗牌算法(题解参考)

24. 稀疏矩阵存储以及相乘


1. n个[0,n)的数,求每个数的出现次数(不能开辟额外空间)

基本思路

  • n 个不超过 n 的数字,可以利用取模和除数来思考
  • 可以遍历所有数据,然后在每个数据作为下标的位置处加上一个 n ,此时:
  • 每个位置的数据取余 n 就是其原始数据
  • 每个位置的数据除以 n 就是其出现次数

剑指Offer and Leetcode刷题总结之xx:其他面试题_第1张图片

def numberAppearTimes(nums):
    if not nums: return None
    size = len(nums)
    for i in range(size):
        nums[nums[i] % size] += size
    ans = []
    for i in range(size):
        ans.append((i, nums[i] // size))
    return ans
a = [1, 2, 1, 4, 1]
print(numberAppearTimes(a))

2. 约瑟夫环问题(题解参考)

题目:有n个人围成一圈,1..n,从1开始报数,每次踢出报到k的人。然后下一个继续从1开始报数,输出环中的最后一个人。

基本思路:1. 考虑到k=1的情况,直接输出最后一个人;2. 考虑计数大于len(people),需要对len(people)取模;

def josephus(n, k):
    """
    n:总共有n个人围成一圈
    k:每次踢出排名第k的人
    """
    if k == 1:
        print('survive:', n)          # 特殊情况,最后一个人留下来
    p = 0
    people = list(range(1, n+1))      # 编号是从1开始的
    while True:
        if len(people) == 1:
            break                     # 剩下最后一个人,跳出来
        p = (p + (k-1)) % len(people) # 考虑到k大于len(people)的情况,也考虑到编号遍历时大于len(people)的情况
        print('kill:', people[p])
        del people([p])               # 找到该节点后删除
    print('survive:', people[0])

3. 图的BFS and DFS遍历(题解参考 and 题解参考2)

基本思路

from queue import Queue
# Definition for a node
class Node:
    def __init__(self, val):
        self.val = val
        self.next = None
class Solution:
    """
    根据邻接表创建有向图
    """
    def __init__(self, adj_list):
        self.adj_list = adj_list
        self.peaks = []
        self.visited = [False] * len(self.adj_list)
        
    def bfs(self):
        """
        广度优先遍历,时间复杂度O(n)
        """
        queue = Queue()
        self.visted = [False] * len(self.adj_list) # 存储顶点的访问状态
        queue.put(0)   # 默认从邻接表第0个邻接点开始,这里队列存的是index,以下基于index来
        self.visted[0] = True
        self.peaks.append(self.adj_list[0][0])
        while not queue.empty():
            index = queue.get() # 出队
            for i in self.adj_list[index][1]: # 遍历每个顶点的连接点list
                if not self.visited[i]:       # 判断是否访问过
                    self.visited[i] = True
                    queue.put(i)              # 将顶点所对应的index入队
                    self.peaks.append(adj_list[i][0]) # 打印顶点名称
        return self.peaks
    
    def dfs(self, node):
        """
        递归调用深度优先遍历,时间复杂度O(n)
        node:其实节点的index
        """
        if self.visited[node] == True: return
        self.visited[node] = True
        self.peaks.append(self.adj_list[node][0])
        for index in self.adj_list[node][1]:
            if not self.visited[index]:
                self.dfs(index)
        return self.peaks
if __name__ == '__main__':
    adj_list = [
        ("a",[4,2,1]),
        ("b",[2]),
        ("c",[4,3]),
        ("d",[4]),
        ("e",[]),
        ]
    solution = Solution(adj_list)
#     print(solution.bfs())
    print(solution.dfs(0))

3. 圆环回原点问题(题解参考)

题目:一个环上有10个点,编号为0-9,从0点出发,每步可以顺时针到下一个点,也可以逆时针到上一个点,求:经过n步又回到0点有多少种不同的走法。

基本思路:再回到0点可以从右面回来,也可以从左面回来,即先到达旁边的一个点,看看有多少回来的方法;d(k, j) = d(k-1, j-1) + d(k-1, j+1);d(k, j)表示从点j走k步到达原点0的方法数,因此可以转化为他相邻的点经过k-1步回到原点的问题,这样将问题的规模缩小.由于是环的问题, j-1, j+1可能会超出 0到n-1的范围,因此,我们将递推式改成如下:d(k, j) = d(k-1, (j-1+n)%n) + d(k-1, (j+1)%n);

def getstep(n, k):
    """
    n:环的元素个数,圆环从0点开始,最大值为n-1
    k:回到原点走的步数
    """
    if n == 0: return 0 # 没有元素;
    if n == 1 and k == 0: return 1 # 1个元素,走0步,1种走法;
    # 自己define max(n, k) <= 100
    dp = [[0 for _ in range(100)] for _ in range(100)]
    # dp[0][0] = 0;
    for i in range(1, n):
        dp[0][i] = 0 # 在圆环内任意一个非原点的点走k=0步到回到原点的走法为0
    for i in range(1, k + 1):
        for j in range(n):
            dp[i][j] = dp[i-1][(j-1+n) % n] + dp[i-1][(j+1) % n] # 可以从右边回来,也可以从左边回来,所以j-1和j+1两种情况;同时考虑到j-1要满足0..n-1,额外+n再取模
    return dp[k][0]  # 从0开始,走k步最后又回到0点;

4. Leetcode164:最大间距

题目:给定一个无序的数组,找出数组在排序之后,相邻元素之间最大的差值。如果数组元素个数小于 2,则返回 0。(要求线性时间和空间复杂度

示例输入: [3,6,9,1] 输出: 3 解释: 排序后的数组是 [1,3,6,9], 其中相邻元素 (3,6) 和 (6,9) 之间都存在最大差值 3。

基本思路:1. 先排序,再遍历;(时间复杂度O(nlogn),不满足时间复杂度)

5. 有A和B两个有序数组(数组元素不重复),给出sum,请找到A和B中所有相加和为sum的序列对

示例:A:[1,4,5] and B:[3,4,6] and sum=8 --> output 1,1,2,0 => because A[1]+B[1]=8 /A[2]+B[0]=8

基本思路:前后双指针 + hashmap

"""
方法1:前后双指针
时间复杂度:max(O(nlogn), O(mlogm)) if not sorted; O(m + n) if sorted
空间复杂度:O(1)
"""
def findSumIndex(nums1, nums2, target):
    # if unsorted, need sort;
    nums1.sort()
    nums2.sort()
    ans = []
    if not nums1 or not nums2: return []
    i, j = 0, len(nums2)
    while i < len(nums1) and j >= 0:
        if nums1[i] + nums[j] == target:
            ans.append([i, j])
            i += 1
            j -= 1
        elif nums1[i] + nums[j] > target:
            j -= 1
        else:
            i += 1
    return ans
"""
方法2:hashmap -- 将长度更短的一个nums构建hashmap, 遍历另一个nums查找
时间复杂度:max(O(n), O(m))
空间复杂度:min(O(n), O(m))
""" 

6. 找出数组中的k数

题目:k数的定义:比位置位于这个数前面的数字都大,且比位置位于这个数后面的数字都小的数字(数组首尾的数字不用管)

示例:[4,1,3,2,7,9,8,10,12] --> [7, 10]

基本思路:遍历第一遍find符合比左边值都大的index, 遍历第二遍找到比右边值都小的index,同时结合第一遍遍历的结果,得到同时满足条件的index;

"""
方法1:
遍历第一遍find符合比左边值都大的index, 遍历第二遍找到比右边值都小的index,同时结合第一遍遍历的结果,得到同时满足条件的index;
时间复杂度:O(n)
空间复杂度:O(n) -- 辅助数组
"""
def knums(nums):
    if not nums: return -1
    size = len(nums)
    mi = [False for _ in range(size)]
    min_val = nums[0]
    max_val = nums[size - 1]
    ans = []
    for i in range(1, size):
        if nums[i] > min_val:
            mi[i] = True
            min_val = max(min_val, nums[i])
    for j in range(size-1, -1, -1):
        if nums[j] < max_val:
            max_val = min(max_val, nums[j])
            if mi[j] == True:
                ans.append(nums[j])
    return ans

7. 重写Equals

题目:两个字符串,按照规则判断相等,规则是两个字符串相同字符出现的次数相同,遍判定相等。例(AAB 和 ABA 相等)

基本思路:1. 两个hashmap;2. 转换为数组,再sort;最后遍历比对;

8. Leetcode50:Pow(x, n)

题目:实现 pow(xn) ,即计算 x 的 n 次幂函数。

示例:输入: 2.00000, 10 输出: 1024.00000

基本思路:1. 递归法;2.快速幂法;

class Solution(object):
    """
    方法1:递归
    时间复杂度:递归调用次数O(logn)
    空间复杂度:递归调用栈所占空间O(logn)
    """
    def myPow(self, x, n):
        """
        :type x: float
        :type n: int
        :rtype: float
        """
        if n == 0: return 1
        if n < 0:                           # 需要考虑负数,如果不考虑,2^-2这个case,首先变为(2*2)^-1,下一层递归又变为(2*2)^-2
            x, n = 1 / x, -n
        if n % 2:
            return x * self.myPow(x, n - 1) # 如果n为奇数,n-1即可,在下一轮递归中即为偶数,可二分
        return self.myPow(x * x, n / 2)
    
    """
    方法2:快速幂
    时间复杂度:O(logn) -- 分而治之,n不断进行二分
    空间复杂度:O(1)
    """
    def myPow(self, x, n):
        if n == 0: return 1
        if n < 0:
            x, n = 1 / x, -n
        ans = 1
        while n:       # case1: x^5 = x^(2^2 + 2^0) -- 5(二进制:101)
            if n & 1:  # case2: x^10= x^(2^3 + 2^1) -- 5(二进制:1010)
                ans *= x
            x *= x     # x^(2^0) --> x^(2^1) --> x^(2^2) --> x^(2^3)...
            n >>= 1
        return ans

9. AUC计算

基本思路

1. 穷举所有样本对,正例样本比负例样本预测分数高+1,相等+0.5,否则+0;最后除以总对数;

2. 直接根据公式计算;

10. 二分查找和牛顿迭代求平方根

基本思路

"""
要求输出整数,这里是舍弃小数部分后的整数
"""
class Solution(object):
    def mySqrt(self, x):
        """
        :type x: int
        :rtype: int
        """
        lo, hi = 0, x  #// 2 + 1 这部分加和不加都没有影响
        while (lo < hi):
            mid = (lo + hi + 1) >> 1
            if mid * mid > x:
                hi = mid - 1
            else:
                lo = mid
        return lo
"""
不能这样写的原因是8的sqrt()之后取的是2,而不是3; mid = 2的时候,会执行lo = mid + 1;
只能是从较大值往较小值出缩减,比如mid = 3的时候,mid * mid > x,那么最终结果一定是< mid,所以右边界hi = mid - 1,同理,当剩下最后两个值时,取的需要是右边界的数字,所以需要mid = (lo + hi + 1) >> 1
"""
        lo, hi = 0, x   # // 2 + 1 这部分加和不加都没有影响
        while (lo < hi):
            mid = (lo + hi) >> 1
            if mid * mid < x:
                lo = mid + 1
            else:
                hi = mid
        return lo
"""
要求保留精度
方法1:二分法
时间复杂度:O(logn)
"""
from math import sqrt
def sqrt_binary(x):
    lo, hi = 0.0, x  # 这里一定要注意变为float形式,否则之后做运算的时候一直是int形式
    while lo <= hi:  
        mid = lo + (hi - lo) / 2 # 这里也不能写成位运算的形式,只有int可以应用位运算
        ans = mid
        if abs(mid * mid - x) <= 1e-6:
            break
        elif mid * mid < x:
            lo = mid
        else:
            hi = mid
    return ans
"""
方法1:牛顿法
基本思路:相当于求解 f(x) = x^2 - A的根,这里的A是sqrt(A)需要求的. 
迭代公式Xk+1 = Xk - f(x) /f'(x) = X - (x^2 - A)/(2 * x) = 0.5 * (x + A / x)
时间复杂度
"""
def sqrt_newton(x):
    ans = x
    while abs(ans * ans - x) > 1e-6:
        ans = (ans + x / ans) / 2
    return ans

12. 列表构建二叉树

基本思路:注意这一点--parentNum = len(li) // 2 - 1 # 这个需要再研究一下, (貌似完全二叉树才是这种情况,index在parentNum之后的都没有leaf node了)

def createTree(alist):
    """
    alist: list
    """
    li = []
    for a in alist:
        li.append(TreeNode(a))
    parentNum = len(li) // 2 - 1 # 这个需要再研究一下, (貌似完全二叉树才是这种情况,index在parentNum之后的都没有leaf node了)    
    for i in range(parentNum + 1):
        leftIndex = 2 * i + 1
        rightIndex = 2 * i + 2
        if leftIndex < len(li): li[i].left = li[leftIndex]
        if rightIndex < len(li):li[i].right= li[rightIndex]
    return li[0]

13. 保留有序链表中的重复元素,并且只保留一次

基本思路:见代码

def deleteduplicate(head):
    if not head: return head
    p = head
    while p.next:
        if p.val == p.next.val:
            p.next = p.next.next
        else:
            p = p.next
    return head

14. 快排(递归and非递归)||Leetcode75:颜色分类(荷兰国旗问题)||剑指 Offer 45. 把数组排成最小的数||Leetcode179:最大数

快排

基本思路:非递归采用栈的思想,先压右边界,再压左边界;

递归:平均时间复杂度O(nlogn);平均空间复杂度O(logn)--递归调用栈;

当pivot选择不合理,退化为链表时,最坏时间复杂度O(n * n) ;最坏空间复杂度O(n),每一层递归调用栈是一个元素;

非递归:其实就是用stack来模拟函数调用栈;时间复杂度为O(nlogn);

颜色分类

题目:给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

示例输入: [2,0,2,1,1,0] 输出: [0,0,1,1,2,2]

基本思路:快排的partition思想,分四个区域(0区域,1区域,未处理区域 and 2区域)

数组排成最小的数

题目:输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。

示例输入: [3,30,34,5,9] 输出: "3033459"

基本思路:基本沿用快排框架,更改比较大小的规则即可;

最大数

题目:给定一组非负整数,重新排列它们的顺序使之组成一个最大的整数。

示例:输入: [3,30,34,5,9]输出: 9534330

基本思路:与上一题思路完全相同;

from random import randint
def partition(nums, lo, hi):
    """
    nums: list for sort
    lo: the start index
    hi: the last index
    """
    i, j = lo, hi + 1
    ind = randint(lo, hi)
    nums[lo], nums[ind] = nums[ind], nums[lo]
    pivot = nums[lo]
    while i + 1 < j:
        if nums[i+1] <= s[i]:
            nums[i+1], nums[i] = nums[i], nums[i+1]
            i += 1
        else:
            nums[i+1], nums[j-1] = nums[j-1], nums[i+1]
            j -= 1
    return i

def quick_sort(nums, lo, hi ):
    if lo < hi:
        pivot = partition(nums, lo, hi)
        quick_sort(nums, lo, pivot - 1)
        quick_sort(nums, pivot + 1, hi)
    return nums

def quick_sortStack(nums, lo, hi):
    if hi <= lo: return 
    stack = []
    stack.append(hi) # 右边界先入栈,左边界后入栈;出栈时,按照left, and then right出栈
    stack.append(lo)
    while stack:
        lo = stack.pop()
        hi = stack.pop()
        pivot = partition(nums, lo, hi)
        if lo < pivot - 1:   
            stack.append(pivot-1)
            stack.append(lo)
        if hi > pivot + 1:
            stack.append(hi)
            stack.append(pivot + 1)
    return nums

######################荷兰国旗######################
class Solution(object):
    def sortColors(self, nums):
        """
        :type nums: List[int]
        :rtype: None Do not return anything, modify nums in-place instead.
        """
        size = len(nums)
        if size < 2: return nums
        k, i, j = -1, -1, size
        while i + 1 < j:
            if nums[i+1] == 2:
                nums[i+1], nums[j-1] = nums[j-1], nums[i+1]
                j -= 1
            elif nums[i+1] == 1:
                i += 1
            else:
                nums[i+1], nums[k+1] = nums[k+1], nums[i+1]
                k += 1
                i += 1
        return nums

######################把数组排成最小的数######################
class Solution(object):
    def minNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: str
        """
        from random import randint
        nums = [str(i) for i in nums]
        def partition(nums, lo, hi):
            i, j = lo, hi + 1
            ind = randint(lo, hi)
            nums[lo], nums[ind] = nums[ind], nums[lo]
            pivot = nums[lo]
            while i + 1 < j:
                if nums[i + 1] + pivot <= pivot + nums[i + 1]:
                    nums[i + 1], nums[i] = nums[i], nums[i + 1]
                    i += 1
                else:
                    nums[i + 1], nums[j - 1] = nums[j - 1], nums[i + 1]
                    j -= 1
            return i
        def fastsort(nums, lo, hi):
            if hi <= lo: return
            pivot =  partition(nums, lo, hi)
            fastsort(nums, lo, pivot - 1)
            fastsort(nums, pivot + 1, hi)
        size = len(nums)
        if size == 0: return ''
        if size == 1: return str(nums[0])
        fastsort(nums, 0, len(nums)-1)
        return ''.join(nums)

######################把数组排成最大的数######################
class Solution(object):
    def largestNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: str
        """
        from random import randint
        nums = [str(i) for i in nums]
        def partition(nums, lo, hi):
            i, j = lo, hi + 1
            ind = randint(lo, hi)
            nums[lo], nums[ind] = nums[ind], nums[lo]
            pivot = nums[lo]
            while i + 1 < j:
                if nums[i + 1] + pivot <= pivot + nums[i + 1]:
                    nums[i + 1], nums[i] = nums[i], nums[i + 1]
                    i += 1
                else:
                    nums[i + 1], nums[j - 1] = nums[j - 1], nums[i + 1]
                    j -= 1
            return i
        
        def fastsort(nums, lo, hi):
            if hi <= lo: return
            pivot =  partition(nums, lo, hi)
            fastsort(nums, lo, pivot - 1)
            fastsort(nums, pivot + 1, hi)
        size = len(nums)
        if size == 0: return ''
        if size == 1: return str(nums[0])
        fastsort(nums, 0, len(nums)-1)
        for e in nums:          ## 目的是为了处理[0, 0]这个bad case;
            if e != '0': 
                return ''.join(nums[::-1])
        return '0'

17. 从n个元素中等概率地抽取m个元素(蓄水池抽样)(参考题解1)(参考题解2)

基本思路

1. 等概率抽样
每次从n个元素中利用随机函数逐个抽取,如果发现该次抽取的元素在之前已经被成功抽取过,则重新抽取直至抽到新元素;
缺点:随着m的增大,越到后面重复的概率越高;cost比较大;

2. 蓄水池抽样
先将前m个元素放入蓄水池中,从m+1个元素开始抽取,同时以m / n的概率替换掉已经存在于蓄水池中的数字;(数学归纳法证明)

"""
方法1:等概率抽样
每次从n个元素中利用随机函数逐个抽取,如果发现该次抽取的元素在之前已经被成功抽取过,则重新抽取直至抽到新元素;
缺点:随着m的增大,越到后面重复的概率越高;cost比较大;
"""
"""
方法2:蓄水池抽样
先将前m个元素放入蓄水池中,从m+1个元素开始抽取,同时以m / n的概率替换掉已经存在于蓄水池中的数字;
"""
from random import randint
def sampling(n, m):
    size = len(n)
    for i in range(m, size): # 0..m-1总共m个数
        j = randint(0, i)    # randint(a, b) 包括a和b
        if j < m:
            n[i], n[j] = n[j], n[i]
    return n[:m]

18. 归并排序(题解参考)

-->下面一题则是应用:此题是数组,下一题是链表

基本思路:时间复杂度O(nlogn) and 空间复杂度O(n)

def merge_sort(nums):
    size = len(nums)
    if size < 2: return nums
    mid = size // 2
    left = merge_sort(nums[:mid])
    right = merge_sort(nums[mid:])
    return merge(left, right)

def merge(left, right):
    ans = []
    i = j = 0
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:  # 这里一定要取等号,保持归并的稳定性
            ans.append(left[i])
            i += 1
        else:
            ans.append(right[j])
            j += 1
    ans += left[i:]    # 如果right先遍历完所有元素,在最后append上left的剩余元素
    ans += right[j:]   # 如果left先遍历完所有元素,在最后append上right所有剩余元素
    return ans

19. Leetcode148:排序链表(未调试成功)

题目:在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。

示例输入: 4->2->1->3 输出: 1->2->3->4

基本思路:归并排序

"""
方法:时间复杂度为O(nlogn) --> 归并排序
"""
class Solution(object):
    
    def sortList(self, head):

        """
        :type head: ListNode
        :rtype: ListNode
        """

        def merge2List(l1, l2):
            if not l1: return l2
            if not l2: return l1
            if l1.val <= l2.val:
                l1.next = merge2List(l1.next, l2)
                return l1
            else:
                l2.next = merge2List(l1, l2.next)
                return l2

        # 快慢指针找到中点,切分两条链表
        if not head:
            return head
        sl = fa = head
        while fa and fa.next:
            fa, sl = fa.next.next, sl.next
        l1, l2 = head, sl.next
        sl.next = None
        left = self.sortList(l1)
        right = self.sortList(l2)
        return merge2List(left, right)

20. 单例(题解参考1)(题解参考2)

基本概念:单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。

1. 使用模块

2. 使用装饰器

3. 使用类

4. 基于__new__方法实现(推荐使用,方便)--属于饿汉模式?:

原理:在python的类创建对象的过程中,先通过__new__方法实例化一个对象(PS:在没有自己定义该方法时会默认调用object.new),然后才执行__init__方法对实例化的对象进行各项初始化复制操作。

实现单例时,为了保证线程安全需要在内部加入锁。当实例化一个对象时,是先执行了类的__new__方法(我们没写时,默认调用object.__new__),实例化对象;然后再执行类的__init__方法,对这个对象进行初始化,所有可以基于这个,实现单例模式。

"""
方法1:实现1 -- 不考虑加锁
"""
class Singleton:
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'): # hasattr()函数用于判断对象是否包含对应的属性
            cls._instance = super().__new__(cls) 
            # cls._instance = super(Singleton, cls).__new__(cls)
        return cls._instance
"""
方法1:实现2
"""
class Singleton:
    _instance = None # 定义一个类属性做判断
    def __new__(cls):
        if cls._instance == None:
            # 如果_instance为空证明是第一次创建实例
            # 通过父类的__new__(cls)创建实例
            cls._instance == object.__new__(cls)
            return cls._instance
       else:
            # 返回上一个对象的引用
            return cls._instance

"""
方法2:考虑加锁
"""
import threading
class Singleton(object):
    _instance_lock = threading.Lock()

    def __init__(self):
        pass

    def __new__(cls, *args, **kwargs):
        if not hasattr(Singleton, "_instance"):
            with Singleton._instance_lock: # 加锁防止多线程环境中两个线程同时判断到上一行代码为True,同时创建该类的实例
                if not hasattr(Singleton, "_instance"):
                    # 调用object类的__new__方法
                    Singleton._instance = object.__new__(cls)
                    # Singleton._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)  
        return Singleton._instance

obj1 = Singleton()
obj2 = Singleton()
print(obj1,obj2)

def task(arg):
    obj = Singleton()
    print(obj)

for i in range(10):
    t = threading.Thread(target=task,args=[i,])
    t.start()

21. threading实现多线程(题解参考)

"""
可以通过直接从threading.Thread继承创建一个新的子类,并实例化后调用start()方法启动新线程,即它调用了线程的run()方法:
--通过继承treading.Thread,并重写run函数
--join() 调用这个方法的时候,主进程会在这里停住,等待该线程进行完毕再继续往下执行;
Why join()? -- 线程执行时间过长,而且要在后续代码中使用线程运行的结果,这样可以使用join,防止主线程比子线程先结束,
               或者后续代码在线程结束之前就运行了从而获取不到线程处理结果。
使用join会导致进程阻塞,这个阻塞并不是目的,只是表现。并不是为了阻塞线程而使用join.
"""
import threading
import time

exitFlag = 0
class myThread(threading.Thread):
    def __init__(self, threadID, name, counter):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter
    def run(self): # 重写run函数
        print("开始线程:" + self.name)
        print_time(self.name, self.counter, 5)
        print("退出线程:" + self.name)

def print_time(threadName, delay, counter):
    while counter:
        if exitFlag:
            threadName.exit()
        time.sleep(delay)
        print("%s: %s" % (threadName, time.ctime(time.time())))
        counter -= 1

# 创建新线程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# 开启新线程
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print("退出主线程")

22. 找出100以内的所有质数(题解参考)

基本思路:两个for循环,外层遍历2-100,2是最小的质数,内层从2开始到i结束(质数:除了1和本身,没有其他因子)

i=2   
for i in range(2,100):  # 取i从2开始,2 最小的质数
	for j in range(2,i):     #j在i的范围内取,range区间取不到i,看i是否有其他因数
		if i%j==0:
			break
	if i%j!=0:  #最好用if 如果用else不规范
		print(i)

23. 随机洗牌算法(题解参考)

基本思路:

1. 有n!种组合,生成n!种组合之后,在里面任意选一种即可,也就是等概率地给出这n!结果中任意一个。但是复杂度太高。

2. 关键点:对于生成的排列,每一个元素都能独立地等概率地出现在每一个位置。或者说每一个位置能独立等概率地放置每个元素

3. 公平性证明(eg. 仅有5个元素,最后一位元素被固定在最后一个位置的概率是1/5;倒数第二个元素被固定在倒数第二个位置的概率是4/5 * 1/4 = 1/5,其他同理;)

for i in range(len(nums)-1, -1, -1):
    swap(nums[i], nums[randint(0, i)]) # randint(0, i) 生成[0, i]之间的随机整数

24. 稀疏矩阵存储以及相乘

题解参考:(不完成正确)Python利用三元组完成稀疏矩阵相乘

import numpy as np
def sparseToTriple(matrix):
    """
    matrix: sparse matrix
    return: triple dict, rows, columns
    """
    m, n = np.shape(matrix)
    triple = {}
    for i in range(m):
        for j in range(n):
            if matrix[i][j] != 0:
                triple[(i, j)] = matrix[i][j]
    return triple, [m, n]

def multiTriple(tripleA, shapeA, tripleB, shapeB):
    """
    return: tripleA * tripleB, shape
    
    """
    if shapeA[1] != shapeB[0]: return 'Error' # 无法相乘
    multiMatrix = {}
    for (i, j) in tripleA:
        for (m, n) in tripleB:
            if j == m: # matrixA.column == matrix.row时,可以相乘,对应的结果放在(matrixA.row, maxtrixB.column)
                if not multiMatrix.get((i, n)):
                    multiMatrix[(i, n)] = tripleA[(i, j)] * tripleB[(m, n)]
                else:
                    multiMatrix[(i, n)] += tripleA[(i, j)] * tripleB[(m, n)]
    # 相乘的结果为0,也需要删除
    for key in multiMatrix: 
        if multiMatrix[key] == 0:
            multiMatrix.pop(key)
    return multiMatrix, [shapeA[0], shapeB[1]]

def tripleToSparse(triple, shape):
    outMatrix = zeros([shape[0], shape[1]])
    for point in triple:
        outMatrix[point[0]][point[1]] = triple[(point[0], point[1])]
    return outMatrix

def matrixMultiple(matrixA, matrixB):
    rowA, colA = np.shape(matrixA)
    rowB, colB = np.shape(matrixB)
    if colA != rowB:
        print("The two matries doesn't match")
        return -1
    tripleA, shapeA = sparseToTriple(matrixA)
    tripleB, shapeB = sparseToTriple(matrixB)
    multipleTriples, shape = multiTriple(tripleA, shapeA, tripleB, shapeB)
    outMatrix = tripleToSparse(multipleTriples, shape)
    return outMatrix

25. 排序数组中绝对值不同的个数

基本思路:前后双指针;类似于归并排序,但是是从大到小;

def differentAbs(nums):
    size = len(nums)
    if size <= 1: return size
    i, j = 0, size - 1
    if abs(nums[j]) >= abs(nums[i]):
        curNum = abs(nums[j])
        j -= 1
    else:
        curNum = abs(nums[i])
        i += 1
    count = 1
    while i < j:
        if abs(nums[j]) >= abs(nums[i]):
            if abs(nums[j]) != curNum:
                curNum = abs(nums[j])
                count += 1
            j -= 1
        else:
            if abs(nums[i]) != curNum:
                curNum = abs(nums[i])
                count += 1
            i += 1
        print(curNum, i, j, count)
    return count

26. 从左上到右下的路径总数(已知行数rows and列数columns)

基本思路:动态规划状态方程dp[i][j] = dp[i-1][j] + dp[i][j-1]

Follow up:空间and时间复杂度优化--只保留两行,空间复杂度优化为O(2*n)

def sumPath(m, n):
    dp = [[0 for _ in range(n)] for _ in range(2)]
    for i in range(n):
        dp[0][i] = 1
    j = 0
    while j < m:
        dp[1][0] = 1
        for i in range(n):
            dp[1][i] = dp[1][i-1] + dp[0][i]
        dp[0] = dp[1]
    return dp[-1][-1]

27. 扑克牌问题

题目:手上一叠扑克牌,从扑克牌的最上方开始拿一张放到桌面上,再将下一张牌放到最底部,重复这个步骤直至手上没有扑克牌为止;<给定桌上的扑克牌的顺序,求最初手上的一堆牌的顺序>

基本思路:逆向思考;从桌子上的最后一张牌开始拿回手上,再将最底部的牌放至最顶部,重复这个步骤直到桌上的牌全部都被拿至手上。

def poker(nums):
    if not nums: return None
    size = len(nums)
    if size == 1: return nums[0]
    ans = []
    while nums:
        ans.append(nums.pop())
        ans.append(ans.pop(0))
    return ans

 

你可能感兴趣的:(剑指Offer and Leetcode刷题总结之xx:其他面试题)