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

作者: PetitBread | 来源:发表于2018-06-12 17:39 被阅读50次

    Hash 算法

    所在一个字符串中找到第一个只出现一次的字符
    如:输入"sadagqeqsf" ,则输出 d。

    算法思路:
    ASCII码值有256种。
    每个字母根据其ASCII码作为数组对的下标对应数组的一个数字
    数组中存储的是每个字符出现的次数

    代码

    char findFirstChar(char* cha)
    {
        char result = '\0';
        // 定义一个数组 用来存储各个字母出现次数
        int array[256];
        // 对数组进行初始化操作
        for (int i=0; i<256; i++) {
            array[i] =0;
        }
        // 定义一个指针 指向当前字符串头部
        char* p = cha;
        // 遍历每个字符
        while (*p != '\0') {
            // 在字母对应存储位置 进行出现次数+1操作
            array[*(p++)]++;
        }
        
        // 将P指针重新指向字符串头部
        p = cha;
        // 遍历每个字母的出现次数
        while (*p != '\0') {
            // 遇到第一个出现次数为1的字符,打印结果
            if (array[*p] == 1)
            {
                result = *p;
                break;
            }
            // 反之继续向后遍历
            p++;
        }
        
        return result;
    }
    

    字符串反转

    比如 给定字符串 "hello,world",输出 "dlrow,olleh"


    在遍历过程中逐步交换 begin 和 end 俩个指针指向的内容,实现内容翻转

    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 本身联系就非常紧密了, 首先需要获取到俩个子视图的所有父视图,然后倒序比较找到第一个不一样的

    如 X视图的 D->C->B->A
    Y视图 B->A
    循环遍历, 找到共同视图 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++;
            }
            // 如果不相等,则结束遍历
            else{
                break;
            }
        }
        
        return result;
    }
    
    - (NSArray <UIView *> *)findSuperViews:(UIView *)view
    {
        // 初始化为第一父视图
        UIView *temp = view.superview;
        // 保存结果的数组
        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的数,再从左往右找一个大于6的数,然后交换他们。这里可以用两个变量i和j,分别指向序列最左边和最右边。我们为这两个变量起个好听的名字“哨兵i”和“哨兵j”。刚开始的时候让哨兵i指向序列的最左边(即i=1),指向数字6。让哨兵j指向序列的最右边(即=10),指向数字。

    首先哨兵j开始出动。因为此处设置的基准数是最左边的数,所以需要让哨兵j先出动,这一点非常重要(请自己想一想为什么)。哨兵j一步一步地向左挪动(即j--),直到找到一个小于6的数停下来。接下来哨兵i再一步一步向右挪动(即i++),直到找到一个数大于6的数停下来。最后哨兵j停在了数字5面前,哨兵i停在了数字7面前。

    现在交换哨兵i和哨兵j所指向的元素的值。交换之后的序列如下:

    6 1 2 5 9 3 4 7 10 8

    到此,第一次交换结束。接下来开始哨兵j继续向左挪动(再友情提醒,每次必须是哨兵j先出发)。他发现了4(比基准数6要小,满足要求)之后停了下来。哨兵i也继续向右挪动的,他发现了9(比基准数6要大,满足要求)之后停了下来。此时再次进行交换,交换之后的序列如下:

    6 1 2 5 4 3 9 7 10 8

    第二次交换结束,“探测”继续。哨兵j继续向左挪动,他发现了3(比基准数6要小,满足要求)之后又停了下来。哨兵i继续向右移动,糟啦!此时哨兵i和哨兵j相遇了,哨兵i和哨兵j都走到3面前。说明此时“探测”结束。我们将基准数6和3进行交换。交换之后的序列如下:

    3 1 2 5 4 6 9 7 10 8

    到此第一轮“探测”真正结束。此时以基准数6为分界点,6左边的数都小于等于6,6右边的数都大于等于6。回顾一下刚才的过程,其实哨兵j的使命就是要找小于基准数的数,而哨兵i的使命就是要找大于基准数的数,直到i和j碰头为止。

    OK,解释完毕。现在基准数6已经归位,它正好处在序列的第6位。此时我们已经将原来的序列,以6为分界点拆分成了两个序列,左边的序列是“3 1 2 5 4”,右边的序列是“9 7 10 8”。接下来还需要分别处理这两个序列。因为6左边和右边的序列目前都还是很混乱的。不过不要紧,我们已经掌握了方法,接下来只要模拟刚才的方法分别处理6左边和右边的序列即可。现在先来处理6左边的序列现吧。

    左边的序列是“3 1 2 5 4”。请将这个序列以3为基准数进行调整,使得3左边的数都小于等于3,3右边的数都大于等于3。好了开始动笔吧

    如果你模拟的没有错,调整完毕之后的序列的顺序应该是:

    2 1 3 5 4

    OK,现在3已经归位。接下来需要处理3左边的序列“2 1”和右边的序列“5 4”。对序列“2 1”以2为基准数进行调整,处理完毕之后的序列为“1 2”,到此2已经归位。序列“1”只有一个数,也不需要进行任何处理。至此我们对序列“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

    到此,排序完全结束
    代码

    //求一个无序数组的中位数
    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)
            {
                //左半区间找
                div = PartSort(a, low, div - 1);
            }
            else
            {
                //右半区间找
                div = PartSort(a, div + 1, high);
            }
        }
        //找到了
        return a[mid];
    }
    
    int PartSort(int a[], int start, int end)
    {
        int low = start;
        int high = end;
        
        //选取关键字
        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;
    }
    

    单链表反转

    定义一个新的头结点指针NewH -> NULL,之后定义一个临时变量 P 指针用来进行原有链表的遍历动作

    比如一开始p指向的是头结点 1 ,遍历之后 p 移动到第二个位置2.然后把 NewH 指向节点 1,1的next指向NULL。( 一定是先移动 p 指针,否则会把原链表的后面部分弄丢)

    接下来 p 指针移动到位置 3 , NewH 指向节点 2 ,2的Next是原来的节点1。 一直遍历直到 Next 为NULL

    代码的算法实现

    .h文件

    // 定义一个链表
    struct Node {
        int data;
        struct Node *next;
    };
    
    @interface ReverseList : NSObject
    // 链表反转
    struct Node* reverseList(struct Node *head);
    // 构造一个链表
    struct Node* constructList(void);
    // 打印链表中的数据
    void printList(struct Node *head);
    

    .m文件

    struct Node* reverseList(struct Node *head)
    {
        // 定义遍历指针,初始化为头结点
        struct Node *p = head;
        // 反转后的链表头部
        struct Node *newH = NULL;
        
        // 遍历链表
        while (p != NULL) {
            
            // 记录下一个结点
            struct Node *temp = p->next;
            // 当前结点的next指向新链表头部
            p->next = newH;
            // 更改新链表头部为当前结点
            newH = p;
            // 移动p指针
            p = temp;
        }
        
        // 返回反转后的链表头结点
        return newH;
    }
    
    struct Node* constructList(void)
    {
        // 头结点定义
        struct Node *head = NULL;
        // 记录当前尾结点
        struct Node *cur = NULL;
        
        for (int i = 1; i < 5; i++) {
            struct Node *node = malloc(sizeof(struct Node));
            node->data = i;
            
            // 头结点为空,新结点即为头结点
            if (head == NULL) {
                head = node;
            }
            // 当前结点的next为新结点
            else{
                cur->next = node;
            }
            
            // 设置当前结点为新结点
            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;
        
    }
    

    相关文章

      网友评论

        本文标题:iOS 面试全方位剖析 -- 算法篇

        本文链接:https://www.haomeiwen.com/subject/jewleftx.html