記事ディレクトリ
剣はオファー04を指します。二次元配列で検索
のn * m
2D 配列:
- 各行は左から右へ非降順でソートされます。
- 各列は上から下へ非降順でソートされます。
効率的な関数を完成させて、このような2 次元配列と整数を入力し、配列に整数が含まれているかどうかを判断してください。
例:
- 既存の行列行列は次のとおりです。
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
与えられた場合target = 5
、 を返しますtrue
。
与えられた場合target = 20
、返すfalse
制限:
0 <= n <= 1000
0 <= m <= 1000
コード
class Solution:
def findNumberIn2DArray(self, matrix: List[List[int]], target: int) -> bool:
row=len(matrix)-1
col=0
while( row>=0 and col<len(matrix[0]) ):
if(matrix[row][col]==target):
return True
else:
if(matrix[row][col]>target):
row=row-1
else:
col=col+1
return False
問題解決計画+アイデア
- タグ: 配列トラバース
- 行列の左下から上にある数字はそれより小さく、右にある数字はそれより大きいので、この規則に従って数字が存在するかどうかを判断します
- 現在の数値を とし
cur
、目標の数値を とします。target
target < cur
のとき、cur はその上の数値に更新されますtarget > cur
のとき、cur はその右側の数字に更新されます
- 等しくなるまで戻り、等しくない場合は行列の境界
true
に戻りますfalse
- 現在の数値を とし
- 時間計算量:
O(m+n)
アルゴリズムのステップ
その他の実装アイデア - 二分探索
- 順序を見ると、最初の反応は二分探索です。最も直接的な方法は、行ごとに二分探索を実行することです。
また、秩序ある性質と相まって、状況によっては早期に終了する可能性があります。
- たとえば、行の最初の要素がターゲットより大きい場合、現在の行と後続のすべての行は無視され、直接 false が返されます。
- 行の最後の要素がターゲットより小さい場合、現在の行は考慮されず、次の行が置き換えられます。
この質問では、「各行の最初の整数が前の行の最後の整数より大きい」ことが保証されないため、「2 回の二分法」アプローチを取ることはできません。
- 唯一の解決策は、行/列を走査し、列/行を 2 つに分割することです。
時間計算量の観点から言えば、m 行 n 列であれば、そうですO(mlog(n))
。
import numpy as np
class Solution(object):
def searchMatrix(self, matrix, target):
"""
:type matrix: List[List[int]]
:type target: int
:rtype: bool
"""
matrix = np.array(matrix)
row = matrix.shape[0]
col = matrix.shape[1]
flag =0
for row in matrix:
left = 0
right = col-1
while(left<=right):
mid = (left+right+1)/2
if row[mid]<target:
left=mid+1
elif row[mid]>target:
right=mid-1
else:
return True
return False
その他の実装アイデア_変形した二分法
二分法の考え方は、ターゲット値と中間値を比較し、要素の半分を破棄できるというものです。
- この質問はマトリックスです
- 行列の中心が見つかったら、ターゲット値と比較して、一部の要素を破棄できるかどうかを確認します。
如下图,中心位置是 9
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, /9/,16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
通过中心位置, 我们可以把原矩形分成四个矩形, 左上, 右上, 左下, 右下
[1, 4, 7 [11, 15
2, 5, 8 12, 19
3, 6, /9/] 16, 22]
[10, 13, 14 [17, 24
[18, 21, 23] 26, 30]
如果 target = 10,
此时中心值小于目标值,左上角矩形中所有的数都小于目标值,我们可以丢弃左上角的矩形,继续从剩下三个矩形中寻找
如果 target = 5,
此时中心值大于目标值,右下角矩形中所有的数都大于目标值,那么我们可以丢弃右下角的矩形,继续从剩下三个矩形中寻找
要素を破棄する原理を見つけたので、コードを書くことができます。
ここで、長方形は左上隅と右下隅の座標で表され、分割された長方形の座標は次の図のようになります。
再帰的に書くこともできますが、
- 再帰的終了の場合、行列の要素が 1 つだけの場合は、現在の要素がターゲット値であるかどうかを直接判断すれば十分です。
分割する際に境界線を越える可能性もあります。
- たとえば、元の行列には 1 行しかなく、左下隅と右下隅の行列は実際には存在しません。
- 上記の座標式に従って計算した後、範囲外かどうかを判断する必要があります。
Sword Pointer Offer 05. スペースを置き換える
トピックの説明
s
文字列内の各スペースを「%20」に置き換える関数を実装してください。
例 1:
- 入力: s = 「私たちは幸せです。」
- 出力: 「私たちは %20 %20 幸せです。」
制限:
0 <= s 的长度
<= 10000
コード
Pythonの簡単な方法:
class Solution:
def replaceSpace(self, s: str) -> str:
return "%20".join(s.split(' '))
Python メソッド:
class Solution {
public:
string replaceSpace(string s) {
for(int i = 0; i < s.length(); i++){
if(s.find(" ") == i){
s.erase(i, 1);
s.insert(i, "%20");
}
}
return s;
}
};
class Solution(object):
def replaceSpace(self, s):
"""
:type s: str
:rtype: str
"""
s1=""
for c in s:
if c == ' ':
s1 += '%20'
else:
s1 +=c
return s1
C++:
class Solution {
public:
string replaceSpace(string s) {
for(int i = 0; i < s.length(); i++){
if(s.find(" ") == i){
# 查找到空格所在的位置
s.erase(i, 1); # 先清除空格所占的一个字符
s.insert(i, "%20"); # 在该位置插入%20
}
}
return s;
}
};
問題解決計画+アイデア
- ラベル:文字列
- 最も簡単な解決策は、当然のことながら、ライブラリ関数を直接使用することです。もちろん、タイトルは決してこれを望んでいるわけではありません。
- 新しい文字列を追加し、走査プロセス中に 元の文字列を走査します。
- スペースでない場合は、元の文字が新しい文字列に直接結合されます。
- スペースが見つかった場合は、
%20
新しい文字列に結合されます
- 時間計算量:
O(n)
、空間計算量:O(n)
アルゴリズムのステップ
方法の概要
方法 1: トラバースと追加
Python や Java などの言語では、文字列は「不変」型として設計されています。つまり、文字列の特定の文字を直接変更することはできず、それを実装するには新しい文字列を作成する必要があります。 。
アルゴリズムの流れ:
- ;として示されるリスト (Python) / StringBuilder (Java) を初期化します
res
。 s
リスト内の各文字を反復処理しますc
。c
がスペースの場合:res
後ろに文字列を追加します"%20"
。c
がスペースでない場合: ;res
の後に文字を追加します。c
- リストを
res
文字列に変換して返します。
複雑さの分析:
- 時間計算量 O(N): O(N) はトラバーサルに使用され、O(1) は文字の追加 (変更) 操作の各ラウンドに使用されます。
- スペースの複雑さ O(N): Python の新しいリストと Java の新しい StringBuilder はどちらも線形サイズの追加スペースを使用します。
方法 2: インプレース変更
C++ 言語では、文字列は「変数」型 (参照) として設計されているため、新しい文字列を作成することなくインプレース変更が実現できます。
- スペースを「%20」に置き換える必要があるため、文字列の総文字数が増加し、元の文字列 s の長さを長くする必要があります。
- 計算式は次のとおりです。新しい文字列の長さ = 元の文字列の長さ + 2 * スペースの数、
- 以下の図に例を示します。
アルゴリズムの流れ:
- 初期化: スペースの数
count
、文字列 s の長さlen
。 - 空白の数を数えます。空白が見つかった場合は s を走査します
count++
。 - s の長さを変更します。「%20」を追加した後の文字列の長さは
len + 2 * count
;になります。 - 逆順トラバーサル変更:
i
元の文字列の末尾要素を指し、j
新しい文字列の末尾要素を指します。- i = j の場合に飛び出します (左側にスペースがないため、トラバースを続ける必要がないことを意味します)。
- s[i] がスペースでない場合: 実行します
s[j] = s[i]
。 - s[i]がスペースの場合:
[j-2, j]
文字列の閉区間の要素を に変更します"%20"
; 3つの要素が変更されているので、 が必要ですj -= 2
。
- 戻り値: 変更された文字列
s
。
複雑さの分析:
- 時間計算量 O(N): トラバーサル統計とトラバーサル変更は両方とも O(N) 時間を使用します。
- スペース複雑さ O(1): s の長さがその場で拡張されるため、O(1) 個の余分なスペースが使用されます。
class Solution {
public:
string replaceSpace(string s) {
int count = 0, len = s.size();
// 统计空格数量
for (char c : s) {
if (c == ' ') count++;
}
// 修改 s 长度
s.resize(len + 2 * count);
// 倒序遍历修改
for(int i = len - 1, j = s.size() - 1; i < j; i--, j--) {
if (s[i] != ' ')
s[j] = s[i];
else {
s[j - 2] = '%';
s[j - 1] = '2';
s[j] = '0';
j -= 2;
}
}
return s;
}
};
剣はオファー 11 を指します。回転配列の最小数 - 解決策
トピックの説明
配列の最初のいくつかの要素を配列の末尾に移動することを、配列の回転と呼びます。
- 昇順にソートされた配列の回転を入力し、回転された配列の最小要素を出力します。
- たとえば、配列 [3,4,5,1,2] は、最小値 1 を持つ [1,2,3,4,5] の回転です。
[a[0], a[1], a[2], ..., a[n-1]]
配列を 1 回回転した結果が配列になることに注意してください[a[n-1], a[0], a[1], a[2], ..., a[n-2]]
。
例 1:
- 入力: [3,4,5,1,2]
- 出力: 1
例 2:
- 入力: [2,2,2,0,1]
- 出力: 0
ヒント:
- n ==
numbers.length
- 1 <=
n
<= 5000 - -5000 <=
numbers[i]
<= 5000 - 数値は 1 ~ n 回転の昇順にソートされた配列であることが判明します
コード
- Pythonトラバーサル
class Solution:
def minArray(self, numbers: List[int]) -> int:
j=numbers[0]
for i in range(len(numbers)):
if j<=numbers[i]:
j=j
else:
j=numbers[i]
return j
- 二分法
class Solution:
def findMin(self, nums: List[int]) -> int:
left, right = 0, len(nums) - 1
while left < right:
mid = (left + right) // 2
if nums[mid] > nums[right]: left = mid + 1
elif nums[mid] < nums[right]: right = mid
else: right = right - 1 # key
return nums[left]
問題解決計画+アイデア
- タグ:二分探索
- 全体のアイデア:
- まず、配列は順序付けられた配列を回転させたものであり、この条件から配列が通常のサイズであることがわかります。
- 二分検索を使用すると、既存のルールを使用して結果を迅速に見つけることができます。
- 時間計算量: O(logn)、空間計算量: O(1)
アルゴリズムのステップ
- 添え字を初期化し
left
、right
- 中央の添字が計算されるたびに
mid = (right + left) / 2
、ここでの除算は四捨五入演算となり、小数点は表示されません。
numbers[mid] < numbers[right]
の場合、最小値が[left, mid]
区間内にあることを意味し、right = mid
それを次の計算に使用します。- の場合
numbers[mid] > numbers[right]
、最小値が[mid, right]
間隔内にあることを意味し、left = mid + 1
それを次の計算ラウンドに使用します。 - 【注意】 の場合
numbers[mid] == numbers[right]
、最小値がどの区間にあるのか判断できません。この時は区間を絞っright--
て次のラウンドで判断しましょう
なぜleft++ ではなくright-- が縮小するのでしょうか?
- 配列は昇順であるため、最小値は右ではなく左に近くなければなりません。
- たとえば、[1,2,2,2,2]、left = 0、right = 4、mid = 2 の場合、値は
numbers[mid] == numbers[right]
この条件を満たしますが、left++ の場合、最小値は見つかりません。
アルゴリズムの考え方(2つのポイント)
- 回転ソート配列は、
nums
2 つのソート配列 nums1、nums2、および nums1 の任意の要素 >= nums2 の任意の要素に分割できます。したがって、2 つの配列の分割点 nums[i] (つまり、 2 番目の配列 ) の最初の要素。 - 左ポインタと右ポインタを nums 配列の両端に設定します。mid は各二等分の中点です。
nums[mid] > nums[right]
の場合、mid は最初にソートされた配列内になければならず、iii は Mid < i <= right を満たす必要があるため、 left = Mid + 1; を実行します。- この
nums[mid] < nums[right]
とき、mid は 2 番目にソートされた配列内になければならず、iii は left < i <= Mid を満たす必要があるため、right = Mid; を実行します。 nums[mid] == nums[right]
当時 は、この問題を153問と比較するのが難しかった(理由は、この問題は配列の要素が重複する可能性があり、分割点iiiのポインタ間隔の判断が難しいため)。- たとえば、[1,0,1,1,1] の場合、left = 0、right = 4、mid = 2 の場合、ソートされた配列 Midmidmid がどの配列に含まれるかを判断することはできません。
- この問題は次のことを証明することで解決します
right = right - 1
。- この操作では配列が範囲外になることはありません。反復条件によって right > left >= 0 が保証されるためです。
- この操作では最小値は失われません。nums[right] が最小値であると仮定すると、次の 2 つのケースがあります。
- nums[right] が唯一の最小値の場合:mid < right (left != right and mid = (left + right) // 2 であるため、判定条件 nums[mid] == nums[right] を満たすことができません。小数点以下切り捨て);
- nums[right] が唯一の最小値ではない場合、mid < right および nums[mid] == nums[right] であるため、つまり、[left,right−1] 区間にはまだ最小値が存在します。最小値は失われません。
上記は理論的な分析であり、思考を助けるために次の配列に代入できます:
[1,2,3]
[1,1,0,1]
[1,0,1,1,1]
[1,1] ,1,1]
- 時間計算量 O(logN)、特殊な場合 ([1,1,1,1] など) では O(N) に縮退します。
Jianzhi Offer 17. 1 から最大までの n 桁の数を出力します。
トピックの説明
数値 n を入力し、1 から最大の n までの 10 進数を順番に出力します。
- たとえば、「3」と入力すると、1、2、3 が最大 3 桁の数字 999 まで出力されます。
例 1:
- 入力:
n = 1
- 出力:
[1,2,3,4,5,6,7,8,9]
例証します:
- 出力する代わりに整数のリストを返す
- nは正の整数です
解決
アイデア 1
ラベル: Array
全体的なアイデア:
- まず、出力する数値の範囲を見つけます。
- 次に、1 から最大の数値まで出力を開始します
。 時間計算量: O( 1 0 n 10 ^n1 0n )、空間計算量: O(1 0 n 10 ^n1 0n )
アルゴリズム処理
- 初期化
sum = 1
- ループして 10 を乗算し、合計を境界値にします
- サイズが次の新しい res 配列を作成します。
sum-1
- 1 から sum-1 までを出力します
class Solution(object):
def printNumbers(self, n):
"""
:type n: int
:rtype: List[int]
"""
max = 10**(n)
list=[]
for i in range(1,max):
list.append(i)
return list
アイデア 2
ラベル:文字列
全体的なアイデア:
- 元の質問の意味は、実際には大きな数値の計算を調査することです。int 配列はオーバーフローする可能性があるため、文字列処理を使用すると、オーバーフローしないことを保証できます。
- しかし、戻り値は int 配列として指定されているため、実際には戻り値の観点からは絶対にオーバーフローすることはなく、非常に矛盾しています。
- そこでアイデア 2 を与えます。文字列を使用して大きな数値を処理する方法を学ぶだけです。オーバーフローの時間の複雑さについては心配しないでください
。 O( 1 0 n 10 ^n1 0n )、空間計算量: O(1 0 n 10 ^n1 0n )
アルゴリズム処理
- string を
str
初期化します。その初期値はn-1です。"0"
- 文字を使用してstr をインクリメントします。
- インクリメント処理中にキャリーの有無を判定し、キャリーがある場合はキャリーを+1し、
- 最大値に達するまでループを終了する
- 各値を取得した後、先頭の冗長な「0」をたどり、冗長な「0」を削除します。
- int に変換して結果の配列に格納します
【大数問題!】実は、この問題の主なテストポイントは、大きな数が境界を越えたときの出力です。
次の 3 つの問題に対処する必要があります。
-
大きな数値を表す変数の型:
- どの変数タイプであっても
short / int / long
、数値の値の範囲は制限されています。 - したがって、大きな数値の表現はString 型である必要があります。
- どの変数タイプであっても
-
数値の文字列セットを生成します。
- この型を使用する場合
int
、次の数値は+1
によってラウンドごとに生成できますが、これはString 型には適用できません。 - また、String 型の数値は桁上げの効率が悪く、例えば「9999」から「10000」までは 1 から 1000 までの判定を 4 回行う必要があります。
n
生成されたリストは実際にはビット 000 ~ 999 の完全な配列であることが観察からわかるため、桁上げ操作を回避でき、数値の文字列リストを再帰的に生成できます。
- この型を使用する場合
-
完全な順列を再帰的に生成します。
- 分割統治アルゴリズムの考え方に基づいて、最初に上位ビットを修正し、次に下位ビットに再帰的に戻り、1 のビットが修正されたら、数値の文字列を追加します。
- たとえば
n=2
、 (数値範囲が 1−991 - 991−99 )、10の位が 000 - 999 に固定され、順次再帰が開始されると、1の位が 000 - 999 に固定され、再帰が終了します。そしてデジタル文字列が追加されます。
上記の方法に従って、完全な配置コードを最初に記述することができます。
class Solution:
def printNumbers(self, n: int) -> [int]:
def dfs(x):
if x == n: # 终止条件:已固定完所有位
res.append(''.join(num)) # 拼接 num 并添加至 res 尾部
return
for i in range(10): # 遍历 0 - 9
num[x] = str(i) # 固定第 x 位为 i
dfs(x + 1) # 开启固定第 x + 1 位
num = ['0'] * n # 起始数字定义为 n 个 0 组成的字符列表
res = [] # 数字字符串列表
dfs(0) # 开启全排列递归
return ','.join(res) # 拼接所有数字字符串,使用逗号隔开,并返回
この方法では、数値の文字列がカンマで区切られて長い文字列が形成されます。
返される数字セット文字列は次のようになります。
输入:n = 1
输出:"0,1,2,3,4,5,6,7,8,9"
输入:n = 2
输出:"00,01,02,...,10,11,12,...,97,98,99"
输入:n = 3
输出:"000,001,002,...,100,101,102,...,997,998,999"
観察の結果、現在の生成方法にはまだ次の問題があることがわかりました。
- 00,01,02,⋯,⋯ などは 0,1,2,⋯ と表示されます。
- つまり、上位の冗長な 0 は削除する必要があります。
- このメソッドは
0
から始まり、質問リクエストリストは1
から始まります。
上記 2 つの問題の解決策は次のとおりです。
- 上位ビットから余分な 0 を削除します。
-
文字列の左境界の定義:
- 変数を宣言すると、追加される数値文字列に上位の冗長な 0 が存在しないように、文字列の左側の境界
start
が指定されます。num[start:]
- 例えば、n=2の場合、1〜9の場合はstart=1、10〜99の場合はstart=0となります。
- 変数を宣言すると、追加される数値文字列に上位の冗長な 0 が存在しないように、文字列の左側の境界
-
左境界開始の変化則:
- 観察すると、出力数値のすべての桁が 9 の場合、次の桁は 1 を上位桁に繰り上げる必要があり、このとき、左境界の開始を 1 減らす必要があることがわかります (つまり、上位桁の余分な 0 は 1 減らす必要があります)。
- たとえば、n=3 (数値範囲 1 ~ 999) の場合、左側の境界開始を 1 つ減らす必要があります。「009」は「010」に、「099」は「100」になります。
- 各桁の 999 の数を 9 とすると、全桁が 9 であるという判定条件は、次式で表すことができます。
n − start = ninen−start = ninen−始めましょう_ _=ナイン_
-
統計的
nine
手法:- 固定xxxビットの場合i = 9 の場合、i=9私=9 则执行 n i n e = n i n e + 1 nine=nine+1 ナイン_=ナイン_+1、
- そして、バックトラックする前にnine = nine − 1 nine=nine−1を復元します。ナイン_=ナイン_−1
- リストは 1 から始まります。
- 上記の方法に基づき、デジタル列を追加する前に「0」かどうかを判定し、「0」の場合はスキップします。
複雑さの分析:
- 時間計算量O ( 1 0 n ) O(10^n)O ( 1 0n ): 再帰的に生成される順列の数は1 0 n 10^n1 0n
- 空間計算量O ( 1 0 n ) O(10^n)O ( 1 0n ): 結果リスト res の長さは1 0 n − 1 10^n - 11 0n−1、各デジタル文字列の長さの間隔は1 、 2 、...、 n 1、2、...、n1 、2 、... 、nなので、O ( 1 0 n ) O(10^n) がO ( 1 0n )の追加スペース。
大きな数値を正しく表すために、次のコードの戻り値は、一連の数値文字列を連結した長い文字列になります。
class Solution:
def printNumbers(self, n: int) -> [int]:
def dfs(x):
if x == n:
s = ''.join(num[self.start:])
if s != '0': res.append(s)
if n - self.start == self.nine: self.start -= 1
return
for i in range(10):
if i == 9: self.nine += 1
num[x] = str(i)
dfs(x + 1)
self.nine -= 1
num, res = ['0'] * n, []
self.nine = 0
self.start = n - 1
dfs(0)
return ','.join(res)
この質問では、int 型の配列の出力が必要です。
- 渡すには、数値文字列 s を追加する前に int に変換します。
コードは次のようになります。
class Solution:
def printNumbers(self, n: int) -> [int]:
def dfs(x):
if x == n:
s = ''.join(num[self.start:])
if s != '0': res.append(int(s))
if n - self.start == self.nine: self.start -= 1
return
for i in range(10):
if i == 9: self.nine += 1
num[x] = str(i)
dfs(x + 1)
self.nine -= 1
num, res = ['0'] * n, []
self.nine = 0
self.start = n - 1
dfs(0)
return res
[その他] 完全順列解の簡易版
大きな数値の場合は、long 型で扱えない場合でも string に格納する必要があります。
- この問題では、実際には「09」という数字の全配列と、0~9のn桁の全配列になりますが、数字の先頭に0があってはならないことに注意してください。
完全な配置について簡単に説明します。たとえば、数字 1、2、および 3 の完全な配置は次のとおりです。
123、132、213、231、312、321
テストに合格できるようにするには、最後に文字列形式を int 形式に変更します。実際には、文字列配列が返される必要があります。
具体的な手順は以下の通りです
- 数字の先頭の0 0を避けるため0、
first
最初の桁を最初に固定します。first
値の範囲は1 9 1~91 9 digit
生成される数値を表す桁数を使用します。この質問は1 1から始める必要があります。nnまで1桁生成n桁の場合、最初の桁が各桁に対して生成されるため、二重の for ループ- 最初の数字を生成した後、再帰的に入力して残りの数字を生成します( 0 ~ 9 0 ~ 9)
digit - 1
0 ~9の値 - 再帰の終了条件
digit
は、桁数が生成されたこと、つまりindex == digit
このときの数値 num をint に変換して結果の res に加算することです。
class Solution:
def printNumbers(self, n: int) -> List[int]:
def dfs(index, num, digit):
if index == digit:
res.append(int(''.join(num)))
return
for i in range(10):
num.append(str(i))
dfs(index + 1, num, digit)
num.pop()
res = []
for digit in range(1, n + 1):
for first in range(1, 10):
num = [str(first)]
dfs(1, num, digit)
return res
Jianzhi Offer 21. 奇数が偶数の前になるように配列の順序を調整します。
トピックの説明
整数の配列を入力し、配列内の数値の順序を調整する関数を実装します。
- すべての奇数が配列の前半にあるように、
- すべての偶数は配列の後半にあります。
例:
- 入力:
nums = [1,2,3,4]
- 出力:
[1,3,2,4]
- 注:
[3,1,2,4]
これも正解の 1 つです。
- 注:
ヒント:
- 1 <=
nums.length
<= 50000 - 1 <=
nums[i]
<= 10000
解決
一連の考え
- タグ:ダブルポインター
- 全体のアイデア:
- まず前方ポインタ
start
と後方ポインタを指定します。end
- 次に、前部のポインタが偶数を見つけ、後部のポインタが奇数を見つけます。
- 配置後、配列の走査が完了するまで 2 つの値を交換します
- まず前方ポインタ
- 時間計算量:
O(n)
、空間計算量:O(1)
アルゴリズム処理
- フロントポインタ
start = 0
、バックポインタを初期化します。end = nums.length - 1
- この
start < end
時点では、配列がまだ走査されていないことを意味し、奇数と偶数の交換を続けます。 nums[start]
が奇数の場合は、奇数でない添え字が見つかるまでstart++
です。nums[end]
が偶数の場合は、偶数以外の添え字が見つかるend--
まで です。- 交換
nums[start]
し、次の交換ラウンドにnums[end]
進みます nums
交換の結果である返品
- 通常のトラバース
class Solution(object):
def exchange(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
List1 = []
List2 = []
for i in nums:
if (i % 2) == 0:
List1.append(i)
else:
List2.append(i)
List2.extend(List1)
return List2
- ダブルポインタ方式
class Solution(object):
def exchange(self, nums):
start = 0
end = len(nums)-1
while(start < end):
while(nums[start] %2 != 0) and start < end:
start+=1
while(nums[end]%2 ==0)and start < end:
end-=1
tmp = nums[start]
nums[start] = nums[end]
nums[end] = tmp
start+=1
end-=1
return nums
問題解決のアイデア (ダブルポインタ):
ダブル ポインター iii、jjj を定義して配列の左端と右端を並べ替え、ループで実行することを検討してください。
- ポインタii 偶数を左から右に探します。
- ポインタjjj は 右から左に奇数を探します。
- nums [ i ] nums[i]も実行されますnu m s [ i ]と奇数nums [ j ] nums[j]num s [ j ]交換。 _
===> 常に次のことを保証します: ポインタiii の左辺は奇数、ポインタjjj の右辺は偶数です。
アルゴリズムの流れ:
-
初期化: i 、 j はダブル ポインターで、それぞれ配列 nums の左端と右端を指します。
-
循環交換:i = ji = jの場合私=jで飛び出します。
- ポインタ i が奇数に遭遇した場合、i = i + 1 i=i+1を実行します。私=私+1偶数がまでスキップします。
- ポインタ j が偶数に遭遇すると、j = j − 1 j=j−1を実行します。j=j−1奇数がまでスキップします。
- スワップ
nums[i]
とnums[j]
値。
-
戻り値: 変更された nums 配列を返します。
複雑さの分析:
- 時間計算量O ( N ) O(N)O ( N ):NNNは配列の長さ nums、ダブル ポインタi、ji、j私、j は配列全体をまとめて反復します。
- 空間複雑度O ( 1 ) O(1)O ( 1 ) : ダブルポインタi、ji、j私、j は一定サイズの追加スペースを使用します
コード:
x & 1
ビット演算はx % 2
剰余演算と同等であり、どちらも数値のパリティを判断するために使用できます。
class Solution:
def exchange(self, nums: List[int]) -> List[int]:
i, j = 0, len(nums) - 1
while i < j:
while i < j and nums[i] & 1 == 1: i += 1
while i < j and nums[j] & 1 == 0: j -= 1
nums[i], nums[j] = nums[j], nums[i]
return nums
チップ:
- クイックソートの基本
- つまり、クイックソートでは、基点より小さいものは前に、基点より大きいものは後ろに配置されます。
その他のメソッド - 高速および低速ダブル ポインター
- fast と low のダブル ポインタを fast と low で定義します。前方は fast、後方は low です。
- fast の役割は前方の奇数位置を検索すること、 low の役割は次の奇数が格納される位置を指すことです
- 早く早くf a s t は前方に進み、奇数が見つかった場合はnums[low] nums[low]で合計されます。num s [ low ]交換、現時点ではlow low低速1 つ前に移動します。
- fast が配列の末尾を指すまで、上記の操作を繰り返します。
class Solution {
public:
vector<int> exchange(vector<int>& nums) {
int low = 0, fast = 0;
while (fast < nums.size()) {
if (nums[fast] & 1) {
swap(nums[low], nums[fast]);
low ++;
}
fast ++;
}
return nums;
}
};
剣はオファー 29 を指します。マトリックスを時計回りに印刷します。
トピックの説明
行列を入力し、各数値を外側から内側に時計回りの順序で出力します。
例 1:
- 入力:行列 = [ [ 1 , 2 , 3 ] , [ 4 , 5 , 6 ] , [ 7 , 8 , 9 ] ] 行列 = [[1,2,3],[4,5,6],[7] 、8、9]]マトリックス_ _ _ _=[[ 1 、2 、3 ] 、[ 4 、5 、6 ] 、[ 7 、8 、9 ]]
- 出力: [ 1 、 2 、 3 、 6 、 9 、 8 、 7 、 4 、 5 ] [1,2,3,6,9,8,7,4,5][ 1 、2 、3 、6 、9 、8 、7 、4 、5 ]
例 2:
- 入力:行列 = [ [ 1 , 2 , 3 , 4 ] , [ 5 , 6 , 7 , 8 ] , [ 9 , 10 , 11 , 12 ] ] 行列 = [[1,2,3,4],[5] 、6、7、8]、[9、10、11、12]]マトリックス_ _ _ _=[[ 1 、2 、3 、4 ] 、[ 5 、6 、7 、8 ] 、[ 9 、10 、11 、12 ]]
- 出力: [ 1 、 2 、 3 、 4 、 8 、 12 、 11 、 10 、 9 、 5 、 6 、 7 ] [1,2,3,4,8,12,11,10,9,5,6, 7][ 1 、2 、3 、4 、8 、12 、11 、10 、9 、5 、6 、7 ]
制限:
- 0 < = 行列。長さ < = 100 0 <= 行列.長さ <= 1000<=マトリックス。_ _ _ _ 長さ_ _ _ _<=100
- 0 < = 行列 [ i ] 。長さ < = 100 0 <= 行列[i].length <= 1000<=行列[ i ] 。_ _ _ _ 長さ_ _ _ _<=100
解決
一連の考え
- タグ: 2D 配列
- 全体のアイデア:
- 配列全体をループし、ループ内にさらに 4 つのループをネストします。
- それらは、左から右、上から下、右から左、および下から上です。
- タイトルの意味に従って配列全体の走査を完了し、境界を制御します
- んんmは行数、nnnは列数、時間計算量:O ( mn ) O(mn)O ( mn )、空間計算量:O ( 1 ) O(1)○ (1)
アルゴリズム処理
- タイトルのマトリックス マトリックスmat r i x は空でも構いません。空の配列を返すだけです
- 境界 の左、右、上、下の左、右、上、下を初期化します。le f t 、right t 、top 、b o tt o m の4つの値、結果配列res resを初期化します。resと配列の添字 xxバツ
- トラバーサルの方向に従って、数値を取り出し、結果の配列に入れます。
- 左から右へ: 走査が完了した後
++top
、上 > 下 上 > 下の場合トップ_ _>底値、境界サイクルの終わりに到達 - 上から下へ: トラバースが完了した後
--right
、左 > 右の場合、左 > 右左_ _ _>右、境界サイクルの終わりに到達 - 右から左へ: 走査が完了した後
--bottom
、上 > 下 上 > 下の場合トップ_ _>底値、境界サイクルの終わりに到達 - 下から上へ: トラバース完了後
++left
、左 > 右の場合、左 > 右左_ _ _>右、境界サイクルの終わりに到達
- 左から右へ: 走査が完了した後
コード
- 横断戦略:最後まで横断する
class Solution(object):
def spiralOrder(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: List[int]
"""
if len(matrix) ==0 : return None
# if not matrix or not matrix[0]:
# return list()
right = len(matrix[0])-1
bottom = len(matrix)-1
top =0
left=0
res = []
maxSize = (right+1)*(bottom+1)
while(maxSize>0):
for i in range(left,right+1):
if maxSize>0:
res.append(matrix[top][i])
maxSize-=1
top+=1
for j in range(top,bottom+1):
if maxSize>0:
res.append(matrix[j][right])
maxSize-=1
right-=1
for k in range(right,left-1,-1):
if maxSize>0:
res.append(matrix[bottom][k])
maxSize-=1
bottom-=1
for l in range(bottom,top-1,-1):
if maxSize>0:
res.append(matrix[l][left])
maxSize-=1
left+=1
return res
メソッド分析 - レイヤーごとのシミュレーション
- マトリックスは複数の層として見ることができます
- まず、最も外側の要素を出力します
- 次に、2 番目の外側層の要素を出力します。
- 最も内側の要素が出力されるまで。
行列の kk 番目を定義しますk層は、最も近い境界までの距離です。kのすべての頂点。
- たとえば、次の図の行列の最も外側の要素はすべて 1 1です。レベル1、2 番目の外側要素は2 番目の 22 つのレイヤー、残りの要素は 3 番目の3 つ3階建て。
[[1, 1, 1, 1, 1, 1, 1],
[1, 2, 2, 2, 2, 2, 1],
[1, 2, 3, 3, 3, 2, 1],
[1, 2, 2, 2, 2, 2, 1],
[1, 1, 1, 1, 1, 1, 1]]
各レベルで、左上から時計回りにすべての要素を繰り返します。
- 現在のレイヤーの左上隅が位置し
(top,left)
、右下隅が位置すると仮定すると(bottom,right)
、現在のレイヤーの要素は次の順序で走査されます。
- 上部の要素を左から右に、(top, left) から (top, right) にトラバースします。
- 右側の要素を上から下に移動し、続いて (トップ+1、右)から(下、右)まで。
- 場合左 < 右、上 < 下 左 < 右、上 < 下左_ _ _<右と上_ _ _ _ _ _<b o tt o m、次に下の要素を 右から左に
- (下、右−1) から (下、左+1)
- そして、左側の要素を下から上に順番に走査します。
- (下、左) ~ (トップ+1、左)。
現在のレイヤーの要素を走査した後、
- 左と左上と上に置きますleft tとto p は1ずつ増加します。
- 右と下と右と下を変更します右と下が1減ります。_ _ _ _ _
- 次の層に入って横断を続けます。
- すべての要素を通過するまで。
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
if not matrix or not matrix[0]:
return list()
rows, columns = len(matrix), len(matrix[0])
order = list()
left, right, top, bottom = 0, columns - 1, 0, rows - 1
while left <= right and top <= bottom:
for column in range(left, right + 1):
order.append(matrix[top][column])
for row in range(top + 1, bottom + 1):
order.append(matrix[row][right])
if left < right and top < bottom:
for column in range(right - 1, left, -1):
order.append(matrix[bottom][column])
for row in range(bottom, top, -1):
order.append(matrix[row][left])
left, right, top, bottom = left + 1, right - 1, top + 1, bottom - 1
return order
複雑さの分析
- 時間計算量: O(mn)。ここで、m と n はそれぞれ入力行列の行数と列数です。マトリックスの各要素には 1 回アクセスされます。
- 空間複雑度: O(1)。空間計算量は、出力配列を除いて一定です。