一、簡介二、基本操作步驟三、作用四、反轉模板交換元素的方法模板總結1 反轉數組區間2 反轉數組區間中的特定元素五、牛刀小試練習1 反轉字符串題目大意題目解析AC代碼練習2 反轉鍊表題目大意題目解析思路AC代碼練習3 反轉字符串中的元音字母題目大意題目解析AC代碼練習4 反轉字符串中的單詞 III題目大意題目解析AC代碼六、代碼模板七、總結八、實戰訓練代碼基礎訓練題AK leetcode
一、簡介數組反轉是比較基礎的模擬性操作。局部反轉操作可以抽象出來模板。原理不難理解,實現也簡單。主要是利用對撞雙指針思想,不過還是有一些細節要注意。裡面的2數交換,以及查找需要交換的數據步驟,在很多題目中都有用到(如快速排序中查找交換數據)。需要通過練習達到熟練地步,不至於到用時卡在細節上。
為了避免眼高手低,所以總結出來供大家參與練習。
二、基本操作步驟定義arr為目標數組,[i,j]為反轉的區間。
採用對撞雙指針法進行。
step 1判斷 i<j,如果成立直接結束算法
step 2 i一直增加,直到找到需要反轉的元素
step 3 j一直減少,直到找到需要反轉的元素
step 4 判斷i<j , 並執行交換,跳轉至step 1
step 5 結束
三、作用四、反轉模板反轉模板主要包括2部分:尋找要交換的2個元素,然後交換元素。
交換元素的方法交換原元素有2種方法:臨時變量法和異或法。前者適用於任何類型,後者只能適用於數字,字符等可以異或運算的類型。
// 臨時變量法
func swap(arr []int, i, j int) {
temp := arr[i] // 保存arr[i]
arr[i] = arr[j] // 上方保存誰的值就先改變誰的值,順序不能錯
arr[j]=temp
}// 異或法
func swap(arr []int, i, j int) {
// 注意i==j時以下算法會失效
// 注意i==j時以下算法會失效
// 注意i==j時以下算法會失效,想想為什麼,臨時變量法就沒有這樣的麻煩
if i==j {return} // 注意i==j時以下算法會失效
arr[i] = arr[i]^arr[j] // 保存2者的異或
arr[j] = arr[i]^arr[j] // 將arr[j] 變成 原來arr[i]的值,arr[i]^arr[j]^arr[j] = arr[i],
arr[i] = arr[i]^arr[j] // 現在arr[j]是原來的arr[i],故arr[i]^arr[j]^arr[i] = arr[j]
}
// 舉例說明
/*
arr[i]=1
arr[j]=2
arr[i] = 1^2
arr[j] = arr[i]^arr[j] = 1^2^2 = 1 // 此時arr[j]已經變成原來的arr[i]了
arr[i] = arr[i]^arr[j] = 1^2^1 = 2
*/
模板總結1 反轉數組區間// 異或法
func swap(arr []type, i, j int) {
// 注意i==j時以下算法會失效
// 注意i==j時以下算法會失效
// 注意i==j時以下算法會失效,想想為什麼,臨時變量法就沒有這樣的麻煩
if i==j {return} // 注意i==j時以下算法會失效
arr[i] = arr[i]^arr[j] // 保存2者的異或
arr[j] = arr[i]^arr[j] // 將arr[j] 變成 原來arr[i]的值,arr[i]^arr[j]^arr[j] = arr[i],
arr[i] = arr[i]^arr[j] // 現在arr[j]是原來的arr[i],故arr[i]^arr[j]^arr[i] = arr[j]
}
// 反轉arr區間[i,j]
func reverse(arr []type, i, j int) {
for ;i<j; i, j = i+1, j-1 { // 每交換完一個都往裡移一個
swap(arr, i, j)
}
}
2 反轉數組區間中的特定元素大致框架與上面的一致,不同的是需要判斷當前指針是否指到需要交換的元素,如果不是需要繼續移動。
func isNeedSwap(b type) bool {
}
// 異或法
func swap(arr []type, i, j int) {
// 注意i==j時以下算法會失效
// 注意i==j時以下算法會失效
// 注意i==j時以下算法會失效,想想為什麼,臨時變量法就沒有這樣的麻煩
if i==j {return} // 注意i==j時以下算法會失效
arr[i] = arr[i] ^ arr[j] // 保存2者的異或
arr[j] = arr[i] ^ arr[j] // 將arr[j] 變成 原來arr[i]的值,arr[i]^arr[j]^arr[j] = arr[i],
arr[i] = arr[i] ^ arr[j] // 現在arr[j]是原來的arr[i],故arr[i]^arr[j]^arr[i] = arr[j]
}
// 反轉arr區間[i,j]中的特定元素
func reverse(arr []type, i, j int) {
for ; i < j; i, j = i+1, j-1 { // 每交換完一個都往裡移一個
for ; i < j && !isNeedSwap(arr[i]); i++ { // 查找到第1個需要交換的元素
}
for ; i < j && !isNeedSwap(arr[j]); j-- { // 查找最後一個需要交換的元素
}
//if i < j { // 這裡可以去除判斷,極端情況是 i==j 相當於沒有交換
swap(arr, i, j)
//}
}
}
/*
可以看出前面的反轉數組是這個算法的特殊情況,
相當於isNeedSwap一直是true,
所以裡面的for循環不起作用
*/
五、牛刀小試練習1 反轉字符串題目連結 https://leetcode-cn.com/problems/reverse-string/
題目大意編寫一個函數,其作用是將輸入的字符串反轉過來。輸入字符串以字符數組 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","H"]
題目解析沒有條件限制,只要調用模板1即可
func reverseString(s []byte) {
reverse(s, 0, len(s)-1)
}
AC代碼// 異或法
func swap(arr []byte, i, j int) {
// 注意i==j時以下算法會失效
// 注意i==j時以下算法會失效
// 注意i==j時以下算法會失效,想想為什麼,臨時變量法就沒有這樣的麻煩
if i==j {return} // 注意i==j時以下算法會失效
arr[i] = arr[i]^arr[j] // 保存2者的異或
arr[j] = arr[i]^arr[j] // 將arr[j] 變成 原來arr[i]的值,arr[i]^arr[j]^arr[j] = arr[i],
arr[i] = arr[i]^arr[j] // 現在arr[j]是原來的arr[i],故arr[i]^arr[j]^arr[i] = arr[j]
}
// 反轉arr區間[i,j]
func reverse(arr []byte, i, j int) {
for ;i<j; i, j = i+1, j-1 {
swap(arr, i, j)
}
}
func reverseString(s []byte) {
reverse(s, 0, len(s)-1)
}
練習2 反轉鍊表題目連結:https://leetcode-cn.com/problems/reverse-linked-list/
題目大意反轉一個單鍊表。
示例:
輸入: 1->2->3->4->5->NULL輸出: 5->4->3->2->1->NULL進階:你可以迭代或遞歸地反轉鍊表。你能否用兩種方法解決這道題?
題目解析解決這個問題的方法有很多,比如尾插法、遞歸法、數組反轉法。這裡我們就演示一下數組反轉法的應用。
思路/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func reverseList(head *ListNode) *ListNode {
arr := []int{}
// 遍歷 head 並往arr中添加
// 調用反轉數組方法(模板1)
// 遍歷arr 依次更新到head鍊表中
}
AC代碼/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
// 異或法
func swap(arr []int, i, j int) {
// 注意i==j時以下算法會失效
// 注意i==j時以下算法會失效
// 注意i==j時以下算法會失效,想想為什麼,臨時變量法就沒有這樣的麻煩
if i == j {
return
} // 注意i==j時以下算法會失效
arr[i] = arr[i] ^ arr[j] // 保存2者的異或
arr[j] = arr[i] ^ arr[j] // 將arr[j] 變成 原來arr[i]的值,arr[i]^arr[j]^arr[j] = arr[i],
arr[i] = arr[i] ^ arr[j] // 現在arr[j]是原來的arr[i],故arr[i]^arr[j]^arr[i] = arr[j]
}
// 反轉arr區間[i,j]
func reverse(arr []int, i, j int) {
for ; i < j; i, j = i+1, j-1 { // 每交換完一個都往裡移一個
swap(arr, i, j)
}
}
func reverseList(head *ListNode) *ListNode {
arr := []int{}
// 遍歷 head 並往arr中添加
for temp := head; temp != nil; temp = temp.Next {
// 注意:這裡temp一定要重新定義一個,後續head還有用
arr = append(arr, temp.Val)
}
// 調用反轉數組方法(模板1)
reverse(arr, 0, len(arr)-1)
// 遍歷arr 依次更新到head鍊表中
for temp, i := head, 0; i < len(arr); i, temp = i+1, temp.Next {
// 注意:這裡temp一定要重新定義一個,後續head還有用
temp.Val = arr[i]
}
return head
}
/*
[1,2,3,4,5]
[]
[1]
*/
練習3 反轉字符串中的元音字母題目連結:https://leetcode-cn.com/problems/reverse-vowels-of-a-string/
題目大意編寫一個函數,以字符串作為輸入,反轉該字符串中的元音字母。
示例 1:
輸入:"hello"輸出:"holle"示例 2:
輸入:"leetcode"輸出:"leotcede"
提示:
元音字母不包含字母 "y" 。
題目解析題目要求交換特定的字母,需要用到模板2.
元音字母有 aeiouAEIOU , 注意大寫字母也算的。
AC代碼func isNeedSwap(b uint8) bool {
lettersMap := map[uint8]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'A': true, 'E': true, 'I': true, 'O': true, 'U': true}
return lettersMap[b]
}
// 異或法
func swap(arr []uint8, i, j int) {
if i==j {return}
arr[i] = arr[i] ^ arr[j] // 保存2者的異或
arr[j] = arr[i] ^ arr[j] // 將arr[j] 變成 原來arr[i]的值,arr[i]^arr[j]^arr[j] = arr[i],
arr[i] = arr[i] ^ arr[j] // 現在arr[j]是原來的arr[i],故arr[i]^arr[j]^arr[i] = arr[j]
}
// 反轉arr區間[i,j]中的特定元素
func reverse(arr []uint8, i, j int) {
for ; i < j; i, j = i+1, j-1 { // 每交換完一個都往裡移一個
for ; i < j && !isNeedSwap(arr[i]); i++ {
}
for ; i < j && !isNeedSwap(arr[j]); j-- {
}
swap(arr, i, j)
}
}
func reverseVowels(s string) string {
ans := []byte(s)
reverse(ans, 0, len(s)-1)
return string(ans)
}
練習4 反轉字符串中的單詞 III題目連結:https://leetcode-cn.com/problems/reverse-words-in-a-string-iii/
題目大意給定一個字符串,你需要反轉字符串中每個單詞的字符順序,同時仍保留空格和單詞的初始順序。
示例:
輸入:"Let's take LeetCode contest"輸出:"s'teL ekat edoCteeL tsetnoc"
提示:
在字符串中,每個單詞由單個空格分隔,並且字符串中不會有任何額外的空格。
題目解析題目的關鍵是如何查找單詞。
題目提示單詞之間只有一個空格,那我們就可以設置2個指針,一個指向前一個空格,然後再找一個空格。反轉空格之間的字符。再繼續向下查找空格。
AC代碼// 異或法
func swap(arr []uint8, i, j int) {
if i==j {return}
arr[i] = arr[i] ^ arr[j] // 保存2者的異或
arr[j] = arr[i] ^ arr[j] // 將arr[j] 變成 原來arr[i]的值,arr[i]^arr[j]^arr[j] = arr[i],
arr[i] = arr[i] ^ arr[j] // 現在arr[j]是原來的arr[i],故arr[i]^arr[j]^arr[i] = arr[j]
}
// 反轉arr區間[i,j]中的特定元素
func reverse(arr []uint8, i, j int) {
for ; i < j; i, j = i+1, j-1 { // 每交換完一個都往裡移一個
swap(arr, i, j)
}
}
func reverseWords(s string) string {
ans := []byte(s)
for i,j:=-1, 0;j<=len(ans);j++ {
if j==len(ans) || ans[j]==' ' { // 遇到空格或者結尾
reverse(ans, i+1, j-1)
i=j
}
}
return string(ans)
}
六、代碼模板func isNeedSwap(b type) bool {
}
// 異或法
func swap(arr []type, i, j int) {
// 注意i==j時以下算法會失效
// 注意i==j時以下算法會失效
// 注意i==j時以下算法會失效,想想為什麼,臨時變量法就沒有這樣的麻煩
if i==j {return} // 注意i==j時以下算法會失效
arr[i] = arr[i] ^ arr[j] // 保存2者的異或
arr[j] = arr[i] ^ arr[j] // 將arr[j] 變成 原來arr[i]的值,arr[i]^arr[j]^arr[j] = arr[i],
arr[i] = arr[i] ^ arr[j] // 現在arr[j]是原來的arr[i],故arr[i]^arr[j]^arr[i] = arr[j]
}
// 反轉arr區間[i,j]
func reverseCommon(arr []type, i, j int) {
for ;i<j; i, j = i+1, j-1 { // 每交換完一個都往裡移一個
swap(arr, i, j)
}
}
// 反轉arr區間[i,j]中的特定元素
func reverseSpecial(arr []type, i, j int) {
for ; i < j; i, j = i+1, j-1 { // 每交換完一個都往裡移一個
for ; i < j && !isNeedSwap(arr[i]); i++ { // 查找到第1個需要交換的元素
}
for ; i < j && !isNeedSwap(arr[j]); j-- { // 查找最後一個需要交換的元素
}
//if i < j { // 這裡可以去除判斷,極端情況是 i==j 相當於沒有交換
swap(arr, i, j)
//}
}
}
/*
可以看出前面的反轉數組reverseCommon是這個算法的特殊情況,
相當於isNeedSwap一直是true,
所以裡面的for循環不起作用
*/
七、總結主要內容:
其中交換數據與查找特殊元素雖然簡單,但是細節多,現場思考容易出錯。平時多理解,總結好代碼在用到時可脫手而出。最好自己先完整寫幾題,感覺很熟練了,閉眼代碼框架就有了。可以直接調用模板。
筆者水平有限,有寫得不對或者解釋不清楚的地方還望大家指出,我會儘自己最大努力去完善。
下面我精心準備了leetcode幾個題目(首先AK F.*ing leetcode),給大家準備了一些題目,供大家練習參考。幹他F.*ing (Fighting?)。
八、實戰訓練代碼基礎訓練題光說不練假把式,學完了怎麼也要實操一下吧,快快動用把剛才那4題A了。
反轉字符串 題目連結 https://leetcode-cn.com/problems/reverse-string/反轉鍊表 題目連結 https://leetcode-cn.com/problems/reverse-linked-list/反轉字符串中的元音字母 題目連結 https://leetcode-cn.com/problems/reverse-vowels-of-a-string/反轉字符串中的單詞 III 題目連結 https://leetcode-cn.com/problems/reverse-words-in-a-string-iii/AK leetcodeleetcode相關題目都在下面了,拿起武器挨個點名唄。
7. 整數反轉 題目連結 https://leetcode-cn.com/problems/reverse-integer/
92. 反轉鍊表 II 題目連結 https://leetcode-cn.com/problems/reverse-linked-list-ii/
206. 反轉鍊表 題目連結 https://leetcode-cn.com/problems/reverse-linked-list/
344. 反轉字符串 題目連結 https://leetcode-cn.com/problems/reverse-string/
345. 反轉字符串中的元音字母 題目連結 https://leetcode-cn.com/problems/reverse-vowels-of-a-string/
541. 反轉字符串 II 題目連結 https://leetcode-cn.com/problems/reverse-string-ii/
557. 反轉字符串中的單詞 III 題目連結 https://leetcode-cn.com/problems/reverse-words-in-a-string-iii/
917. 僅僅反轉字母 題目連結 https://leetcode-cn.com/problems/reverse-only-letters/
1190. 反轉每對括號間的子串 題目連結 https://leetcode-cn.com/problems/reverse-substrings-between-each-pair-of-parentheses/
劍指 Offer 24. 反轉鍊表 題目連結 https://leetcode-cn.com/problems/fan-zhuan-lian-biao-lcof/
本期內容簡單,主要注意代碼細節的把握。沒有大神進階題目。
本人碼農,希望通過自己的分享,讓大家更容易學懂計算機知識。