本人是一个长期的数据分析爱好者,最近半年的时间的在网上学习了很多关于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,在修改被指定的元素的值之后,其他所有指向该地址元素的值均会被改变;
## 数组应用:
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倍;
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))
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的效率好很多;