前書き
ダブルポインタ演算は、配列の問題で非常に一般的に使用されます。ダブルポインタは主に配列をトラバースするために使用され、2つのポインタはタスクを一緒に完了するために異なる要素を指します。
1.衝突ポインタ
衝突ポインタ方式は、その名前が示すように、ポインタが両側の中央で衝突する方法です。3つ見る
第二に、スピードポインター
スピードインジケーターは、円形トラックでの速いランナーと遅いランナーのレースとして理解できます。ある瞬間、速いランナーは後ろから遅いランナーに追いつきます。高速ポインタは一度に2ステップ速く移動し、低速ポインタは一度に1ステップ移動し、最終的にリング内の特定の位置で合流します(最小公倍数)
2つの状況があります:
1 fastはリンクリストの最後に移動し、nullを返します
。2fast= slow、2つのポインタが初めて合流し、s lowポインタは合流位置と開始位置から同時に開始し、合流位置は次のようになります。リングの入り口(わからなくても覚えておくことができます)
証明:
a =リングの始点から入口までのステップ数= 4
b =リングのステップ数= 5
速い= 2 *遅い= 2
遅い= 1
会うとき、速いステップ数=遅いステップの数+ nリングのステップの数; fastによって実行されるステップの数は、slowによって実行されるステップの数の2倍に等しくなります。したがって、
slowで実行されるステップ数は、ループ内のステップ数と同じですfast = slow + nb => slow = nb
3、LeetCode—167
順序付けられた整数配列と整数ターゲットが与えられた場合、その中の2つの要素を探して、ターゲットの合計を作成します。これら2つの数値のインデックスを返します。
例:nums = [2,7,11,15]、target = 9
出力:[1,2]
- 解決策がない場合はどうなりますか?保証されたソリューション
- 複数のソリューションがある場合はどうなりますか?解決策を返す
注意:
3,3 + 3 + 3 = 9は数字ですか?
インデックスの問題、1から開始するか0から開始するか
解決策1:
暴力的な解決策、ダブルトラバーサル、時間度はO(n ^ 2)、タイムアウトしやすい!!!
class Solution:
def findTwosum(self, num, target):
"""
在有序数组中,找到两数和为标签
:param num:
:param target:
:return:
"""
return self.violentSolution(num, target)
def violentSolution(self, num, target):
length = len(num)
# 双循环
for i in range(length):
for j in range(length):
if num[i] + num[j] == target:
return [i, j]
def main():
num = [2, 3, 5, 7, 9, 10]
target = 9
s = Solution()
solution = s.findTwosum(num, target)
print('返回索引为', solution)
if __name__ == '__main__':
main()
解決策2:
ブルートフォースソリューションは、元の配列(順序付き)の性質を十分に活用していないため、バイナリ検索の使用を検討する必要があり、その時間計算量はO(nlogn)です。
二分探索はハーフサーチとも呼ばれます。長所は比較回数が少なく、検索速度が速く、平均的なパフォーマンスが良いことです。短所は、検索するテーブルが順序付きリストである必要があることです。挿入と削除が困難です。したがって、二分探索法は、頻繁に変更されない頻繁に順序付けられたリストを検索するのに適しています。
二分探索の原理:まず、テーブル内の要素が昇順で配置されていると仮定します。テーブルの中央に記録されているキーワードを検索キーワードと比較します。2つが等しい場合は検索が成功し、そうでない場合は次を使用します。テーブルを前後に分割するための中央位置のレコード2つのサブテーブルの場合、中央位置に記録されたキーが検索キーよりも大きい場合、前のサブテーブルがさらに検索されます。それ以外の場合、後者のサブテーブルは次のようになります。さらに検索しました。条件を満たすレコードが見つかり、検索が成功するまで、またはサブテーブルが存在しなくなるまで、この時点で検索が失敗するまで、上記のプロセスを繰り返します。
class Solution:
def findTwosum(self,numbers,target):
"""
二分查找
:param num: 数组
:param target: 值
:return: 返回索引
"""
return self.binary_search(numbers,target)
def binary_search(self,numbers,target):
n = len(numbers)
for i in range(n):
low, high = i + 1, n - 1
while low <= high:
mid = (low + high) // 2
if numbers[mid] == target - numbers[i]:
return [i + 1, mid + 1]
elif numbers[mid] > target - numbers[i]:
high = mid - 1
else:
low = mid + 1
def main():
num = [2, 7, 11, 15]
target = 9
s = Solution()
solution = s.findTwosum(num, target)
print('索引为', solution)
if __name__ == '__main__':
main()
解決策3:
衝突ポインターの方法は、その名前が示すように、ポインターが両側の中央に当たることです。スペースの複雑さO(1)
class Solution:
def findTwosum(self, num, target):
return self.collision_pointer(num, target)
def collision_pointer(self, num, target):
"""
对撞指针
:param num: 有序数组
:param target: 标签
:return: 返回索引
"""
i, j = 0, len(num) - 1
while i < j:
if num[i] + num[j] == target:
break
elif num[i] + num[j] > target:
j -= 1
else:
i += 1
if num[i] + num[j] == target:
return [i + 1, j + 1]
else:
print('List is not solution!!!')
def main():
num = [2, 7, 11, 15]
target = 9
s = Solution()
solution = s.findTwosum(num, target)
print('索引为', solution)
if __name__ == '__main__':
main()
衝突するポインタを使用するその他の問題
125.それが回文文字列であるかどうかを判別します
文字列が与えられたら、大文字と小文字を区別せずにその中の数字と文字だけを見て、その文字列が回文文字列であるかどうかを判断します
class Solution:
def isPalindrome(self, s):
return self.pointer(s)
# 对撞指针解回文字符串
def pointer(self, s):
s = s.lower()
# 去掉除字符串与数字外的其他
s = [i for i in s if i.isalpha() or i.isnumeric()]
s = "".join(s)
i, j = 0, len(s) - 1
while i < j:
if s[i] == s[j]:
i += 1
j -= 1
else:
return False
return True
def main():
s = "A man, a plan, a canal: Panama"
ss = Solution()
solution = ss.isPalindrome(s)
print(solution)
if __name__ == '__main__':
main()
344.反転した文字列
文字列を指定すると、この文字列の逆の文字列を返します
class Solution:
def reverseString(self, s) :
return self.pointer(s)
def pointer(self,s):
i,j = 0,len(s)-1
while i < j:
s[i],s[j] = s[j],s[i]
i += 1
j -= 1
return s
def main():
s = ["h","e","l","l","o"]
ss = Solution()
solution = ss.reverseString(s)
print(solution)
if __name__ == '__main__':
main()
345.文字列の母音を反転します
文字列が与えられたら、文字列の母音を反転します
# 元音字母有'a', 'e', 'i', 'o', 'u','A', 'E', 'I', 'O', 'U'
class Solution:
def reverseVowels(self, s):
return self.pointer(s)
def pointer(self, s):
s = [i for i in s]
# print(s)
i, j = 0, len(s) - 1
while i < j:
if s[i] not in ['a', 'e', 'i', 'o', 'u','A', 'E', 'I', 'O', 'U']:
i += 1
elif s[j] not in ['a', 'e', 'i', 'o', 'u','A', 'E', 'I', 'O', 'U']:
j -= 1
else:
s[i], s[j] = s[j], s[i]
i += 1
j -= 1
s = ''.join(s)
return s
def main():
s = "hello"
ss = Solution()
solution = ss.reverseVowels(s)
print(solution)
if __name__ == '__main__':
main()
4、LeetCode-142
142.循環リンクリストII
リンクリストを指定して、リンクリストのリングの最初のノードを返します。リンクリストにリングがない場合は、nullが返されます。特定のリンクリスト内のリングを表すために、整数posを使用して、リンクリストの終わりがリンクリストに接続されている位置を示します(インデックスは0から始まります)。posが-1の場合、リンクリストにリングがないことを意味します。posは、リングのケースを識別するためにのみ使用され、パラメーターとして関数に渡されることはありません。
注:指定されたリンクリストを変更することは許可されていません
入力:head = [3,2,0、-4]、pos = 1
出力:インデックス1のリンクリストノードを返します
説明:リンクリストにリングがあり、そのテールは2番目のノードに接続されています
セットを使用して訪問したノードを保存すると、リンクリスト全体をトラバースして、最初の重複ノードを返すことができます。スペースの複雑さはO(n)です
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
record = set()
while head:
if head in record:
return head
record.add(head)
head = head.next
return None
追加条件:スペースの複雑さはO(1)
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
fast,slow = head,head
while fast != None:
fast = fast.next
if fast == None:
break
fast = fast.next
slow = slow.next
if slow == fast:
break
# 链表中没有环
if fast == None:
return None
p1,p2 = slow,head
while p1 != p2:
p1 = p1.next
p2 = p2.next
return p1