目標
1. 配列と動的配列の違いを理解します。
2. 配列および動的配列の基本的な操作に精通している。
3. 多次元配列を理解し、二次元配列の使い方を使いこなすことができる。
4. 文字列の概念と文字列のさまざまな特性を理解します。
5. ダブルポインタ手法を使用して実際的な問題を解決できるようになります。
配列の概要
数组
による並べ替えに使用される基本的なデータ構造です存储元素的集合
。ただし、配列内の各要素は配列によって識別できるため、要素にランダムにアクセスできます索引
。
配列には 1 つ以上の次元を含めることができます。ここでは一维数组
、線形配列としても知られる から始めます。
動的配列の概要
配列には があるため固定的容量
、初期化時に配列のサイズを指定する必要があります。場合によっては、非常に不便で無駄が生じる可能性があります。
そのため、ほとんどのプログラミング言語は組み込みの を提供しますが动态数组
、これは依然としてランダム アクセス リスト データ構造ですが大小是可变的
、
配列の中間インデックスを見つける
整数型の配列を指定して nums
、配列の「中心インデックス」を返すメソッドを作成します。
配列の中心インデックスを次のように定義します。配列の中心インデックスの左側にあるすべての要素の合計は、右側にあるすべての要素の合計に等しいです。
配列に中心インデックスがない場合は、-1 を返す必要があります。配列に複数の中心インデックスがある場合は、左側に最も近いインデックスを返す必要があります。
例 1:
输入:
nums = [1, 7, 3, 6, 5, 6]
输出: 3
解释:
索引3 (nums[3] = 6) 的左侧数之和(1 + 7 + 3 = 11),与右侧数之和(5 + 6 = 11)相等。
同时, 3 也是第一个符合要求的中心索引。
例 2:
输入:
nums = [1, 2, 3]
输出: -1
解释:
数组中不存在满足此条件的中心索引。
例証します:
nums
長さの範囲は です[0, 10000]
。- どちらも 範囲内の整数
nums[i]
になります 。[-1000, 1000]
class Solution:
def pivotIndex(self, nums: List[int]) -> int:
if len(nums) < 3:
return -1
for i in range(len(nums)):
if sum(nums[:i]) == sum(nums[i+1:]):
return i
return -1
他のデータの2倍以上の最大の数値
指定された配列にはnums
、常に最大の要素が存在します。
配列内の最大の要素が、配列内の他の数値の少なくとも 2 倍であるかどうかを検索します。
「はい」の場合は最大の要素のインデックスを返し、それ以外の場合は -1 を返します。
例 1:
输入: nums = [3, 6, 1, 0]
输出: 1
解释: 6是最大的整数, 对于数组中的其他整数,
6大于数组中其他元素的两倍。6的索引是1, 所以我们返回1.
例 2:
输入: nums = [1, 2, 3, 4]
输出: -1
解释: 4没有超过3的两倍大, 所以我们返回 -1.
ヒント:
nums
長さの範囲は です[1, 50]
。- 各
nums[i]
整数の範囲は です[0, 100]
。
class Solution:
def dominantIndex(self, nums: List[int]) -> int:
nums_max = max(nums)
ind = nums.index(nums_max)
tmp = []
for item in nums:
if nums_max >= 2 * item or item == nums_max:
tmp.append(item)
if len(tmp) == len(nums) :
return ind
else:
return -1
一を足す
空でない整数の配列で表される負でない整数が与えられた場合、その数値に 1 を加えます。
最上位の桁は配列の先頭に格納され、配列内の各要素には1 つの数値のみが格納されます。
この整数は、整数 0 以外のゼロで始まることはないと想定できます。
例 1:
输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。
例 2:
输入: [4,3,2,1]
输出: [4,3,2,2]
解释: 输入数组表示数字 4321。
class Solution:
def plusOne(self, digits: List[int]) -> List[int]:
n=len(digits)
for i in range(n-1,-1,-1):
if digits[i]<9:
digits[i]+=1
return digits
else:
digits[i]=0
return [1]+digits
キャレット位置の検索
ソートされた配列とターゲット値を指定すると、配列内でターゲット値を検索し、そのインデックスを返します。配列内に対象の値が存在しない場合は、順番に挿入される位置を返します。配列内に重複する要素はないと仮定できます。
例 1:
输入: [1,3,5,6], 5
输出: 2
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
for i in range(0, len(nums)):
if nums[i] >= target:
return i
elif i == len(nums) - 1:
return len(nums)
マージ間隔
一連の間隔を指定して、重複するすべての間隔をマージします。
例 1:
输入: [[1,3],[2,6],[8,10],[15,18]]
输出: [[1,6],[8,10],[15,18]]
解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
res = []
intervals.sort()
for i in intervals:
if not res or res[-1][1]<i[0]:
res.append(i)
else:
res[-1][1] = max(res[-1][1],i[1])
return res
2D データの概要
多次元配列は、テーブルや行列などの複雑な構造に適しています。
学習目標:
1. 2 次元配列はどのようにメモリに格納されるのでしょうか?
2. 2 次元配列を使用して問題を解決する方法
回転行列
N × N
各ピクセルのサイズが 4 バイトの行列で表される画像が与えられます 。画像を90度回転するアルゴリズムを設計してください。
余分なメモリ領域を占有せずに実行できますか?
例 1:
给定 matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],
原地旋转输入矩阵,使其变为:
[
[7,4,1],
[8,5,2],
[9,6,3]
]
class Solution:
def rotate(self, matrix: List[List[int]]) -> None:
"""
Do not return anything, modify matrix in-place instead.
"""
n = len(matrix)
# 先上下镜面翻转
for i in range(n // 2):
for j in range(n):
matrix[i][j], matrix[n - 1 - i][j] = matrix[n - 1 - i][j], matrix[i][j]
# 再主对角线翻转
for i in range(n):
for j in range(i):
matrix[j][i], matrix[i][j] = matrix[i][j], matrix[j][i]
ゼロ行列
M × N 行列の要素が 0 である行と列をクリアするアルゴリズムを作成します。
例 1:
入力:
[
[1,1,1]、
[1,0,1]、
[1,1,1]
]
出力:
[
[1,0,1]、
[0,0,0]、
[1,0] ,1]
]
class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
"""
Do not return anything, modify matrix in-place instead.
"""
rownum = len(matrix)
colnum = len(matrix[0])
row = [False for i in range(rownum)]
col = [False for i in range(colnum)]
for i in range(rownum):
for j in range(colnum):
if matrix[i][j] == 0:
row[i] = True
col[j] = True
for i in range(rownum):
for j in range(colnum):
if row[i] or col[j]:
matrix[i][j] = 0
斜めの横断
M x N 要素 (M 行、N 列) を持つ行列が与えられた場合、次の図に示すように、この行列内のすべての要素を対角線の走査の順序で返してください。
例:
入力:
[
[ 1, 2, 3 ]、
[ 4, 5, 6 ]、
[ 7, 8, 9 ]
]
出力: [1,2,4,7,5,3,6,8,9]
説明:
著者: LeetCode
リンク: https://leetcode-cn.com/leetbook/read/array-and-string/cuxq3/
出典: LeetCode
著作権は著者に帰属します。商業的転載の場合は著者に連絡して承認を求め、非商業的転載の場合は出典を明記してください。
class Solution:
def findDiagonalOrder(self, matrix: List[List[int]]) -> List[int]:
m = len(matrix)
if not m:
return []
n = len(matrix[0])
if not n:
return []
matrix_num = m*n
count = 0
x = y = 0
li = []
direction = "up"
while count < matrix_num:
count += 1
li.append(matrix[x][y])
# 右上方向
if direction == "up":
# 无需调换方向的条件(1.x>0 碰到上壁前, 2.y<n-1碰到右壁前)
if x > 0 and y < n-1:
x -= 1
y += 1
continue
else:
direction = "down"
# 碰到上壁 x=0
if x == 0 and y < n-1:
y += 1
# 碰到右壁
elif y == n-1:
x += 1
# 左下方向
else:
# 无需调换方向的条件(1.x<m 碰到下壁前, 2.y>0 碰到右壁前)
if x < m-1 and y > 0:
x += 1
y -= 1
continue
else:
direction = "up"
if x == m-1:
y += 1
elif y == 0 and x < m-1:
x += 1
return li
文字列の紹介
文字列は文字の配列です。
学習目標:
1. 文字列の基本的な操作、特に配列では使用できない固有の操作に精通していること;
2. さまざまな比較関数の違いを理解していること;
3. 文字列が変更可能で接続プロセスで問題を引き起こすかどうかを理解していること;
4. できること並べ替え、部分文字列、文字列の一致など、文字列に関連する基本的な問題を解決します。
最長の共通プレフィックス
文字列の配列内で最も長い共通プレフィックスを検索する関数を作成します。
共通のプレフィックスが存在しない場合は、空の文字列 "" を返します。
例 1:
入力: ["flower","flow","flight"]
出力: "fl"
例 2:
入力: ["dog","racecar","car"]
出力: ""
説明: 入力に共通のプレフィックスはありません。
例証します:
すべての入力には、小文字 a z のみが含まれます。
著者: LeetCode
リンク: https://leetcode-cn.com/leetbook/read/array-and-string/ceda1/
出典: LeetCode
著作権は著者に帰属します。商業的転載の場合は著者に連絡して承認を求め、非商業的転載の場合は出典を明記してください。
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
import os
return os.path.commonprefix(strs)
最長の回文部分文字列
文字列 s が与えられた場合、s 内の最長の回文部分文字列を見つけます。s の最大長は 1000 であると想定できます。
例 1:
入力: "babad"
出力: "bab"
注: "aba" も有効な答えです。
例 2:
入力: "cbbd"
出力: "bb"
著者: LeetCode
リンク: https://leetcode-cn.com/leetbook/read/array-and-string/conm7/
出典: LeetCode
著作権は著者に帰属します。商業的転載の場合は著者に連絡して承認を求め、非商業的転載の場合は出典を明記してください。
class Solution:
def longestPalindrome(self, s: str) -> str:
max_length = 0
start = 0
for i in range(len(s)):
if i - max_length >= 1 and s[i-max_length-1: i+1] == s[i-max_length-1: i+1][::-1]:
start = i - max_length - 1
max_length +=2
continue
if i - max_length >= 0 and s[i-max_length: i+1] == s[i-max_length: i+1][::-1]:
start = i - max_length
max_length += 1
return s[start: start+max_length]
文字列内の単語を反転する
文字列を指定して、文字列内の各単語を 1 つずつ反転します。
例 1:
入力: "the sky is blue
" 出力: "blue is sky the
"
class Solution:
def reverseWords(self, s: str) -> str:
li = s.split(" ")
str = ""
for i in range(len(li)-1, -1, -1):
if li[i]:
str = str + li[i] + " "
# 因为最后一个单词多加了一个空格符“ ”
return str[:-1]
文字列マッチングアルゴリズム: KMP
Knuth-Morris-Pratt (KMP) アルゴリズムは、改良された文字列マッチング アルゴリズムです。その核心は、マッチングが失敗した後の情報を使用して、パターン文字列とメイン文字列の間の一致数を最小限に抑え、高速マッチングの目的を達成することです。その時間計算量は O(m+n)O(m+n)O(m+n) です。
int match (char* P, char* S){ // KMP 算法
int* next = buildNext(P); // 构造 next 表
int m = (int) strlen (S), i = 0; // 文本串指针
int n = (int) strlen(P), j = 0; //模式串指针
while (j < n && i < m) // 自左向右逐个比对字符
if (0 > j || S[i] == P[j]) // 若匹配,或 P 已移除最左侧
{i++; j++} // 则转到下一字符
else
j = next[j]; // 模式串右移(注意:文本串不用回退)
delete [] next; // 释放 next 表
return i - j;
}
strStr() を実装する
strStr() 関数を実装します。
干し草の山の文字列と針の文字列が与えられた場合、干し草の山の文字列内の針の文字列の最初の位置 (0 から始まる) を見つけます。存在しない場合は -1 を返します。
例 1:
入力: haystack = "hello"、needle = "ll"
出力: 2
例 2:
入力: haystack = "aaaaa"、needle = "bba"
出力: -1
説明:
針が空の文字列の場合、何を返すべきでしょうか? これは面接で尋ねるべき素晴らしい質問です。
この質問では、needle が空の文字列の場合は 0 を返す必要があります。これは、C の strstr() および Java のindexOf() の定義と一致します。
著者: LeetCode
リンク: https://leetcode-cn.com/leetbook/read/array-and-string/cm5e2/
出典: LeetCode
著作権は著者に帰属します。商業的転載の場合は著者に連絡して承認を求め、非商業的転載の場合は出典を明記してください。
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
for i in range(0, len(haystack) - len(needle) + 1):
if haystack[i: i + len(needle)] == needle:
return i
return -1
ツーポインターテクニック
ツー ポインター テクニック シナリオ 1
配列内の要素を反転します。たとえば、配列は ['l', 'e', 'e', 't', 'c', 'o', 'd', 'e'] となり、反転後は ['e] になります。 '、'd'、'o'、'c'、't'、'e'、'e'、'l']。
def reverseString(self, s):
i, j = 0, len(s) - 1
while i < j:
s[i], s[j] = s[j], s[i]
i += 1
j -= 1
逆文字列
入力文字列を反転する関数を作成します。入力文字列は、文字の char[] 配列として指定されます。
別の配列に余分なスペースを割り当てないでください。この問題を解決するには、O(1) の余分なスペースを使用して、入力配列をインプレースで変更する必要があります。
配列内のすべての文字が ASCII コード テーブルの印刷可能な文字であると想定できます。
例 1:
入力: ["h","e","l","l","o"]
出力: ["o","l","l","e","h"]
例 2:
输入:["H","a","n","n","a","h"]
输出:["h","a","n","n","a", 「は」]
著者: LeetCode
リンク: https://leetcode-cn.com/leetbook/read/array-and-string/cacxi/
出典: LeetCode
著作権は著者に帰属します。商業的転載の場合は著者に連絡して承認を求め、非商業的転載の場合は出典を明記してください。
class Solution:
def reverseString(self, s: List[str]) -> None:
"""
Do not return anything, modify s in-place instead.
"""
j=-1
for i in range(0,len(s)//2):
t=s[i]
s[i]=s[j]
s[j]=t
j-=1
ダブル ポインター テクニック シナリオ 2
この問題を解決するには、高速ポインタと低速ポインタの 2 つの非同期ポインタを使用します。
例
古典的な問題から始めましょう。
配列
nums
と value が 与えられた場合、 その value に等しい要素をすべて削除し 、 削除された配列の新しい長さを返すval
必要があります 。val
def removeElement(self, nums: List[int], val: int) -> int:
slow = 0
n = len(nums)
for fast in range(n):
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
return slow
要素を削除する
配列 nums と値 val が与えられた場合、値が val に等しいすべての要素をその場で削除し、削除された配列の新しい長さを返す必要があります。
余分な配列スペースを使用しないでください。O(1) の追加スペースのみを使用し、入力配列をインプレースで変更する必要があります。
要素の順序は変更できます。新しい長さを超える配列内の要素を考慮する必要はありません。
例 1:
nums = [3,2,2,3]、val = 3 とすると、
関数は新しい長さ 2 を返す必要があり、nums の最初の 2 つの要素は両方とも 2 です。
新しい長さを超える配列内の要素を考慮する必要はありません。
例証します:
戻り値が整数であるのに、出力される答えは配列であるのはなぜですか?
入力配列は「参照によって」渡されることに注意してください。これは、関数内の入力配列への変更が呼び出し側に見えることを意味します。
内部動作は次のように想像できます。
// nums は「参照」によって渡されます。つまり、実際のパラメータのコピーを作成しないでください
。 int len=removeElement(nums, val);
// 関数内の入力配列への変更は呼び出し元に表示されます。
// 関数によって返される長さに応じて、その長さ内の配列内のすべての要素が出力されます。
for (int i = 0; i < len; i++) { print(nums[i]); }
著者: LeetCode
リンク: https://leetcode-cn.com/leetbook/read/array-and-string/cwuyj/
出典: LeetCode
著作権は著者に帰属します。商業的転載の場合は著者に連絡して承認を求め、非商業的転載の場合は出典を明記してください。
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
while val in nums:
nums.remove(val)
return len(nums)
まとめ
アレイ関連技術
1、ここに他の配列のようなデータ構造がいくつかありますが、いくつかの異なるプロパティがあります。
文字列
ハッシュ テーブル
のリンク リスト
キュー
スタック
2 では、前述したように、組み込み関数を呼び出して配列を並べ替えることができます。ただし、広く使用されているいくつかの並べ替えアルゴリズムの原理とその複雑さを理解することは役に立ちます。
3. 二分探索も、ソートされた配列内の特定の要素を検索するための重要な手法です。
4. この章ではダブル ポインター手法を紹介しました。このテクニックを柔軟に使用するのは簡単ではありません。このトリックは次の解決にも使用できます。
リンク リストにおけるスロー ポインタとファスト ポインタの問題 スライディング
ウィンドウの問題
5、ダブル ポインタ手法は、貪欲アルゴリズムに関連する場合があり、ポインタの移動戦略の設計に役立ちます。