数据结构与算法学习笔记(python)——第二节 数组和动态数组

前言

本人是一个长期的数据分析爱好者,最近半年的时间的在网上学习了很多关于python、数据分析、数据挖掘以及项目管理相关的课程和知识,但是在学习的过程中,过于追求课程数量的增长,长时间关注于学习了多少多少门课程。事实上,学完一门课之后真正掌握的知识并不多,主要的原因是自己没有认真学习和理解温故而知新的这句话的真正含义。因此,从现在开始,我在学习《数据结构与算法——基于python》的课程内容之后,抽出固定的时间对每天学习的内容进行总结和分享,一方面有助于个人更好的掌握课程的内容,另一方面能和大家一起分享个人的学习历程和相应的学习知识。

第二节 数组和动态数组

## 基础知识:

2.1 动态数组大小为什么可以改变:当原有的空间内存满了之后,会在内存中新开一个原来空间两倍大的空间,然后将原来的元素复制到新的空间,原来的空间会被清空回收。
2.2 append.() o(1),insert.() o(n)、deleate.() o(n),
对于无序的数组,查找的时间复杂度也是o(n);
2.4 数组在做切片操作的时候,是首先将数据复制过来,然后再操作,不会修改原始数据;
np.arange做切片操作的时候会修改原始数据,修改之后会改变原始数据;
Shallow Copy和Deep Copy,在修改被指定的元素的值之后,其他所有指向该地址元素的值均会被改变;
## 数组应用:

  1. 挖雷游戏
    问题描述:
    程序接收三个参数,M,N和p,然后生成一个M * N的矩阵,然后每一个cell有p的概率是地雷。生成矩阵后,再计算出每一个cell周围地雷的数量 。
    思路:
    对于每一个m和n,生成一个m+2*n+2的矩阵,将矩阵的最外一层的元素全部置空,用随机数生成一个0到1的数,对于小于p的位置,就放上地雷;大于p的位置,就不放上地雷;然后重新生成一个矩阵计算每个位置周围的地雷数;最后输出地雷矩阵和每个点周围的地雷数的矩阵;
    代码
import random
def minesweeper(m, n, p):
    board = [[None] * (n+2) for i in range(m+2)]
    for i in range(1, m + 1):
        for j in range(1, n + 1): # 生成地雷举证
            r = random.random()
            board[i][j] = -1 if r < p else 0

    for i in range(1, m + 1): #输出地雷举证
        for j in range(1, n + 1):
            print("*", end=" ") if board[i][j] == -1 else print(".", end=" ") 
        print()
         
        # 计算每个点周的地雷数;
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if (board[i][j] != -1):
                for ii in range(i-1, i+2):
                    for jj in range(j-1, j+2):
                        if (board[ii][jj] == -1):
                            board[i][j] += 1
    
    print()
    # 输出地雷数的矩阵
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            print("*", end=" ") if board[i][j] == -1 else print(board[i][j], end=" ")
        print()
minesweeper(5, 10, 0.2) # 初始化 m、n、p

输出结果:
在这里插入图片描述
2. 矩阵0变换
问题描述:
给一个m×n的矩阵,如果有一个元素为0,则把该元素对应的行与列所有元素全部变成0;
思路:
首先记录矩阵中哪一行和哪一列出现了零;最后对出现零的行和列进行清零处理;新加两个一位数组,一个长度为m;另外一个的长度为n;对于m*n矩阵中出现0的值就将m[i]和n[j]变为1,最后对于m[i]或n[j]= 1的位置对应行和列全部变为零;空间复杂度为o(m+n)
代码

# O(m+n) space complexity
def zero(matrix):
    m = [None] * len(matrix)
    n = [None] * len(matrix[0])
    for i in range(len(matrix)):
        for j in range(len(matrix[0])):
            if (matrix[i][j] == 0):
                m[i] = 1
                n[j] = 1
                
    for i in range(len(matrix)):
        for j in range(len(matrix[0])):
            if (m[i] == 1 or n[j] == 1):
                matrix[i][j] = 0
matrix = [  [ 1, 1, 1, 1, 1, 0, 1, 1, 1, 1 ],
            [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
            [ 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 ],
            [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
            [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] ]
for x in matrix:
    print(x, sep=" ")zero(matrix)
for x in matrix:
    print(x, sep=" ")    

输出结果
在这里插入图片描述
如图所示,将原来行或者列存在0的元素全部变成了0;
3. 九宫图
问题描述:
给一个n×n的矩阵,保证每行每列以及对角线的和都相等;
思路:
按照下图所示的方法构建矩阵,将1放在最后一行的最中间,然后逆时针旋转依次放入2、3、4等,遇到位置上已经有数字的位置,向上移动一行,再继续按照逆时针旋转即可;
在这里插入图片描述
代码

def magic_square(n):
    magic = [[0] * (n) for i in range(n)]
    row = n - 1
    col = n//2
    magic[row][col] = 1 # 1 放在最后一行的中间
    
    for i in range(2, n * n + 1):
        try_row = (row + 1) % n
        try_col = (col + 1) % n

        if (magic[try_row][try_col] == 0):
            row = try_row
            col = try_col
        else:
            row = (row - 1 + n) % n
        
        magic[row][col] = i
    
    for x in magic:
        print(x, sep=" ")
magic_square(5)

输出结果如下所示;
在这里插入图片描述
4. 数独验证
问题描述:
给一个填好的数独,验证是否正确;
思路:
数独的验证条件是在99的矩阵里面。每一行每一列都有1到9这9个数,并且在每个小的33的小矩阵里面也要有1到9这9个数。如何将上述要求用代码实现呢?这是一个问题!!!经过我上手计算,终于搞明白了老师的思路,现分享如下:
对每一行(列)的数据,建立一个9个元素的数组,对于该行出现1到9中的任意一个数i,就将该数组中的第i变成1,到最后如果所有的位置都变为了1,即可确定为满足数独的条件;
对于每个3*3的小矩阵,按照从左到右,从上到下的顺序,按照上同样的方法,出现i就将第i个元素变成1,到最后如果所有的位置都变为了1,即可确定为满足数独的条件;
第i个元素变成i的操作需要使用到二进制的位操作,具体如程序所示;
代码

matrix = [
    [5,3,4,6,7,8,9,1,2],
    [6,7,2,1,9,5,3,4,8],
    [1,9,8,3,4,2,5,6,7],
    [8,5,9,7,6,1,4,2,3],
    [4,2,6,8,5,3,7,9,1],
    [7,1,3,9,2,4,8,5,6],
    [9,6,1,5,3,7,2,8,4],
    [2,8,7,4,1,9,6,3,5],
    [3,4,5,2,8,6,1,7,9]
]
def sudoku(matrix):
    n = len(matrix)
    result_row = result_col = result_blk = 0

    for i in range(n):
        result_row = result_col = result_blk = 0
        for j in range(n):
            ## check row
            tmp = matrix[i][j]
            
            if ((result_row & (1 << (tmp-1))) == 0):
                result_row = result_row | (1<<(tmp-1))
            else:
                print("row: ", i, j)
                return False
 
            ## check column
            tmp = matrix[j][i]
            if ((result_col & (1 << (tmp-1))) == 0):
                result_col = result_col | (1<<(tmp-1))
            else:
                print("col: ", j, i)
                return False

            ## check block
            idx_row = (i//3) * 3 + j//3
            idx_col = (i%3)  * 3 + j%3
            tmp = matrix[idx_row][idx_col]
            if ((result_blk & (1 << (tmp-1))) == 0):
                result_blk = result_blk | (1<<(tmp-1))
            else:
                print("block: ", idx_row, idx_col)
                return False
    return True
sudoku(matrix)

输出结果如下所示;
在这里插入图片描述
5. 旋转数组
问题描述:
给一个n×n的数组,旋转90度;
思路:
思路一:可以新建一个数组,在新的数组里面直接赋值,使得新数组的行和原数组的列相等。
思路二:将原始的矩阵中每个元素按照下图的顺序进行旋转即可实现旋转90度的要求;
在这里插入图片描述
代码
思路1

def rotate(matrix):
    n = len(matrix)
    result = [[0] * (n) for i in range(n)]
    
    for i in range(n):
        for j in range(n):
            result[j][n-1-i] = matrix[i][j]
    
    for x in result:
        print(x, sep=" ")
matrix = [[i*5+j for j in range(5)] for i in range(5)]
rotate(matrix)

这是原始矩阵;
在这里插入图片描述
这是新矩阵;
在这里插入图片描述
由图可以看出,完成了矩阵的顺时针90度的旋转;

思路1

# in-place

def rotate_in_place(matrix):
    n = len(matrix)
    for layer in range(n//2):
        first = layer
        last = n - 1 - layer
        for i in range(first, last):
            offset = i - first
            top = matrix[first][i]  # save top
            
            ## left->top
            matrix[first][i] = matrix[last-offset][first]
            
            ##bottom -> left
            matrix[last-offset][first] = matrix[last][last - offset];

            # right -> bottom
            matrix[last][last - offset] = matrix[i][last];

            # top -> right
            matrix[i][last] = top;  # right <- saved top            
            
    for x in matrix:
        print(x, sep=" ")
 rotate_in_place(matrix)

输出结果;
在这里插入图片描述
如图所示,完成了矩阵旋转的功能。
6. 反转字符串
问题描述:
hello => olleh;
思路:
思路一:直接用python的s[::-1]。
思路二:将原始的数值中每个元素按度进行首尾替换;
代码
思路1:

def reverse(s):
    return s[::-1]
s = "hello"
r = reverse(s) # O(n)
r

思路2:

def reverse2(s):
    l = list(s)
    for i in range(len(l)//2):
        l[i], l[len(s)-1-i] = l[len(s)-1-i], l[i]
    return ''.join(l)
s = "hello"
r = reverse2(s)
r

即可完成题目要求的操作;
7. 最长连续子串
问题描述:
给一个只包含0和1的数组,找出最长的全是1的子数组。
Example:
Input: [1,1,0,1,1,1]
Output: 3;
思路:
记录出现1的个数,如果遇到0,个数清零,然后选出记录1 的最大值即可。
代码

def find_consecutive_ones(nums):
    local = maximum = 0
    for i in nums:
        local = local + 1 if i == 1 else 0
        maximum = max(maximum, local)
    return maximum
nums = [1,1,0,1,1,1,1,0,0,0,0,0,1,1,1,0,0,1]
result = find_consecutive_ones(nums)
result

输出结果如下图所示,完成了题目的要求;
在这里插入图片描述
8. 最大数
问题描述:
给定一个数组,数组里有一个数组有且只有一个最大数,判断这个最大数是否是其他数的两倍或更大。如果存在这个数,则返回其index,否则返回-1;
思路:
找到最大值和次最大值,然后判断最大值是不是次大值的二倍即可。
代码

def largest_twice(nums):
    maximum = second = idx = 0
    for i in range(len(nums)):
        if (maximum < nums[i]):
            second = maximum
            maximum = nums[i]
            idx = i
        elif second < nums[i]:
            second = nums[i]
    return idx if (maximum >= second * 2) else -1
nums = [1, 2,3,8,3,2,1]
result = largest_twice(nums)
result

输出结果;
在这里插入图片描述
找到了最大值的序号,第四个数,并且满足是其他数的2倍;

  1. 查找数组中缺少的数字
    问题描述:
    给定一个整数数组,其中1≤a [i]≤n(n =数组大小),某些元素出现两次,而另一些元素出现一次。
    查找[1,n]包含的所有未出现在此数组中的元素。
    您可以在没有额外空间的情况下在O(n)运行时执行此操作吗? 您可能会假定返回的列表不算作多余的空间。
    例:
    输入:[4,3,2,7,8,2,3,1]
    输出:[5,6]
    思路:
    思路1:直接从1到n逐个判断,是否存在数组中;
    思路2:对数组中的每个元素i,将该数组中的第i个元素变为负数,最后只需要检查新数组中非负数的序号,则输出该序号就是缺失的数据;
    代码
    思路1:
def findDisappearedNumbers1(nums):
    result = []
    for i in range(1, len(nums) + 1):
        if (i not in nums):
            result.append(i)
    return result
nums = [4,3,2,7,8,2,3,1]
print(findDisappearedNumbers1(nums))

输出结果
在这里插入图片描述
思路2:;

def findDisappearedNumbers2(nums):
    # For each number i in nums,
    # we mark the number that i points as negative.
    # Then we filter the list, get all the indexes
    # who points to a positive number
    for i in range(len(nums)):
        index = abs(nums[i]) - 1
        nums[index] = - abs(nums[index])

    return [i + 1 for i in range(len(nums)) if nums[i] > 0]
nums = [4,3,2,7,8,2,3,1]
print(findDisappearedNumbers2(nums))

输出结果;
在这里插入图片描述
由上述两个图片可以看书,两个程序都实现了要求的功能,但是二者在效率上有很大的区别;
思路1 和思路2 对比分析
思路1 的时间复杂度;

def findDisappearedNumbersTest1(nums):
    start = time.time()
    r = findDisappearedNumbers1(nums)
    t = time.time() - start
    return r, len(nums), t
import time
import matplotlib.pyplot as plt
import random
import math
%matplotlib inline  

def random_list(l):
    return [[i + 1 for i in range(l * n)] for n in range(1, 20)]
random_lists = random_list(100)
rst = [findDisappearedNumbersTest1(l) for l in random_lists]
len(rst)
x = list(zip(*rst))[1]
y = list(zip(*rst))[2]
plt.plot(x, y)

输出结果;
在这里插入图片描述
由图可以看出,时间复杂度是O(n^2),思路1 的效率并不高;
思路2的时间复杂度;
程序:


def findDisappearedNumbersTest2(nums):
    start = time.time()
    r = findDisappearedNumbers2(nums)
    t = time.time() - start
    return r, len(nums), t
random_lists = random_list(100)
rst = [findDisappearedNumbersTest2(l) for l in random_lists]
len(rst)
x = list(zip(*rst))[1]
y = list(zip(*rst))[2]
plt.plot(x, y)

输出结果;
在这里插入图片描述
如图所示,思路2的时间复杂度要小很多,为# O(n),思路2的效率比思路1的效率好很多;

持续更新,欢迎大家点赞收藏

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值