iOS 面試全方位剖析 -- 算法篇

Hash 算法

所在一個字符串中找到第一個只出現(xiàn)一次的字符
如:輸入"sadagqeqsf" ,則輸出 d。

算法思路:
ASCII碼值有256種。
每個字母根據(jù)其ASCII碼作為數(shù)組對的下標(biāo)對應(yīng)數(shù)組的一個數(shù)字
數(shù)組中存儲的是每個字符出現(xiàn)的次數(shù)

代碼

char findFirstChar(char* cha)
{
    char result = '\0';
    // 定義一個數(shù)組 用來存儲各個字母出現(xiàn)次數(shù)
    int array[256];
    // 對數(shù)組進行初始化操作
    for (int i=0; i<256; i++) {
        array[i] =0;
    }
    // 定義一個指針 指向當(dāng)前字符串頭部
    char* p = cha;
    // 遍歷每個字符
    while (*p != '\0') {
        // 在字母對應(yīng)存儲位置 進行出現(xiàn)次數(shù)+1操作
        array[*(p++)]++;
    }
    
    // 將P指針重新指向字符串頭部
    p = cha;
    // 遍歷每個字母的出現(xiàn)次數(shù)
    while (*p != '\0') {
        // 遇到第一個出現(xiàn)次數(shù)為1的字符,打印結(jié)果
        if (array[*p] == 1)
        {
            result = *p;
            break;
        }
        // 反之繼續(xù)向后遍歷
        p++;
    }
    
    return result;
}

字符串反轉(zhuǎn)

比如 給定字符串 "hello,world",輸出 "dlrow,olleh"


在遍歷過程中逐步交換 begin 和 end 倆個指針指向的內(nèi)容,實現(xiàn)內(nèi)容翻轉(zhuǎn)

void char_reverse(char* cha)
{
    // 指向第一個字符
    char* begin = cha;
    // 指向最后一個字符
    char* end = cha + strlen(cha) - 1;
    
    while (begin < end) {
        // 交換前后兩個字符,同時移動指針
        char temp = *begin;
        *(begin++) = *end;
        *(end--) = temp;
    }
}

查找倆個子視圖的共同父視圖

這個和iOS 本身聯(lián)系就非常緊密了, 首先需要獲取到倆個子視圖的所有父視圖,然后倒序比較找到第一個不一樣的

如 X視圖的 D->C->B->A
Y視圖 B->A
循環(huán)遍歷, 找到共同視圖 B->A

- (NSArray <UIView *> *)findCommonSuperView:(UIView *)viewOne other:(UIView *)viewOther
{
    NSMutableArray *result = [NSMutableArray array];
    
    // 查找第一個視圖的所有父視圖
    NSArray *arrayOne = [self findSuperViews:viewOne];
    // 查找第二個視圖的所有父視圖
    NSArray *arrayOther = [self findSuperViews:viewOther];
    
    int i = 0;
    // 越界限制條件
    while (i < MIN((int)arrayOne.count, (int)arrayOther.count)) {
        // 倒序方式獲取各個視圖的父視圖
        UIView *superOne = [arrayOne objectAtIndex:arrayOne.count - i - 1];
        UIView *superOther = [arrayOther objectAtIndex:arrayOther.count - i - 1];
        
        // 比較如果相等 則為共同父視圖
        if (superOne == superOther) {
            [result addObject:superOne];
            I++;
        }
        // 如果不相等,則結(jié)束遍歷
        else{
            break;
        }
    }
    
    return result;
}

- (NSArray <UIView *> *)findSuperViews:(UIView *)view
{
    // 初始化為第一父視圖
    UIView *temp = view.superview;
    // 保存結(jié)果的數(shù)組
    NSMutableArray *result = [NSMutableArray array];
    while (temp) {
        [result addObject:temp];
        // 順著superview指針一直向上查找
        temp = temp.superview;
    }
    return result;
}

快速排序

快排的思想,這里引用一段啊哈磊博客的介紹,很清楚
分別從初始序列“6 1 2 7 9 3 4 5 10 8”兩端開始“探測”。先從右往左找一個小于6的數(shù),再從左往右找一個大于6的數(shù),然后交換他們。這里可以用兩個變量i和j,分別指向序列最左邊和最右邊。我們?yōu)檫@兩個變量起個好聽的名字“哨兵i”和“哨兵j”。剛開始的時候讓哨兵i指向序列的最左邊(即i=1),指向數(shù)字6。讓哨兵j指向序列的最右邊(即=10),指向數(shù)字。

首先哨兵j開始出動。因為此處設(shè)置的基準(zhǔn)數(shù)是最左邊的數(shù),所以需要讓哨兵j先出動,這一點非常重要(請自己想一想為什么)。哨兵j一步一步地向左挪動(即j--),直到找到一個小于6的數(shù)停下來。接下來哨兵i再一步一步向右挪動(即i++),直到找到一個數(shù)大于6的數(shù)停下來。最后哨兵j停在了數(shù)字5面前,哨兵i停在了數(shù)字7面前。

現(xiàn)在交換哨兵i和哨兵j所指向的元素的值。交換之后的序列如下:

6 1 2 5 9 3 4 7 10 8

到此,第一次交換結(jié)束。接下來開始哨兵j繼續(xù)向左挪動(再友情提醒,每次必須是哨兵j先出發(fā))。他發(fā)現(xiàn)了4(比基準(zhǔn)數(shù)6要小,滿足要求)之后停了下來。哨兵i也繼續(xù)向右挪動的,他發(fā)現(xiàn)了9(比基準(zhǔn)數(shù)6要大,滿足要求)之后停了下來。此時再次進行交換,交換之后的序列如下:

6 1 2 5 4 3 9 7 10 8

第二次交換結(jié)束,“探測”繼續(xù)。哨兵j繼續(xù)向左挪動,他發(fā)現(xiàn)了3(比基準(zhǔn)數(shù)6要小,滿足要求)之后又停了下來。哨兵i繼續(xù)向右移動,糟啦!此時哨兵i和哨兵j相遇了,哨兵i和哨兵j都走到3面前。說明此時“探測”結(jié)束。我們將基準(zhǔn)數(shù)6和3進行交換。交換之后的序列如下:

3 1 2 5 4 6 9 7 10 8

到此第一輪“探測”真正結(jié)束。此時以基準(zhǔn)數(shù)6為分界點,6左邊的數(shù)都小于等于6,6右邊的數(shù)都大于等于6?;仡櫼幌聞偛诺倪^程,其實哨兵j的使命就是要找小于基準(zhǔn)數(shù)的數(shù),而哨兵i的使命就是要找大于基準(zhǔn)數(shù)的數(shù),直到i和j碰頭為止。

OK,解釋完畢?,F(xiàn)在基準(zhǔn)數(shù)6已經(jīng)歸位,它正好處在序列的第6位。此時我們已經(jīng)將原來的序列,以6為分界點拆分成了兩個序列,左邊的序列是“3 1 2 5 4”,右邊的序列是“9 7 10 8”。接下來還需要分別處理這兩個序列。因為6左邊和右邊的序列目前都還是很混亂的。不過不要緊,我們已經(jīng)掌握了方法,接下來只要模擬剛才的方法分別處理6左邊和右邊的序列即可?,F(xiàn)在先來處理6左邊的序列現(xiàn)吧。

左邊的序列是“3 1 2 5 4”。請將這個序列以3為基準(zhǔn)數(shù)進行調(diào)整,使得3左邊的數(shù)都小于等于3,3右邊的數(shù)都大于等于3。好了開始動筆吧

如果你模擬的沒有錯,調(diào)整完畢之后的序列的順序應(yīng)該是:

2 1 3 5 4

OK,現(xiàn)在3已經(jīng)歸位。接下來需要處理3左邊的序列“2 1”和右邊的序列“5 4”。對序列“2 1”以2為基準(zhǔn)數(shù)進行調(diào)整,處理完畢之后的序列為“1 2”,到此2已經(jīng)歸位。序列“1”只有一個數(shù),也不需要進行任何處理。至此我們對序列“2 1”已全部處理完畢,得到序列是“1 2”。序列“5 4”的處理也仿照此方法,最后得到的序列如下:

1 2 3 4 5 6 9 7 10 8

對于序列“9 7 10 8”也模擬剛才的過程,直到不可拆分出新的子序列為止。最終將會得到這樣的序列,如下

1 2 3 4 5 6 7 8 9 10

到此,排序完全結(jié)束
代碼

//求一個無序數(shù)組的中位數(shù)
int findMedian(int a[], int aLen)
{
    int low = 0;
    int high = aLen - 1;
    
    int mid = (aLen - 1) / 2;
    int div = PartSort(a, low, high);
    
    while (div != mid)
    {
        if (mid < div)
        {
            //左半?yún)^(qū)間找
            div = PartSort(a, low, div - 1);
        }
        else
        {
            //右半?yún)^(qū)間找
            div = PartSort(a, div + 1, high);
        }
    }
    //找到了
    return a[mid];
}

int PartSort(int a[], int start, int end)
{
    int low = start;
    int high = end;
    
    //選取關(guān)鍵字
    int key = a[end];
    
    while (low < high)
    {
        //左邊找比key大的值
        while (low < high && a[low] <= key)
        {
            ++low;
        }
        
        //右邊找比key小的值
        while (low < high && a[high] >= key)
        {
            --high;
        }
        
        if (low < high)
        {
            //找到之后交換左右的值
            int temp = a[low];
            a[low] = a[high];
            a[high] = temp;
        }
    }
    
    int temp = a[high];
    a[high] = a[end];
    a[end] = temp;
    
    return low;
}

單鏈表反轉(zhuǎn)

定義一個新的頭結(jié)點指針NewH -> NULL,之后定義一個臨時變量 P 指針用來進行原有鏈表的遍歷動作

比如一開始p指向的是頭結(jié)點 1 ,遍歷之后 p 移動到第二個位置2.然后把 NewH 指向節(jié)點 1,1的next指向NULL。( 一定是先移動 p 指針,否則會把原鏈表的后面部分弄丟)

接下來 p 指針移動到位置 3 , NewH 指向節(jié)點 2 ,2的Next是原來的節(jié)點1。 一直遍歷直到 Next 為NULL

代碼的算法實現(xiàn)

.h文件

// 定義一個鏈表
struct Node {
    int data;
    struct Node *next;
};

@interface ReverseList : NSObject
// 鏈表反轉(zhuǎn)
struct Node* reverseList(struct Node *head);
// 構(gòu)造一個鏈表
struct Node* constructList(void);
// 打印鏈表中的數(shù)據(jù)
void printList(struct Node *head);

.m文件

struct Node* reverseList(struct Node *head)
{
    // 定義遍歷指針,初始化為頭結(jié)點
    struct Node *p = head;
    // 反轉(zhuǎn)后的鏈表頭部
    struct Node *newH = NULL;
    
    // 遍歷鏈表
    while (p != NULL) {
        
        // 記錄下一個結(jié)點
        struct Node *temp = p->next;
        // 當(dāng)前結(jié)點的next指向新鏈表頭部
        p->next = newH;
        // 更改新鏈表頭部為當(dāng)前結(jié)點
        newH = p;
        // 移動p指針
        p = temp;
    }
    
    // 返回反轉(zhuǎn)后的鏈表頭結(jié)點
    return newH;
}

struct Node* constructList(void)
{
    // 頭結(jié)點定義
    struct Node *head = NULL;
    // 記錄當(dāng)前尾結(jié)點
    struct Node *cur = NULL;
    
    for (int i = 1; i < 5; i++) {
        struct Node *node = malloc(sizeof(struct Node));
        node->data = i;
        
        // 頭結(jié)點為空,新結(jié)點即為頭結(jié)點
        if (head == NULL) {
            head = node;
        }
        // 當(dāng)前結(jié)點的next為新結(jié)點
        else{
            cur->next = node;
        }
        
        // 設(shè)置當(dāng)前結(jié)點為新結(jié)點
        cur = node;
    }
    
    return head;
}

void printList(struct Node *head)
{
    struct Node* temp = head;
    while (temp != NULL) {
        printf("node is %d \n", temp->data);
        temp = temp->next;
    }
}


void char_reverse(char* cha)
{
    char *begin = cha;
    int a = StrLength(cha);
    char end = cha + a +1;
    
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容