leetcode-explore-learn-数据结构-数据结构-数组和字符串
- 1. 一维数组
- 1.0 概况
- 1.1 寻找数组的中心索引
- 1.2 搜索插入位置
- 1.3 合并区间
- 1.4 至少是其他数字两倍大的最大数
- 1.5 加一
- 2. 二维数组
- 2.1旋转矩阵
本系列博文为leetcode-explore-learn子栏目学习笔记,如有不详之处,请参考leetcode官网:https://leetcode-cn.com/explore/learn/card/array-and-string/198/introduction-to-array/768/
1. 一维数组
1.0 概况
几个常见易混淆的概念:
集合: 一个/多个确定的元素构成的整体。一般种类相同,当然也可以不同。集合中的元素没有顺序。很多编程语言中拥有一些数据结构,是在集合的概念中增加了一些规则而来的。无序。
列表:(又称线性列表)一种数据项构成的有限的序列。即按一定顺序排列的数据项的集合。有序,长度可变。列表常见的表现形式有:数组,链表,堆栈,队列。
数组: 是一种最基本的数据结构,用于按顺序存储数据。数组中元素通过索引来实现读写操作。数组有一维数组,也有多维数组。一般编程语言中,数组具有固定容量,需要在初始化的时候确定数组大小,后续无法修改其长度。操作不是十分便利,于是大多编程语言提供了动态数组的数据结构,其大小可变。在Python中就没这么复杂,一个list数据结构就可以灵活满足众多需求。
数组基本操作:增、删、查、改
读:通过访问索引的方式来读取的,索引一般从 0 开始。访问的时间复杂度为o(1)。
1.1 寻找数组的中心索引
给定一个整数类型的数组,编程实现返回数组的中心索引。
中心索引的定义:中心索引左侧所有元素的和大于右侧所有元素的和。
特殊情况:如果中心索引不存在,返回-1;
如果存在多个中心所以,返回最左边的一个
思路:定义两个数组:
left_sum[i]=∑j=0i−1nums[j],i=1,2,3,...,n−1left\_sum[i]=\sum_{j=0}^{i-1}nums[j],\ \ i=1,2,3,...,n-1left_sum[i]=j=0∑i−1nums[j], i=1,2,3,...,n−1
right_sum[i]=∑j=i+1n−1nums[j],i=n−2,n−3,...,0right\_sum[i]=\sum_{j=i+1}^{n-1}nums[j], \ \ i=n-2,n-3,...,0 right_sum[i]=j=i+1∑n−1nums[j], i=n−2,n−3,...,0
逐个比较left_sum与right_sum的元素,如果相同则返回对应索引,如果遍历完也没有相同的,则返回-1
例子:nums = [1, 7, 3, 6, 5, 6]
left_sum=[0,1,8,11,17,22]
right_sum=[27,20,17,11,6,0]
class Solution(object):def pivotIndex(self, nums):""":type nums: List[int]:rtype: int"""res = -1n = len(nums)left_sum = [0] * nright_sum = [0] * nfor i in range(1, n):left_sum[i] = left_sum[i-1] + nums[i-1]for i in range(n-2, -1, -1):right_sum[i] = right_sum[i+1] + nums[i+1]for i in range(n):if left_sum[i] == right_sum[i]:res = ibreakreturn res
1.2 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。
二分查找能够实现 O(log n) 的时间复杂度。
class Solution(object):def searchInsert(self, nums, target):left_idx, right_idx = 0, len(nums)-1while(left_idx <= right_idx):# print(left_idx, right_idx)mid = int(left_idx + right_idx) / 2if nums[mid] == target:return midelif nums[mid] < target:left_idx = mid + 1elif nums[mid] > target:right_idx = mid - 1return left_idx
1.3 合并区间
以数组 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):intervals.sort(key = lambda x:x[0])res = []for interval in intervals:if res == [] or res[-1][1] < interval[0]:res.append(interval)else:res[-1][1] = max(res[-1][1], interval[1])return res
1.4 至少是其他数字两倍大的最大数
在一个给定的数组nums中,总是存在一个最大元素 。
查找数组中的最大元素是否至少是数组中每个其他数字的两倍。
如果是,则返回最大元素的索引,否则返回-1。
思路:
一次搜索,找最大值
二次搜索,判断是否符合两倍条件
class Solution(object):def dominantIndex(self, nums):""":type nums: List[int]:rtype: int"""n=len(nums)max_num=float("-INF")max_ind=-1for i in range(n):if nums[i]>max_num:max_num=nums[i]max_ind=ifor i in range(n):if i ==max_ind:continue# 判断是否是nums[i]的两倍if max_num<nums[i]*2:return -1return max_ind
1.5 加一
给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
核心难点:有进位的情况该如何处理,因为是加一操作,大大简化了进位难度,因为只有可能产生的进位是1
思路1在这里插入代码片
:直接在数组上操作,判断数组每位置数字是否产生进位:
class Solution(object):def plusOne(self, digits):""":type digits: List[int]:rtype: List[int]"""n=len(digits)digits[n-1]+=1# 判断进位,如何存储进位信息。flag=0for i in range(n-1,-1,-1):if flag==1:digits[i]+=1flag=0if digits[i]==10:digits[i]=0flag=1if flag!=0:res=[flag]for i in range(n):res.append(digits[i])return resreturn digits
思路2:数组转数字,数字+1处理,再转数组。
def plusOne(self, digits):""":type digits: List[int]:rtype: List[int]"""# 整数转数字,加一之后转会数组n=len(digits)nums=0e=0for i in range(n-1,-1,-1):nums+=digits[i]*10**ee+=1nums+=1res=[]while(nums>0):digit=nums%10res.append(digit)nums=(nums-digit)/10res.reverse()return res
2. 二维数组
二维数组是一种结构较为特殊的数组,只是将数组中的每个元素变成了一维数组。所以二维数组的本质上仍然是一个一维数组,内部的一维数组仍然从索引 0 开始,可以将二维数组看作一个矩阵,并处理矩阵的相关问题。
在内存连续存储,
2.1旋转矩阵
将 N × N 矩阵顺时针旋转90度。
123456789(1)\begin{matrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{matrix} \tag{1} 147258369(1)
解题思路:
1.旋转90度后,(i, j)位置上的数字会换到(j, N-1-i)
2.每4个数字可以形成个循环圈,4 * 90 = 360:1会到3的位置,3会到9的位置,9会到7的位置,7会到1的位置。即这四个相互置换数字坐标满足:
{M[j,N−1−i]←M[i,j]M[N−1−i,N−1−j]←M[j,N−1−i]M[N−1−j,i]←M[N−1−i,N−i−j]M[i,j]←M[N−1−j,i]\left\{ \begin{aligned} M[j, N-1-i] & \leftarrow M[i, j]\\ M[N-1-i, N-1-j] & \leftarrow M[j, N-1-i] & \\ M[N-1-j, i] & \leftarrow M[N-1-i, N-i-j] \\ M[i, j] & \leftarrow M[N-1-j, i] \\ \end{aligned} \right. ⎩⎨⎧M[j,N−1−i]M[N−1−i,N−1−j]M[N−1−j,i]M[i,j]←M[i,j]←M[j,N−1−i]←M[N−1−i,N−i−j]←M[N−1−j,i]
3.仅需要旋转关键位置的数字即可:当 N为偶数时,我们需要枚举n2/4=(n/2)×(n/2)n^2/4=(n/2)×(n/2)n2/4=(n/2)×(n/2) 个位置,可以将该图形分为四块,当 n为奇数时,由于中心的位置经过旋转后位置不变,我们需要枚举 (n2−1)/4=((n−1)/2)×((n+1)/2)(n^2−1)/4=((n−1)/2)×((n+1)/2)(n2−1)/4=((n−1)/2)×((n+1)/2)个位置.
class Solution(object):def rotate(self, matrix):N = len(matrix)for i in range(N//2):for j in range((N+1)//2):matrix[i][j], matrix[j][N-1-i], matrix[N-1-i][N-1-j], matrix[N-1-j][i]\= matrix[N-1-j][i], matrix[i][j], matrix[j][N-1-i], matrix[N-1-i][N-1-j]return matrix```