万门大学_数据结构与算法_DAY2

动态数组

插入的时间复杂度

动态数组并不是真正意义上的动态的内存,而是一块连续的内存,当添加新的元素时,容量已经等于当前的大小的时候(存不下了),执行下面3步:
1.重新开辟一块大小为当前容量两倍的数组
2.把原数据拷贝过去
3.释放掉旧的数组(垃圾回收机制,Java和python编译器会帮助你完成)

假如划定了一片大小为10的内存区间a,则:

  • 添加第1个元素(a.append(X))事件复杂度为O(1)
  • 添加第2个元素(a.append(X))事件复杂度为O(1)
  • 添加第11个元素(a.append(X))事件复杂度为O(n)

于是,有平均复杂度O(1)。

动态数组的抽象数据类型如下所示:

import ctypes

class DynamicArray:
    
    def __init__ (self):
        'Create an empty array.'
        self._n = 0 #size
        self._capacity = 10
        self._A = self._make_array(self._capacity)
        
    # len(list)
    def __len__ (self):
        return self._n
    
    def is_empty(self):
        return self._n == 0
    
    # O(1)
    def __getitem__ (self, k):
        if not 0 <= k < self._n:
            raise ValueError('invalid index') 
        return self._A[k]
       
    # O(1) 
    def append(self, obj):
        if self._n == self._capacity:
            self._resize(2 * self._capacity)
        self._A[self._n] = obj    
        self._n += 1
        
    def _make_array(self, c):
        return (c * ctypes.py_object)( )
    
    def _resize(self, c):
        B = self._make_array(c)
        for k in range(self._n):
            B[k] = self._A[k]
        self._A = B
        self._capacity = c   

    # O(n)
    def insert(self, k, value):
        if self._n == self._capacity:
            self._resize(2 * self._capacity)
        for j in range(self._n, k, -1):
            self._A[j] = self._A[j-1]
        self._A[k] = value
        self._n += 1
     
    # O(n)    
    def remove(self, value):
        for k in range(self._n):
            if self._A[k] == value:
                for j in range(k, self._n - 1):
                    self._A[j] = self._A[j+1]
                self._A[self._n - 1] = None
                self._n -= 1
                return
        raise ValueError( 'value not found' )
    
    def _print(self):
        for i in range(self._n):
            print(self._A[i], end = ' ')
        print()

问题1

扫雷:
程序接收三个参数,M,N和p,然后生成一个M * N的矩阵,然后每一个cell有p的概率是地雷。生成矩阵后,再计算出每一个cell周围地雷的数量。(以当前点为中心,四周的雷的数量)

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 # 如果随机生成的概率小于给定的p,就标记为-1,表示雷,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()
	#把有雷的,标记为-1,显示为*。其他标记为0,显示为.    

    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 # 当前点不是雷的时候,数字为0,所以可以直接累加。
    
    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)


结果-------------------------------------
. . . . . . * . . * 
* . . . . . . . . . 
. * . . . . * . . . 
. . * . . . . . . . 
. . * . . . . . . . 

1 1 0 0 0 1 * 1 1 * 
 * 2 1 0 0 2 2 2 1 1 
2 * 2 1 0 1 * 1 0 0 
1 3 * 2 0 1 1 1 0 0 
0 2 * 2 0 0 0 0 0 0 

问题2

九宫格问题(横竖斜方向加起来值相同)
在这里插入图片描述
算法如下:

  • 先找到1的位置。行号为最后一行,列号为 n // 2
  • 往当前位置的下方,右方各移动一格,如果这个位置为空,标号。
  • 否则,往当前位置的上方移动一格,标号。
  • 重复2-3步骤,直至完成。
def magic_square(n):
    magic = [[0] * (n) for i in range(n)]
    row = n - 1
    col = n//2
    magic[row][col] = 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 # 这个写得好。+n是个小技巧
        
        magic[row][col] = i
    
    for x in magic:
        print(x, sep=" ")


结果-----------------------------------------
magic_square(4)
[8, 10, 16, 2]
[3, 5, 11, 13]
[14, 4, 6, 12]
[9, 15, 1, 7]

magic_square(5)
[11, 18, 25, 2, 9]
[10, 12, 19, 21, 3]
[4, 6, 13, 20, 22]
[23, 5, 7, 14, 16]
[17, 24, 1, 8, 15]

问题3

给定一个数独,验证是否正确。
在这里插入图片描述

数独是每一行,每一列数字都不相同。然后9个3×3的矩形的数字也都不相同。
下面的代码利用了位操作。如果再加入for循环,那么会增加时间复杂度。

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):
        '''
        1<<(tmp-1)表示将1往左移动tmp距离,例如当tmp=3时,得到了000000100.
        由此,只需要9位,就可以区分1-9。
		然后将每次循环得到的数字与result_row/result_col/result_blk 按位操作:
		一开始为0,假设碰到了1
		000000000
		000000001
		------------------
		000000001
		下次碰到相同的数字,就返回false,如果不同,就把对应位置设置为1.
        '''
            ## 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
            '''
            这里的数学公式解释如下:
            当i=6,j可能的取值为0-9,模拟一下就可以推出9方格的数字。
            '''
            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

问题4

在不开辟额外数组的情况下,完成矩阵的旋转,如下图所示:

在这里插入图片描述

发现规律:数字的交换都在一个圈圈中进行,且4个数字相互追及。
0->4->24->20->0
1->9->23->15->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=" ")
        
matrix = [[i*5+j for j in range(5)] for i in range(5)]
rotate_in_place(matrix)
结果-------------------------------
转换前
[0, 1, 2, 3, 4]
[5, 6, 7, 8, 9]
[10, 11, 12, 13, 14]
[15, 16, 17, 18, 19]
[20, 21, 22, 23, 24]
转化后
[20, 15, 10, 5, 0]
[21, 16, 11, 6, 1]
[22, 17, 12, 7, 2]
[23, 18, 13, 8, 3]
[24, 19, 14, 9, 4]

问题5

给定一个数组,数组里有一个数组有且只有一个最大数,判断这个最大数是否是其他数的两倍或更大。如果存在这个数,则返回其index,否则返回-1。

思路:找到第二大的数字,和他进行比较即可。

# O(n) time
# O(1) space
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
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值