用Swift刷LeetCode(一)
- 1.兩數之和
給定一個整數陣列 nums 和一個目標值 target,請你在該陣列中找出和為目標值的 兩個 整數。 你可以假設每種輸入只會對應一個答案。但是,你不能重複利用這個陣列中同樣的元素。
思路:雜湊表記錄遍歷過的數字和下標。
// 方法:暴力法 // 執行時間20ms class Solution { func twoSum(_ nums: [Int], _ target: Int) -> [Int] { for (index, _) in nums.enumerated() { if nums[index] + nums[index + 1] == target { return [index, index + 1] } } return Array<Int>() } } // 字典法 // 執行時間16ms func twoSum(_ nums: [Int], _ target: Int) -> [Int] { guard nums.count > 1 else { fatalError() } var dict = Dictionary<Int, Int>() for (i, num) in nums.enumerated() { if let lastIndex = dict[target - num] { return [lastIndex, i] } else { dict[num] = i } } fatalError() } 複製程式碼
- 2.兩數相加
給出兩個 非空 的連結串列用來表示兩個非負的整數。其中,它們各自的位數是按照 逆序 的方式儲存的,並且它們的每個節點只能儲存 一位 數字。 如果,我們將這兩個數相加起來,則會返回一個新的連結串列來表示它們的和。 您可以假設除了數字 0 之外,這兩個數都不會以 0 開頭。
思路:利用進位標識。
/** * Definition for singly-linked list. * public class ListNode { *public var val: Int *public var next: ListNode? *public init(_ val: Int) { *self.val = val *self.next = nil *} * } */ func addTwoNumbers(_ l1: ListNode?, _ l2: ListNode?) -> ListNode? { guard l1 != nil else { return l2 } guard l2 != nil else { return l1 } var c1 = l1,c2 = l2 var carry = 0// 進位標識 let resultListNode: ListNode = ListNode.init(0)// 結果連結串列頭指標 var currentListNode = resultListNode// 操作指標 repeat { var sum: Int = 0// 每次相加的結果 if c1 != nil { sum += c1!.val c1 = c1!.next } if c2 != nil { sum += c2!.val c2 = c2!.next } sum += carry //加上一輪的進位符 carry = sum / 10 // 如果超過10,進位識別符號為1;否則0 currentListNode.next = ListNode.init(sum % 10)// 寫進連結串列 currentListNode = currentListNode.next! //移動操作指標 } while (c1 != nil) || (c2 != nil) if (carry == 1) {// 最後可能會進位 currentListNode.next = ListNode.init(1) } return resultListNode.next } 複製程式碼
- 3.無重複字元的最長子串
給定一個字串,請你找出其中不含有重複字元的 最長子串 的長度。
思路:可以使用HashMap來建立字元和其出現位置之間的對映 滑動視窗
func lengthOfLongestSubstring(_ s: String) -> Int { guard s.count > 1 else { return s.count } var ans: Int = 0,left_index: Int = 0 // 值記錄鍵上一次的位置 var dict: Dictionary<Character, Int> = Dictionary<Character, Int>() var right_index: Int = 0 for char in s { if let max_left = dict[char] { left_index = (left_index > max_left) ? left_index : max_left } ans = (ans > right_index - left_index + 1) ? ans : (right_index - left_index + 1) dict[char] = right_index + 1 right_index += 1 } return ans } 複製程式碼
- 4.尋找兩個有序陣列的中位數
給定兩個大小為 m 和 n 的有序陣列 nums1 和 nums2。 請你找出這兩個有序陣列的中位數,並且要求演算法的時間複雜度為 O(log(m + n))。 你可以假設 nums1 和 nums2 不會同時為空。
太難了,參考了篇文章。
func findMedianSortedArrays(_ nums1: [Int], _ nums2: [Int]) -> Double { var array1 = nums1, array2 = nums2 var lengthA = array1.count, lengthB = array2.count if lengthA > lengthB {//保證B的長度大於等於A的長度 let tempLength = lengthA lengthA = lengthB lengthB = tempLength let tempArray = array1 array1 = nums2 array2 = tempArray } var iMin = 0, iMax = lengthA, halfLen = (lengthA + lengthB + 1) / 2 while iMin <= iMax { let i = (iMin + iMax) / 2 let j = halfLen - i if (i < iMax) && (array2[j-1] > array1[i]) { iMin += 1 } else if (i > iMin) && (array1[i-1] > array2[j]) { iMax -= 1 } else { var maxLeft = 0 if i == 0 { maxLeft = array2[j-1] } else if j == 0 { maxLeft = array1[i-1] } else { maxLeft = array1[i-1] >= array2[j-1] ? array1[i-1] : array2[j-1] } if (lengthA + lengthB) % 2 == 1 { return Double(maxLeft) } var minRight: Int = 0 if i == lengthA { minRight = array2[j] } else if j == lengthB { minRight = array1[i] } else { minRight = array1[i] < array2[j] ? array1[i] : array2[j] } return Double(maxLeft + minRight) / 2.0 } } return 0.0 } 複製程式碼
- 5.最長迴文子串
給定一個字串 s,找到 s 中最長的迴文子串。你可以假設 s 的最大長度為 1000。
思路:動態規劃。
參考ofollow,noindex">最長連續迴文串Swift實現 。
dp[i][j] = { dp[i-1][j + 1] : s[i] == s[j], false: s[i] != s[j] } 複製程式碼
func longestPalindrome(_ s: String) -> String { guard s.count > 1 else { return s } // 其中dp[i][j]表示字串區間[i, j]是否為迴文串 var dp: [[Bool]] = Array() // 初始化dp // 當i = j時,只有一個字元,肯定是迴文串 for i in 0...s.count - 1 { var eachRow:[Bool] = Array() for var j in 0...s.count - 1{ if i == j{ eachRow.append(true); }else{ eachRow.append(false); } } dp.append(eachRow); } var longest: Int = 1 var left: Int = 0 // 當前最長子串的首 var right: Int = 0// 當前最長子串的尾 var i: Int = 0// 遍歷的首 var j: Int = 0// 遍歷的尾 for character_j in s { if j == 0 { j += 1 continue } i = 0 for character_i in s { if character_i == character_j { dp[i][j] = dp[i + 1][j - 1] || j - i <= 1 if dp[i][j] && j - i + 1 > longest{// 如果當前長度大於最新長度就修改 longest = j - i + 1 left = i right = j } } else { dp[i][j] = false } i += 1; if i >= j{ break } } j += 1; } let leftIndex = s.index(s.startIndex, offsetBy: left) let rightIndex = s.index(s.startIndex, offsetBy: right) return String(s[leftIndex...rightIndex]) } 複製程式碼
- 6.Z 字形變換
將一個給定字串根據給定的行數,以從上往下、從左到右進行 Z 字形排列。
思路:找出數字之間的規律,逐行新增。
規律:兩整列之間方差:d = 2 * numRows - 2 非整列的數字公式:j - 2*i + d
func convert(_ s: String, _ numRows: Int) -> String { let len: Int = s.count guard (len > numRows) && (numRows > 1) else { return s } // 兩整列之間的公差 let d = 2*numRows - 2 var resultStr: String = "" // 處理字串 for i in 0..<numRows {// 從第一行遍歷到最後一行 var j = i while j < len { // 整列的新增 let char = s[s.index(s.startIndex, offsetBy: j)] resultStr.append(char) // 單列的新增 if (i != 0) && (i != numRows - 1) && (j - 2*i + d < len) { let char = s[s.index(s.startIndex, offsetBy: j - 2*i + d)] resultStr.append(char) } j += d } } return resultStr } 複製程式碼
- 7.整數反轉
給出一個 32 位的有符號整數,你需要將這個整數中每位上的數字進行反轉。
思路:轉化為字串後反轉,再轉回整數。
func reverse(_ x: Int) -> Int { var string: String var result: Int if x >= 0 { string = String("\(x)".reversed()) result = Int(string)! } else { string = String("\(-x)".reversed()) result = 0 - Int(string)! } // 反轉後整數溢位那麼就返回 0 if result > INT32_MAX - 1 || result < -INT32_MAX - 1 { return 0 } else { return result } } 複製程式碼
- 字串轉換整數(atoi)
請你來實現一個 atoi 函式,使其能將字串轉換成整數。 首先,該函式會根據需要丟棄無用的開頭空格字元,直到尋找到第一個非空格的字元為止。 當我們尋找到的第一個非空字元為正或者負號時,則將該符號與之後面儘可能多的連續數字組合起來,作為該整數的正負號;假如第一個非空字元是數字,則直接將其與之後連續的數字字元組合起來,形成整數。 該字串除了有效的整數部分之後也可能會存在多餘的字元,這些字元可以被忽略,它們對於函式不應該造成影響。 注意:假如該字串中的第一個非空格字元不是一個有效整數字符、字串為空或字串僅包含空白字元時,則你的函式不需要進行轉換。 在任何情況下,若函式不能進行有效的轉換時,請返回 0。
假設我們的環境只能儲存 32 位大小的有符號整數,那麼其數值範圍為 [−231,231 − 1]。如果數值超過這個範圍,qing返回INT_MAX (231 − 1) 或 INT_MIN (−231) 。
思路:因為不能進行有效的轉換時,返回0,所以可以設定初始化結果為0。設定個正負識別符號。然後越過前面的空格,檢測第一個非空格字元是不是正負號,改變正負識別符號。最後遍歷後面的數字,處理超數值範圍。如果沒有問題,最後輸出轉化的數字。
func myAtoi(_ str: String) -> Int { guard !str.isEmpty else { return 0 } var index: Int = 0 var flag: Bool = true// 正數還是負數 var res: Int = 0// 結果 // 越過前面的空格 for char in str { if char == " " { index += 1 } else { break } } // 匹配"+"或"-" if index < str.count && str[str.index(str.startIndex, offsetBy: index)] == "+" { index += 1 } else if index < str.count && str[str.index(str.startIndex, offsetBy: index)] == "-" { index += 1 flag = false } // 處理數字或非數字 while index < str.count { if (str[str.index(str.startIndex, offsetBy: index)] >= "0" && str[str.index(str.startIndex, offsetBy: index)] <= "9") {// 如果是數字 let num: String = String(str[str.index(str.startIndex, offsetBy: index)]) res = res * 10 + Int(num)! if (res > INT32_MAX) { if flag { return Int(INT32_MAX) } else { return Int(-INT32_MAX - 1) } } } else {// 不是數字 if flag { return res } else { return -res } } index += 1 } if flag { return res } else { return -res } } 複製程式碼
- 9.迴文數
判斷一個整數是否是迴文數。迴文數是指正序(從左向右)和倒序(從右向左)讀都是一樣的整數。
思路:轉化為字串,檢測反轉是否相同。
func isPalindrome(_ x: Int) -> Bool { var string: String = String() if x >= 0 {// 正數 string = String(x) if string == String(string.reversed()) { return true } else { return false } } else {// 負數 return false } } 複製程式碼
- 10.正則表示式匹配
給定一個字串 (s) 和一個字元模式 (p)。實現支援 '.' 和 '*' 的正則表示式匹配。
'.' 匹配任意單個字元。 '*' 匹配零個或多個前面的元素。
匹配應該覆蓋整個字串 (s) ,而不是部分字串。
說明: s 可能為空,且只包含從 a-z 的小寫字母。 p 可能為空,且只包含從 a-z 的小寫字母,以及字元 . 和 *
筆者自己想得方法有漏洞,網上的答案沒看懂。