iOS-面試題之算法(較全較易懂)

目錄
  • 常見算法
    • 不用中間變量,用兩種方法交換A和B的值
    • 求最大公約數(shù)
    • 判斷質(zhì)數(shù)
    • 字符串逆序輸出
  • 排序相關(guān)算法
    • 選擇排序
    • 冒泡排序
    • 折半查找(二分查找)
    • 快速排序
  • 模擬棧的操作
序言

雖然我們在平時工作中敦跌,算法用的比較少歹袁,但是面試的時候琼娘,算法考核算是一個必修課逻淌。所以熟悉算法,深刻理解本質(zhì)棠枉,對于面試就成竹在胸了明棍。

一 常用算法
1.1 不用中間變量,用兩種方法交換A和B的值
// 1.中間變量
void swap(int a, int b) {
   int temp = a;
   a = b;
   b = temp;
}

// 2.加法
void swap(int a, int b) {
   a = a + b;
   b = a - b;
   a = a - b;
}

// 3.異或(相同為0攒菠,不同為1. 可以理解為不進位加法)
void swap(int a, int b) {
   a = a ^ b;
   b = a ^ b;
   a = a ^ b;
}
1.2 求最大公約數(shù)

比如204的最大公約數(shù)為41827的最大公約數(shù)為9茎活。

/** 1.直接遍歷法 */
int maxCommonDivisor(int a, int b) {
    int max = 0;
    for (int i = 1; i <=b; i++) {
        if (a % i == 0 && b % i == 0) {
            max = i;
        }
    }
    return max;
}

/** 2.輾轉(zhuǎn)相除法 其中a為大數(shù)昙沦,b為小數(shù) */
int maxCommonDivisor(int a, int b) {
    int r;
    while(a % b > 0) {
        r = a % b;
        a = b;
        b = r;
    }
    return b;
}
1.3 判斷質(zhì)數(shù)

比如2,3,5,7,11,13,19等只能被1和自身整除的叫質(zhì)數(shù)

這里只用最簡單直接打判斷,一個個除载荔,看余數(shù)是否為零盾饮,如果不為零,則非質(zhì)數(shù)。

int isPrime(int n) {
    for(int i = 2; i <= sqrt(n); i++) {
        if(n % i == 0) {
            return 0;
        }
    }
    return 1;
}
1.4 字符串逆序輸出

直接用指針進行操作

void reverse(char s[]) {
    // p指向字符串頭部
    char *p = s ;
    
    // q指向字符串尾部
    char *q = s ;
    while('\0' != *q) {
        q++ ;
    }
    q-- ;
    
    // 交換并移動指針丘损,直到p和q交叉
    while(q > p) {
        char t = *p;
        char m = *q;
        *p = m;
        *q = t;
        p++;
        q--;
    }
}
二 排序相關(guān)算法
2.1 選擇排序

選擇排序(Selection sort)是一種簡單直觀的排序算法普办。它的工作原理是每一次從待排序的數(shù)據(jù)元素中選出最小(或最大)的一個元素徘钥,存放在序列的起始位置衔蹲,然后,再從剩余未排序元素中繼續(xù)尋找最谐蚀 (大)元素舆驶,然后放到已排序序列的末尾。以此類推而钞,直到全部待排序的數(shù)據(jù)元素排完沙廉。 選擇排序是不穩(wěn)定的排序方法。

最值出現(xiàn)在起始端

  • 第1趟:在n個數(shù)中找到最小(大)數(shù)與第一個數(shù)交換位置
  • 第2趟:在剩下n-1個數(shù)中找到最小(大)數(shù)與第二個數(shù)交換位置
  • 重復這樣的操作...依次與第三個臼节、第四個...數(shù)交換位置
  • 第n-1趟撬陵,最終可實現(xiàn)數(shù)據(jù)的升序(降序)排列。
void selectSort(int *arr, int length) {
    for (int i = 0; i < length - 1; i++) { //趟數(shù)
        for (int j = i + 1; j < length; j++) { //比較次數(shù)
            if (arr[i] > arr[j]) {
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
    }
}
2.2 冒泡排序

它重復地走訪過要排序的元素列网缝,依次比較兩個相鄰的元素袱结,如果他們的順序(如從大到小、首字母從A到Z)錯誤就把他們交換過來途凫。走訪元素的工作是重復地進行直到?jīng)]有相鄰元素需要交換垢夹,也就是說該元素已經(jīng)排序完成。

這個算法的名字由來是因為越大的元素會經(jīng)由交換慢慢“浮”到數(shù)列的頂端(升序或降序排列)维费,就如同碳酸飲料中二氧化碳的氣泡最終會上浮到頂端一樣果元,故名“冒泡排序”。

相鄰元素兩兩比較犀盟,比較完一趟而晒,最值出現(xiàn)在末尾

  • 第1趟:依次比較相鄰的兩個數(shù),不斷交換(小數(shù)放前阅畴,大數(shù)放后)逐個推進倡怎,最值最后出現(xiàn)在第n個元素位置
  • 第2趟:依次比較相鄰的兩個數(shù),不斷交換(小數(shù)放前贱枣,大數(shù)放后)逐個推進监署,最值最后出現(xiàn)在第n-1個元素位置
  • …… ……
  • 第n-1趟:依次比較相鄰的兩個數(shù),不斷交換(小數(shù)放前纽哥,大數(shù)放后)逐個推進钠乏,最值最后出現(xiàn)在第2個元素位置
void bublleSort(int *arr, int length) {
    for(int i = 0; i < length - 1; i++) { //趟數(shù)
        for(int j = 0; j < length - i - 1; j++) { //比較次數(shù)
            if(arr[j] > arr[j+1]) {
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        } 
    }
}
2.3 折半查找(二分查找)

在計算機科學中,折半搜索(英語:half-interval search)春塌,也稱二分搜索(英語:binary search)晓避、對數(shù)搜索(英語:logarithmic search)簇捍,是一種在有序數(shù)組中查找某一特定元素的搜索算法。

搜索過程從數(shù)組的中間元素開始俏拱,如果中間元素正好是要查找的元素暑塑,則搜索過程結(jié)束;如果某一特定元素大于或者小于中間元素锅必,則在數(shù)組大于或小于中間元素的那一半中查找梯投,而且跟開始一樣從中間元素開始比較。如果在某一步驟數(shù)組為空况毅,則代表找不到分蓖。這種搜索算法每一次比較都使搜索范圍縮小一半 [1]

優(yōu)化查找時間(不用遍歷全部數(shù)據(jù))

  • 1 數(shù)組必須是有序的
  • 2 必須已知min和max(知道范圍)
  • 3 動態(tài)計算mid的值尔许,取出mid對應的值進行比較
  • 4 如果mid對應的值大于要查找的值么鹤,那么max要變小為mid-1
  • 5 如果mid對應的值小于要查找的值,那么min要變大為mid+1

已知一個有序數(shù)組, 和一個key, 要求從數(shù)組中找到key對應的索引位置

int findKey(int *arr, int length, int key) {
    int min = 0, max = length - 1, mid;
    while (min <= max) {
        mid = (min + max) / 2; //計算中間值
        if (key > arr[mid]) {
            min = mid + 1;
        } else if (key < arr[mid]) {
            max = mid - 1;
        } else {
            return mid;
        }
    }
    return -1;
}
2.4 快速排序

快速排序由C. A. R. Hoare在1962年提出味廊。它的基本思想是:通過一趟排序?qū)⒁判虻臄?shù)據(jù)分割成獨立的兩部分蒸甜,其中一部分的所有數(shù)據(jù)都比另外一部分的所有數(shù)據(jù)都要小,然后再按此方法對這兩部分數(shù)據(jù)分別進行快速排序余佛,整個排序過程可以遞歸進行柠新,以此達到整個數(shù)據(jù)變成有序序列

該方法的基本思想是:

  • 1 先從數(shù)列中取出一個數(shù)作為基準數(shù)辉巡。
  • 2 分區(qū)過程恨憎,將比這個數(shù)大的數(shù)全放到它的右邊,小于或等于它的數(shù)全放到它的左邊郊楣。
  • 3 再對左右區(qū)間重復第二步憔恳,直到各區(qū)間只有一個數(shù)。

** 一趟快速排序的算法是:**

  • 1 設置兩個變量i净蚤、j钥组,排序開始的時候:i=0,j=N-1今瀑;
  • 2 以第一個數(shù)組元素作為關(guān)鍵數(shù)據(jù)程梦,賦值給key,即key=A[0]橘荠;
  • 3 從j開始向前搜索屿附,即由后開始向前搜索(j--),找到第一個小于key的值A[j]砾医,將A[j]的值賦給A[i]拿撩;
  • 4 從i開始向后搜索衣厘,即由前開始向后搜索(i++)如蚜,找到第一個大于key的A[i]压恒,將A[i]的值賦給A[j];
  • 5 重復第3错邦、4步探赫,直到i=j; (3,4步中撬呢,沒找到符合條件的值伦吠,即3中A[j]不小于key,4中A[i]不大于key的時候改變j、i的值魂拦,使得j=j-1毛仪,i=i+1,直至找到為止芯勘。找到符合條件的值箱靴,進行交換的時候i, j指針位置不變荷愕。另外衡怀,i==j這一過程一定正好是i+或j-完成的時候,此時令循環(huán)結(jié)束)安疗。

或許很多人看了后還是很懵逼抛杨,有個哥們總結(jié)的很好

白話經(jīng)典算法系列之六 快速排序 快速搞定

下面詳細描述

雖然快速排序稱為分治法,但分治法這三個字顯然無法很好的概括快速排序的全部步驟荐类。因此我的對快速排序作了進一步的說明:挖坑填數(shù) + 分治法

以一個數(shù)組作為示例怖现,取區(qū)間第一個數(shù)為基準數(shù)。

image.png

初始時玉罐,i = 0; j = 9; X = a[i] = 72

由于已經(jīng)將a[0]中的數(shù)保存到X中真竖,可以理解成在數(shù)組a[0]上挖了個坑,可以將其它數(shù)據(jù)填充到這來厌小。

j開始向前找一個比X小或等于X的數(shù)恢共。當j=8,符合條件璧亚,將a[8]挖出再填到上一個坑a[0]中讨韭。a[0]=a[8]; i++; 這樣一個坑a[0]就被搞定了,但又形成了一個新坑a[8]癣蟋,這怎么辦了透硝?簡單,再找數(shù)字來填a[8]這個坑疯搅。這次從i開始向后找一個大于X的數(shù)濒生,當i=3,符合條件幔欧,將a[3]挖出再填到上一個坑中a[8]=a[3]; j--;

數(shù)組變?yōu)椋?/p>

image.png

i = 3; j = 7; X=72

再重復上面的步驟罪治,先從后向前找丽声,再從前向后找。

從j開始向前找觉义,當j=5雁社,符合條件,將a[5]挖出填到上一個坑中晒骇,a[3] = a[5]; i++;

從i開始向后找霉撵,當i=5時,由于i==j退出洪囤。

此時徒坡,i = j = 5,而a[5]剛好又是上次挖的坑瘤缩,因此將X填入a[5]崭参。

數(shù)組變?yōu)椋?/p>

image.png

可以看出a[5]前面的數(shù)字都小于它,a[5]后面的數(shù)字都大于它款咖。因此再對a[0…4]a[6…9]這二個子區(qū)間重復上述步驟就可以了何暮。

對挖坑填數(shù)進行總結(jié)
1.i =L; j = R; 將基準數(shù)挖出形成第一個坑a[i]。
2.j--由后向前找比它小的數(shù)铐殃,找到后挖出此數(shù)填前一個坑a[i]中海洼。
3.i++由前向后找比它大的數(shù),找到后也挖出此數(shù)填到前一個坑a[j]中富腊。
4.再重復執(zhí)行2坏逢,3二步,直到i==j赘被,將基準數(shù)填入a[i]中是整。

照著這個總結(jié)很容易實現(xiàn)挖坑填數(shù)的代碼:

//快速排序
void quick_sort(int s[], int l, int r)
{
    if (l < r)
    {
        //Swap(s[l], s[(l + r) / 2]); //將中間的這個數(shù)和第一個數(shù)交換 參見注1
        int i = l, j = r, x = s[l];
        while (i < j)
        {
            while(i < j && s[j] >= x) // 從右向左找第一個小于x的數(shù)
                j--;  
            if(i < j) 
                s[i++] = s[j];
            
            while(i < j && s[i] < x) // 從左向右找第一個大于等于x的數(shù)
                i++;  
            if(i < j) 
                s[j--] = s[i];
        }
        s[i] = x;
        quick_sort(s, l, i - 1); // 遞歸調(diào)用 
        quick_sort(s, i + 1, r);
    }
}
三 模擬棧的操作
  • 棧是一種數(shù)據(jù)結(jié)構(gòu),特點:先進后出

代碼實現(xiàn)

  • Stack.h
/**
 定義block
 @param obj 回調(diào)值
 */
typedef void(^StackBlock)(id obj);

/**
 模擬棧實現(xiàn) - 簡單實現(xiàn)
 */
@interface Stack : NSObject

// 初始化操作
- (instancetype)initWithNumbers:(NSArray *)numbers;

/** 入棧 @param obj 指定入棧對象 */
- (void)push:(id)obj;

/** 出棧 */
- (id)popObj;

/** 是否為空 */
- (BOOL)isEmpty;

/** 棧的長度 */
- (NSInteger)stackLength;

/** 從棧底開始遍歷 @param block 回調(diào)遍歷的結(jié)果 */
-(void)enumerateObjectsFromBottom:(StackBlock)block;

/** 從頂部開始遍歷 */
-(void)enumerateObjectsFromtop:(StackBlock)block;

/** 所有元素出棧民假,一邊出棧一邊返回元素 */
-(void)enumerateObjectsPopStack:(StackBlock)block;

/** 清空 */
-(void)removeAllObjects;

/** 返回棧頂元素 */
-(id)topObj;

@end
  • Stack.m
@interface Stack ()
// 存儲棧數(shù)據(jù)
@property (nonatomic, strong) NSMutableArray *stackArray;

@end

@implementation Stack

// 初始化操作
- (instancetype)initWithNumbers:(NSArray *)numbers {
    self = [super init];
    if (self) {
        for (NSNumber *number in numbers) {
            [self.stackArray addObject:number];
        }
    }
    return self;
}

#pragma mark - push

- (void)push:(id)obj {
    [self.stackArray addObject:obj];
}

#pragma mark - get

- (id)popObj {
    if ([self isEmpty]) {
        return nil;
    } else {
        id lastObject = self.stackArray.lastObject;
        [self.stackArray removeLastObject];
        return lastObject;
    }
}

-(id)topObj {
    if ([self isEmpty]) {
        return nil;
    } else {
        return self.stackArray.lastObject;
    }
}

- (BOOL)isEmpty {
    return !self.stackArray.count;
}

- (NSInteger)stackLength {
    return self.stackArray.count;
}

#pragma mark - 遍歷

// 從棧底開始遍歷
-(void)enumerateObjectsFromBottom:(StackBlock)block {
    [self.stackArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        block ? block(obj) : nil;
    }];
}

// 從頂部開始遍歷
-(void)enumerateObjectsFromtop:(StackBlock)block {
    [self.stackArray enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        block ? block(obj) : nil;
    }];
}

// 所有元素出棧浮入,一邊出棧一邊返回元素
-(void)enumerateObjectsPopStack:(StackBlock)block {
    __weak typeof(self) weakSelf = self;
    NSUInteger count = self.stackArray.count;
    for (NSUInteger i = count; i > 0; i --) {
        if (block) {
            block(weakSelf.stackArray.lastObject);
            [self.stackArray removeLastObject];
        }
    }
}

#pragma mark - remove

-(void)removeAllObjects {
    [self.stackArray removeAllObjects];
}

#pragma mark - lazy

- (NSMutableArray *)stackArray {
    if (!_stackArray) {
        _stackArray = [NSMutableArray array];
    }
    return _stackArray;
}

@end

本文參考
【2018年最新】iOS面試題之算法
白話經(jīng)典算法系列之六 快速排序 快速搞定

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市羊异,隨后出現(xiàn)的幾起案子事秀,更是在濱河造成了極大的恐慌,老刑警劉巖野舶,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件易迹,死亡現(xiàn)場離奇詭異,居然都是意外死亡平道,警方通過查閱死者的電腦和手機睹欲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來一屋,“玉大人窘疮,你說我怎么就攤上這事袋哼。” “怎么了考余?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵先嬉,是天一觀的道長轧苫。 經(jīng)常有香客問我楚堤,道長,這世上最難降的妖魔是什么含懊? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任身冬,我火速辦了婚禮,結(jié)果婚禮上岔乔,老公的妹妹穿的比我還像新娘酥筝。我一直安慰自己,他們只是感情好雏门,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布嘿歌。 她就那樣靜靜地躺著,像睡著了一般茁影。 火紅的嫁衣襯著肌膚如雪宙帝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天募闲,我揣著相機與錄音步脓,去河邊找鬼。 笑死浩螺,一個胖子當著我的面吹牛靴患,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播要出,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼鸳君,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了患蹂?” 一聲冷哼從身側(cè)響起相嵌,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎况脆,沒想到半個月后饭宾,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡格了,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年看铆,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盛末。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡弹惦,死狀恐怖否淤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情棠隐,我是刑警寧澤石抡,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站助泽,受9級特大地震影響啰扛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嗡贺,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一隐解、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧诫睬,春花似錦煞茫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至亲澡,卻和暖如春钦扭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背谷扣。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工土全, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人会涎。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓裹匙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親末秃。 傳聞我的和親對象是個殘疾皇子概页,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容

  • 概述 排序有內(nèi)部排序和外部排序,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進行排序练慕,而外部排序是因排序的數(shù)據(jù)很大惰匙,一次不能容納全部...
    蟻前閱讀 5,167評論 0 52
  • 1、不用中間變量,用兩種方法交換A和B的值 2铃将、求最大公約數(shù) 3项鬼、模擬棧操作 棧是一種數(shù)據(jù)結(jié)構(gòu),特點:先進后出 練...
    ptlCoder閱讀 7,098評論 0 17
  • 總結(jié)一下常見的排序算法劲阎。 排序分內(nèi)排序和外排序绘盟。內(nèi)排序:指在排序期間數(shù)據(jù)對象全部存放在內(nèi)存的排序。外排序:指在排序...
    jiangliang閱讀 1,326評論 0 1
  • 人類, 藏好獸性龄毡, 扮好人性吠卷。
    戚布爾閱讀 203評論 0 0
  • 秋風瑟瑟,帶著涼意沦零。已是秋末冬初時節(jié)祭隔。萬物枯寂,寒風掃蕩著大地路操,有種寂寥陰郁之感疾渴。 不過,安府這邊卻是一片喜氣洋洋...
    輕和一閱讀 232評論 0 0