20240309
一. 数组和字符串
1.1 数组介绍
1.1.1 集合、列表和数组
- 集合
- 由一个或多个确定的元素所构成的整体。
- 特点:
- 集合里的元素类型不一定相同
- 无序
- 列表
- 具有相同特性的数据元素构成的有限序列
- 特点:
- 元素个数有限,相对位置线性,且长度是可变的(购物清单)
- 元素无索引,在内存中连续存储(顺序表);非连续存储(链表)
- 数组
- 列表的实现方式之一
- 特点:
- 有索引来标识每项数据在数组中的位置,大多数从0开始;
- 数组中每个元素在内存中连续存储,且占用相同大小的内存
1.1.2 数组的操作
- 读取元素
- 人:访问索引 A A A
- 计算机:已知数组索引0的内存地址,将内存地址+ A A A,找到对应内存地址的元素,输出目标元素
- 时间复杂度:只访问了一次内存地址,时间复杂度为 O ( 1 ) O(1) O(1)
- 查找元素:
- 人:输入要查找的元素’ B B B’
- 计算机:找到索引0的内存地址,内存地址依次+1,逐步查找元素是否是目标元素’ B B B’,是则停止查找,否则一直搜索直到数组末尾。
- 时间复杂度:最长需要搜索到数组末尾,假设数组长度为 N N N,访问 N N N次内存地址,时间复杂度 O ( N ) O(N) O(N)
- 插入元素
两种情况:插入到数组末端或者其他(如索引 C C C( C ∈ [ 0 , N − 1 ) C\in [0,N-1) C∈[0,N−1)))
- 末端:计算机已知数组索引0的内存地址,将内存地址+ N N N,将待插入元素插入到计算得到的的内存地址中
- 其他:计算机已知数组索引0的内存地址,将内存地址+ N N N,将末尾元素到计算得到的的内存地址中,依次将索引 N − 1 → C N-1 \to C N−1→C的元素往后移,直到索引 C C C出内存地址对应元素空出,插入目标插入元素
- 时间复杂度:最坏的情况,要移动 N N N次,所以时间复杂度 O ( n ) O(n) O(n)
- 删除元素
- 人:删除索引 D D D的元素
- 计算机:找到索引 D D D元素内存地址,删除,将后面的元素依次对该位置进行填补
- 时间复杂度:最坏的情况,删除索引0对应元素,需要步骤1+(n-1)步,时间复杂度 O ( n ) O(n) O(n)
1.1.3 解题测试
- 找数组中心索引
- 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。
如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1 - 解题思路:遍历索引计算left总和,如果left总和*2+数组[i]==sum(数组),返回当前i,否则left总和+=数组[i]
- 二分法搜索
- 思想:每次将搜索范围缩小一半
- 基本条件:数据列表是有序的
class BinarySearch(object):
def binary_search(self, array, target):
"""二分法递归查找"""
if len(array)==0
return -1
array.sort()
mid_index = len(array) // 2
if array[mid_index] == target
return mid_index
return self.binary_search(array[mid_index+1 :], target) if target > array[mid_index] else self.binary_search(array[:mid_index], target)
- 合并区间
- 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
class Solution(object):
def merge(self, intervals):
"""
:type intervals: List[List[int]]
:rtype: List[List[int]]
"""
if not intervals:
return []
intervals.sort(key=lambda x: x[0]) #将intervals按照每个区间的起始位置进行排序,确保区间按照起始位置的顺序排列。
merge_list = list() # 创建一个空列表 merges,用于存储合并后的区间。
for intervel in intervals:
if not merge_list or merge_list[-1][-1]<intervel[0]:
merge_list.append(intervel)
else:
merge_list[-1][-1]=max(merge_list[-1][-1],intervel[-1])
return merge_list
1.2 二维数组介绍
1.2.1 二维数组
- 将数组中的每个元素变成了一维数组。
- 计算机已知第一行数组第一个元素的索引位置,即数组[0][0],内存地址顺序为:[0][0]、[0][1]、…、[1][0]、[1][1]、…、[2][0]、…、[N][N]依次存储。
- 注意:数组中元素由于类型不同会占用不同的字节数,因此内存递增不一定为1.
1.2.2 解题测试
- 旋转矩阵
- 给一幅由 N × N 矩阵表示的图像,其中每个像素的大小为 4 字节。请设计一种算法,将图像旋转 90 度。(不占用额外内存空)
class Solution(object):
def rotate(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: None Do not return anything, modify matrix in-place instead.
"""
# 先按主对角线翻转矩阵
for i in range(len(matrix)):
for j in range(0,i+1):
matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
# 再左右翻转矩阵
for i in range(len(matrix)):
for j in range(len(matrix)//2):
matrix[i][j], matrix[i][len(matrix)-1-j] = matrix[i][len(matrix)-1-j], matrix[i][j]
return matrix
- 零矩阵
- 编写一种算法,若M × N矩阵中某个元素为0,则将其所在的行与列清零。
class Solution(object):
def setZeroes(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: None Do not return anything, modify matrix in-place instead.
"""
m = len(matrix)
n = len(matrix[0])
row = [0] * len(matrix)
column = [0] * len(matrix[0])
# 统计、记录行列为0的元素
for i in range(m):
for j in range(n):
if matrix[i][j] == 0:
row[i] = 1
column[j] = 1
for i in range(m):
for j in range(n):
if row[i] == 1 or column[j] == 1:
matrix[i][j] = 0
- 对角线遍历
class Solution(object):
def findDiagonalOrder(self, mat):
"""
:type mat: List[List[int]]
:rtype: List[int]
"""
m = len(mat)
n = len(mat[0])
result_list = list()
for i in range(0, m+n-1):
# 如果遍历数是偶数,向上遍历,行列元素值相加为i
if i % 2 == 0:
l = min(m-1, i)
c = i-l
while l>=0 and c<n: # 添加边界检查
result_list.append(mat[l][c])
l -=1
c +=1
else:
# 如果遍历数是奇数,向下遍历,行列元素值相加为i
c = min(n-1, i)
l = i-c
while c>=0 and l<m: # 添加边界检查
result_list.append(mat[l][c])
l +=1
c -=1
return result_list
1.3 字符串介绍
1.3.1 字符串简介
string = 'Hello, world!' # 字符串用'' 或者""括起来,可包含任何字符(字母、数字、标点符号和特殊字符)
# 可使用索引或者切片来访问单个字符或者子串
print(string[0]) # 输出'H'
print(string[7:]) # 输出'world!'
str_a = string.strip().split() # 去掉空前后缀
print(str_a) #输出 ['Hello,', 'world!']
str_a.reverse() # 反转字符串
print(' '.join(str_a)) # 输出 world! Hello,
1.3.2 解题测试
- 最长公共前缀
- 编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 “”
from typing import List
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
if not strs: return ""
s1 = min(strs) # 字符串中表示 最小字典序 ASCII值
s2 = max(strs)
# enumerate()返回一个元组(index,value)-->索引,元素
for i,x in enumerate(s1):
if x != s2[i]:
return s2[:i]
return s1
strs = ["flower","flow","flight"]
s1 = min(strs) # 'flight'
s2 = max(strs) # 'flower'
result = Solution()
result_end = result.longestCommonPrefix(strs)
- 最长回文子串
class Solution:
def longestPalindrome(self, s: str) -> str:
ret = ''
for i in range(len(s)):
# 考虑回文串长度为奇数的情况,即以s[i]为中心对齐
tmp = self.helper(s,i,i)
if len(tmp)>len(ret):
ret = tmp
# 考虑回文串长度为偶数的情况,即以s[i]和s[i+1]之间的间隙为中心对齐
tmp = self.helper(s,i,i+1)
if len(tmp)>len(ret):
ret = tmp
return ret
def helper(self, s, l, r):
while l>=0 and r<len(s) and s[l]==s[r]:
l-=1; r+=1
return s[l+1:r]
result = Solution() #定义对象
result_end = result.longestPalindrome("cbbd") #调用函数,传入字符串参数
result_end
- KMP算法(模式匹配)
问题:主串 S 和模式串T ,在主串 S中找出模式串T的第一个匹配项的下标(下标从 0 开始)。如果模式串T不是主串 S 的一部分,则返回 -1 。
- 朴素解法:将S中元素与模式串T逐个匹配,若不匹配,则匹配起始点右移,逐个匹配,直到匹配成功。最坏情况需执行匹配 len(S)-len(T)+1轮,每一轮匹配执行匹配操作 len(T)次
class Solution:
def strStr(self, S: str, T: str) -> int:
i = 0
j = 0
while i < len(S) and j < len(T):
if S[i] == T[j]: # 依次比较,相等则比较下一个字符
i += 1; j += 1
else: # 如果不相等,指针i需要回溯到上个起点的下一个位置, 并从头开始比较
i = i - j + 1; j = 0
# while循环结束后,要么是找到合适匹配了,要么是遍历完主串都没有找到合适匹配
if j == len(T):
return i - j
else:
return -1
- KMP解法
- 定义next()函数,其中包含模式串T的局部信息,利用匹配失败后的信息,进行下次匹配。
- 将S中元素与模式串T逐个匹配,如果匹配失败时在主串S[i]和T[j]上,那么匹配起始点尽可能右移多个元素(j-k个),S[i]和T[k]继续匹配。
- next()函数求出k值
next(j) 满足: 当 j=0时,next(0)=-1
其他情况,next(j)= 前后缀(不包括本身)的最长公共子缀的长度T[:j]前后缀(不包括本身)的最长公共子缀的长度
class Solution:
def strStr(self, S: str, T: str) -> int:
i = 0
j = 0
prefix = self.net_generate(T)
while i<len(S) and j<len(T):
if j ==-1 or S[i]==T[j]:
i+=1; j+=1
else:
j =prefix[j]
if j==len(T):
result = i-j
else:
result = -1
return result
def net_generate(self, s):
j = 0
t = -1
next = [-1] *len(s)
while j < len(s)-1:
if t == -1 or s[j]==s[t]:
j +=1; t+=1
next[j] = t
else:
t = next[t]
return next
-