一、氣泡排序及優化
[TOC]
protocol SortAbleSwift {
func sort(item:[NSNumber]) -> [NSNumber]
}
複製程式碼
五種寫法的執行結果
優化核心:過濾掉已經排好序的
最後一種優化完成後,比較10個數字,只比較了19次
class SortVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let array: [NSNumber] = [1, 7, 3, 5, 6, 4, 2, 8, 9, 10]
print("source = \(array)\n")
do {
let bubulle: BubbleSwift0 = BubbleSwift0.init()
let result = bubulle.sort(item: array)
print("result = \(result)\n")
}
do {
let bubulle: BubbleSwift1 = BubbleSwift1.init()
let result = bubulle.sort(item: array)
print("result = \(result)\n")
}
do {
let bubulle: BubbleSwift2 = BubbleSwift2.init()
let result = bubulle.sort(item: array)
print("result = \(result)\n")
}
do {
let bubulle: BubbleSwift3 = BubbleSwift3.init()
let result = bubulle.sort(item: array)
print("result = \(result)\n")
}
do {
let bubulle: BubbleSwift4 = BubbleSwift4.init()
let result = bubulle.sort(item: array)
print("result = \(result)\n")
}
}
}
複製程式碼
一:最先想到的(效率最低)
class BubbleSwift0: SortAbleSwift {
func sort(item: [NSNumber]) -> [NSNumber] {
var swapCount: Int = 0 // 交換次數
var compareCount: Int = 0 // 比較次數
var result = item // 可變陣列
let count = result.count // 陣列長度
for _ in 0..<count {
for j in 0..<count-1 {
compareCount = compareCount + 1
if result[j].intValue > result[j+1].intValue {
result.swapAt(j, j+1)
swapCount = swapCount + 1
}
}
}
print("比較次數 = \(compareCount) 交換次數 = \(swapCount)")
return result
}
}
複製程式碼
二:對每趟比較次數做優化
class BubbleSwift1: SortAbleSwift {
func sort(item: [NSNumber]) -> [NSNumber] {
var swapCount: Int = 0 // 交換次數
var compareCount: Int = 0 // 比較次數
var result = item // 可變陣列
let count = result.count // 陣列長度
for i in 0..<count {
for j in 0..<count-i-1 {
compareCount = compareCount + 1
if result[j].intValue > result[j+1].intValue {
result.swapAt(j, j+1)
swapCount = swapCount + 1
}
}
}
print("比較次數 = \(compareCount) 交換次數 = \(swapCount)")
return result
}
}
複製程式碼
三:對(整體)已經排好序的做優化
class BubbleSwift2: SortAbleSwift {
func sort(item: [NSNumber]) -> [NSNumber] {
var swapCount: Int = 0 // 交換次數
var compareCount: Int = 0 // 比較次數
var result = item // 可變陣列
let count = result.count // 陣列長度
for i in 0..<count {
var isOrderly: Bool = true // 是否已經排好序
for j in 0..<count-i-1 {
compareCount = compareCount + 1
if result[j].intValue > result[j+1].intValue {
result.swapAt(j, j+1)
swapCount = swapCount + 1
isOrderly = false
}
}
if isOrderly { break }
}
print("比較次數 = \(compareCount) 交換次數 = \(swapCount)")
return result
}
}
複製程式碼
四:對(右半部分)已經排好序的做優化
class BubbleSwift3: SortAbleSwift {
func sort(item: [NSNumber]) -> [NSNumber] {
var swapCount: Int = 0 // 交換次數
var compareCount: Int = 0 // 比較次數
var result = item // 可變陣列
let count = result.count // 陣列長度
var lastPosition: Int = count - 1 // (向右上浮最大值)最後一次的交換位置
for _ in 0..<count {
var isOrderly: Bool = true // 是否已經排好序
var lastSwap: Int = 0
for j in 0..<lastPosition {
compareCount = compareCount + 1
if result[j].intValue > result[j+1].intValue {
result.swapAt(j, j+1)
swapCount = swapCount + 1
isOrderly = false
lastSwap = j
}
}
if isOrderly { break }
lastPosition = lastSwap
}
print("比較次數 = \(compareCount) 交換次數 = \(swapCount)")
return result
}
}
複製程式碼
五:對(左半部分 + 右半部分)已經排好序的做優化
class BubbleSwift4: SortAbleSwift {
func sort(item: [NSNumber]) -> [NSNumber] {
var swapCount: Int = 0 // 交換次數
var compareCount: Int = 0 // 比較次數
var result = item // 可變陣列
let count = result.count // 陣列長度
var orderlyMaxPosition: Int = count - 1 // (向右上浮最大值)最後一次的交換位置
var orderlyMinPosition: Int = 0 // (向左下沉最小值)最後一次的交換位置
for _ in 0..<count {
var isOrderly: Bool = true // 是否已經排好序
var orderlyMaxSwapPosition: Int = 0 // 有效的最大交換位置(後面的已經排好序)
var orderlyMinSwapPosition: Int = 0 // 有效的最小交換位置(前面的已經排好序)
// 向右尋上浮大值
do {
var j: Int = orderlyMinPosition
while j < orderlyMaxPosition {
compareCount = compareCount + 1
if result[j].intValue > result[j+1].intValue {
result.swapAt(j, j+1)
swapCount = swapCount + 1
orderlyMaxSwapPosition = j
isOrderly = false
}
j=j+1
}
}
if isOrderly { break }
orderlyMaxPosition = orderlyMaxSwapPosition
// 向左下沉最小值
do {
var j: Int = orderlyMaxPosition
while j > orderlyMinPosition {
compareCount = compareCount + 1
if result[j-1].intValue > result[j].intValue {
result.swapAt(j-1, j)
swapCount = swapCount + 1
orderlyMinSwapPosition = j
isOrderly = false
}
j=j-1
}
}
if isOrderly { break }
orderlyMinPosition = orderlyMinSwapPosition
}
print("比較次數 = \(compareCount) 交換次數 = \(swapCount)")
return result
}
}
複製程式碼
參考部落格
二、五個常用演算法中的貪心演算法
swift技能點練習
五大常用演算法
貪心案例一:會議安排
貪心案例二:零錢支付
貪心案例三:過河問題
驗證結果
swift技能點練習
-
Array
的sort
使用 -
Array
的reduce
函式使用 -
tuple
的使用 -
struct
的使用 -
class
的使用 - Swift中的高階函式
五大常用演算法
- 1、分治演算法
- 2、動態規劃演算法
- 3、貪心演算法
- 4、回溯法
- 5、分支界限法
貪心案例一:會議安排
問題描述:
只有一間會議室,在有限時間內(一天)安排最多的會議(不能衝突,兩個會議不能同時進行)
方案一: (非最優)每次選擇持續時長最短的會議,但是有可能持續時長最短的結束時間最晚
方案二: (非最優)每次選擇開始時間最早的會議,但是有可能開始時間最早的持續時長對長
方案三: (最優)每次選擇開始時間最早
&
持續時間最短的會議,也就是結束時間最早的會議,這是最優策略,可以安排更多的會議
struct Meeting {
var number: Int = 0 // 會議編號
var begin: Int = 0 // 會議開始時間
var end: Int = 0 // 會議結束時間
}
class ArrangeMeeting {
/// 隨機建立count個會議
///
/// - Parameter count: 會議數量
class func createRandomMeetings(count: Int) -> [Meeting] {
var meetings:[Meeting] = []
for idx in 0..<count {
var meeting: Meeting = Meeting.init()
meeting.number = idx
meeting.begin = Int(arc4random()%100)
meeting.end = meeting.begin + Int(arc4random()%100)
meetings.append(meeting)
}
return meetings
}
/// 從給定的會議陣列中選出期望的會議陣列
///
/// - Parameter meetingIn: 給定的會議陣列
/// - Returns: 期望的會議陣列
class func getExpectMeetings(meetingIn: [Meeting]) -> [Meeting] {
var meetings: [Meeting] = meetingIn
// 儲存符合條件的會議的陣列
var expectMeetings: [Meeting] = []
// 第一步:將所有會議按照結束時間從小到大排序
meetings.sort { (c1, c2) -> Bool in
return c1.end < c2.end
}
// 第二步:新增符合條件的會議到陣列中
for var idx in 0..<meetings.count {
if idx == 0 {
expectMeetings.append(meetings[idx])
continue
}
// 找到第一個開始時間比上一次會議結束時間晚的會議
var goal: (duration: Int, idx: Int) = (0, 0)
if meetings[idx].begin >= expectMeetings.last!.end {
goal.duration = meetings[idx].end - meetings[idx].begin
goal.idx = idx
}
// 獲取開始時間相同 && 持續時間最短的會議
while((idx+1 < meetings.count) && (meetings[idx].begin == meetings[idx+1].begin)) {
idx = idx+1
let curDuration: Int = meetings[idx].end - meetings[idx].begin
if curDuration < goal.duration {
goal.duration = curDuration
goal.idx = idx
}
}
if goal.duration > 0 {
expectMeetings.append(meetings[goal.idx])
}
}
return expectMeetings
}
}
複製程式碼
貪心案例二:零錢支付
問題描述:
有1元硬幣、2元硬幣、5元硬幣、10元硬幣,每一種有若干個,從中選擇幾種組合支付N元,找出話費硬幣個數最少的支付方案
這種場景可能沒有結果,比如所有硬幣用完,但是還沒有達到支付總額
方案一: (非最優解)只選擇幣值最大的,行不通,沒法解決比最大值小的場景,而且還有幣種個數限制
方案二: (非最優解)只選擇幣值最小的,行不通,最後結果不可能是最少的,而且還有幣種個數限制
方案三: (最優解)優先選擇幣值最大的優先支付,然後依次選擇次大的幣值優先支付...
struct Coin {
var value: Int = 0
var count: Int = 0
}
class PayWithCoin {
class func payCase1(amount: Int) -> [Coin] {
var payCoins: [Coin] = []
let ownedCoins: [Coin] = [Coin.init(value: 10, count: 10),
Coin.init(value: 5, count: 5),
Coin.init(value: 2, count: 1),
Coin.init(value: 1, count: 10)]
// 通過reduce函式計算出手中的硬幣能夠支付的最大額度
let maxAmount = ownedCoins.reduce(0, { result, obj in
result + obj.count * obj.value
})
if amount > maxAmount {
// 超出最大支付額度
return []
}
var amountWillPay: Int = amount
for idx in 0..<ownedCoins.count {
if amountWillPay >= ownedCoins[idx].value {
// 當前幣種消耗的個數
let value = ownedCoins[idx].value
let count = ownedCoins[idx].count
// 使用該幣種支付全部需要 needCount 個
let needCount = amountWillPay / value
// 如果該幣種能夠支付,並且有剩餘,或者正好能夠支付
if needCount <= count {
// 剩餘需要消耗的金額
let surplus = amountWillPay % value
amountWillPay = surplus
let coin: Coin = Coin.init(value: value, count: needCount)
payCoins.append(coin)
}
// 如果該幣種不能夠支付,自己可以支付的部分
else {
// 剩餘需要消耗的金額
let surplus = amountWillPay - value * count
amountWillPay = surplus
let coin: Coin = Coin.init(value: value, count: count)
payCoins.append(coin)
}
if amountWillPay <= 0 {
break
}
}
}
return payCoins
}
}
複製程式碼
貪心案例三:過河問題
問題:
n個人過河,船每次最多隻能坐兩個人,船載每個人過河的所需時間不同,問最快的過河時間。
最優方案:
如果n >= 4,
time = t[1] + t[0] + t[n-1] + t[1]
主旋律: 最快的人,次最快的人...次最慢的人,最慢的人
步調: 把最快的人和次最快的人,送完次最慢的人和最慢的人,稱為一次基本迴圈 . . . 如果 n = 3,
time = t[1] + t[0] + t[2]
如果 n = 2,
time = t[1]
如果 n = 1,
time = t[0]
struct Crosser {
var timeConsuming: Int = 0
}
class CrossRiver {
/// 隨機建立count個過河的人,每個人的過河時間隨機
///
/// - Parameter count: 過河的人的數量
/// - Returns: 所有過河的人
class func createRandomCrossers(count: Int) -> [Crosser] {
var crossers: [Crosser] = []
for _ in 0..<count {
let crosser: Crosser = Crosser.init(timeConsuming: Int(arc4random()%10))
crossers.append(crosser)
}
return crossers
}
/// 獲取所有人過河所需要的最短時間
///
/// - Parameter corssers: 所有過河的人
/// - Returns: 最短的過河時間
class func getMinimumTimeConsuming(corssers: [Crosser]) -> Int {
var leftCrosser: [Crosser] = corssers
// 對所有過河的人排序(規則 = 最快的人排在最前面,最慢的人排在最後面)
leftCrosser.sort { (c1, c2) -> Bool in
return c1.timeConsuming < c2.timeConsuming
}
var totalTimeConsuming: Int = 0
while leftCrosser.count > 0 {
if leftCrosser.count == 1 {
totalTimeConsuming = totalTimeConsuming + leftCrosser[0].timeConsuming
// 移除掉已經過河的人
leftCrosser.removeFirst()
}
else if leftCrosser.count == 2 {
totalTimeConsuming = totalTimeConsuming + leftCrosser[1].timeConsuming
// 移除掉已經過河的人
leftCrosser.removeFirst()
leftCrosser.removeFirst()
}
else if leftCrosser.count == 3 {
totalTimeConsuming = totalTimeConsuming +
leftCrosser[1].timeConsuming +
leftCrosser[0].timeConsuming +
leftCrosser[2].timeConsuming
// 移除掉已經過河的人
leftCrosser.removeFirst()
leftCrosser.removeFirst()
leftCrosser.removeFirst()
}
else {
// 最快的和次最快的過去,最快的回來;最慢的和次最慢的過去,次最快的回來。
totalTimeConsuming = totalTimeConsuming +
leftCrosser[1].timeConsuming +
leftCrosser[0].timeConsuming +
leftCrosser[leftCrosser.count-1].timeConsuming +
leftCrosser[1].timeConsuming
// 移除掉已經過河的人
leftCrosser.removeLast()
leftCrosser.removeLast()
}
}
return totalTimeConsuming
}
}
複製程式碼
驗證結果
class GreedyAlgorithmVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// 會議安排問題
do {
let allMeetings: [Meeting] = ArrangeMeeting.createRandomMeetings(count: 10)
let expectMeetings: [Meeting] = ArrangeMeeting.getExpectMeetings(meetingIn: allMeetings)
print("expectMeetings = \(expectMeetings)")
}
// 零錢支付問題
do {
let payCoins: [Coin] = PayWithCoin.pay(amount: 101)
print("payCoins = \(payCoins)")
}
// 過河問題
do {
// 網上四人過河的例子
do {
let leftCrosser: [Crosser] = [Crosser.init(timeConsuming: 5),
Crosser.init(timeConsuming: 2),
Crosser.init(timeConsuming: 1),
Crosser.init(timeConsuming: 10)]
let timerConsuming: Int = CrossRiver.getMinimumTimeConsuming(corssers: leftCrosser)
print("timerConsuming = \(timerConsuming)")
}
// 通用的例子(隨機建立n個過河的人,每個人的過河時間也隨機)
do {
let crossers: [Crosser] = CrossRiver.createRandomCrossers(count: 10)
let timerConsuming: Int = CrossRiver.getMinimumTimeConsuming(corssers: crossers)
print("timerConsuming = \(timerConsuming)")
}
}
}
}
複製程式碼
三、查詢指定字串中第一個只出現一次的字元
思路:
第一步:遍歷字串,使用字典儲存字元出現的個數,key為字元,value為出現的次數
第二步:再次遍歷字串,記錄遍歷的下標,如果字元對應的個數==1,則返回該字元和對應的下標
/// 【劍指Offer】第一個只出現一次的字元位置
///
/// - Parameter src: 傳入的字串
/// - Returns: 由字元和下標組成的元組(Int, Character),如果下標 index = -1,表示沒有找到
func firstNotRepeatingChar(src: String) -> (Int, Character) {
var mapCount: [Character:Int] = [:]
for case let ch in src {
mapCount[ch] = (mapCount[ch] ?? 0) + 1;
}
var index: Int = 0
for case let ch in src {
if let count = mapCount[ch] {
if count == 1 {
return (index, ch)
}
}
index = index + 1
}
return (-1, Character.init("0"))
}
複製程式碼
思路2: (ASCII碼對照表)[ascii.911cha.com/]
第一步:每個字元的最大值為126,建立一個長度為126的陣列,遍歷字串用字元對應的ASCII碼對應的下標對應陣列中的值儲存字元個數
第二步:同思路一,再次遍歷字串,記錄遍歷的下標,如果字元對應的個數==1,則返回該字元和對應的下標
四、實現在字串中找出連續最長的字串
思路:
- 從頭開始遍歷字串,記錄連續字元開始位置
index
和個數count
- 使用元組
curData
記錄當前的連續字串的下標index
和個數count
- 使用元組resultData記錄
count
最大的連續字串的下標index
和個數count
- 在
curData
記錄下一個或者字串遍歷完畢的時候,取curData
和resultData
中count
最大的元組儲存到resultData
中- 最後
resultData
中儲存的是目標字串的開始位置和個數
import UIKit
class ArithmeticTest: NSObject {
/// 找出給定字串中連續最長的字串(滿足一定條件)
///
/// - Parameters:
/// - str: 源字串
/// - range: 條件
/// - Returns: 目標字串
func findMaxCountNumberStr(str: String, range:ClosedRange<Int>) -> String {
guard str.count > 0 else {
return ""
}
var curData = (index:0, count:0) // 記錄(當前)連續字串的開始位置index和個數count
var resultData = (index:0, count:0) // 記錄(結果)連續字串的開始位置index和個數count
var curIndex: Int = 0
while curIndex+1 < str.count {
curData.count = 1
curData.index = curIndex
while curIndex+1 < str.count, range.contains(str[curIndex]?.int() ?? -1), str[curIndex]?.int() == str[curIndex+1]?.int() {
curData.count = curData.count + 1
curIndex = curIndex + 1
}
if curData.count > resultData.count {
resultData.count = curData.count
resultData.index = curData.index
}
curIndex = curIndex + 1
}
let resultStr: String = str[resultData.index..<(resultData.index+resultData.count)]
return resultStr
}
}
extension Character {
func int() -> Int {
var intFromCharacter:Int = 0
for scalar in String(self).unicodeScalars {
intFromCharacter = Int(scalar.value)
}
return intFromCharacter
}
}
extension String {
subscript (i: Int) -> Character? {
guard i < self.count else{
return nil
}
return self[self.index(self.startIndex, offsetBy: i)]
}
subscript (i: Int) -> String? {
if let char: Character = self[i] {
return String(char)
}
return nil
}
subscript (r: Range<Int>) -> String {
let start = index(startIndex, offsetBy: r.lowerBound)
let end = index(startIndex, offsetBy: r.upperBound)
return String(self[start..<end])
}
subscript (r: ClosedRange<Int>) -> String {
let start = index(startIndex, offsetBy: r.lowerBound)
let end = index(startIndex, offsetBy: r.upperBound)
return String(self[start...end])
}
}
複製程式碼
參考資料
五、快速排序OC實現:
/**
測試程式碼
*/
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray* array = [NSMutableArray array];
[array addObject:@10];
[array addObject:@5];
[array addObject:@12];
[array addObject:@1];
[array addObject:@45];
[self quickSort:array left:0 right:array.count-1];
NSLog(@"array = %@", array);
}
/**
交換陣列中的兩個下標位置的值
@param arr 陣列
@param x x下標
@param y y下標
*/
- (void)swaparr:(NSMutableArray<NSNumber*>*)arr x:(NSInteger)x y:(NSInteger)y {
NSInteger temp = arr[x].integerValue;
[arr replaceObjectAtIndex:x withObject:@(arr[y].integerValue)];
[arr replaceObjectAtIndex:y withObject:@(temp)];
}
/**
快速排序演算法
*/
- (void)quickSort:(NSMutableArray<NSNumber*>*)arr left:(NSInteger)left right:(NSInteger)right {
if (arr.count <= 0) {
return;
}
// 遞迴退出條件
if (left >= right) {
return;
}
// 選取左值為參考物件
NSInteger pivot = arr[left].integerValue;
NSInteger i = left, j = right;
while (i < j) {
// j 向左移動,直到arr[j] <= pivot
while (arr[j].integerValue >= pivot && i < j) {
j--;
}
// i 向右移動,直到arr[i] >= pivot
while (arr[i].integerValue <= pivot && i < j) {
i++;
}
// 交換arr[i] 和 arr[j]的值
if (i < j) {
[self swaparr:arr x:i y:j];
}
}
// 交換 pivot 和 arr[i] 的值
[self swaparr:arr x:left y:i];
// 遞迴pivot左側的陣列
[self quickSort:arr left:left right:i-1];
// 遞迴pivot右側的陣列
[self quickSort:arr left:i+1 right:right];
}
複製程式碼