记录一些常见的算法题

·  阅读 229

字符串反转

主逻辑: 两个指针,指向头部和末尾,然后做交换,交换完成后头部指针向后移动,尾部指针向前移动,继续交换,直到头部指针地址大于尾部指针地址。

    char *begin = cha;
    char *end   = cha + strlen(cha) - 1;
    
    while (begin < end) {
        // 核心逻辑 -- 换值 移动
        char ws_tmp = *begin;
        *(begin++)  = *end;
        *(end--)    = ws_tmp;
    }
复制代码

链表反转

主逻辑: 假设原链表为A->B->C->D->NULL,那么我们需要新建一个链表NULL,然后对原链表就行遍历,让每一个元素的Next指向新的链表。具体流程:先遍历到A,然后新链表变为A->NULL,再遍历到B,新链表变为B->A->NULL,以此类推...,最终得到翻转链表D->C->B->A->NULL

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

有序数组的合并

主逻辑: 假设有数组A、B,新建数组C,长度为A+B,定义两个指针p和q,用来遍历A和B,再定义一个下标i来记录当前C的存储位置,开始遍历,如果A[p]<=B[q],那么把A[p]存放在C的i下标,然后p++,否则就是把B[q]存放到C的i下标,进行p++,最后A或者B遍历完后,将另外一个未遍历完的剩余的数据添加到C的尾部。

//有序数组的合并,五个参数分别代表 数组A A的长度 数组B B的长度 数组C
void mergeList(int a[], int aLen, int b[], int bLen, int result[])
{
    int p = 0; // 遍历数组a的指针
    int q = 0; // 遍历数组b的指针
    int i = 0; // 记录当前存储位置
    
    // 任一数组没有到达边界则进行遍历
    while (p < aLen && q < bLen) {
        // 如果a数组对应位置的值小于b数组对应位置的值
        if (a[p] <= b[q]) {
            // 存储a数组的值
            result[i] = a[p];
            // 移动a数组的遍历指针
            p++;
        }
        else{
            // 存储b数组的值
            result[i] = b[q];
            // 移动b数组的遍历指针
            q++;
        }
        // 指向合并结果的下一个存储位置
        i++;
    }
    
    // 如果a数组有剩余
    while (p < aLen) {
        // 将a数组剩余部分拼接到合并结果的后面
        result[i] = a[p++];
        i++;
    }
    
    // 如果b数组有剩余
    while (q < bLen) {
        // 将b数组剩余部分拼接到合并结果的后面
        result[i] = b[q++];
        i++;
    }
}
复制代码

查找字符串中第一个只出现一个的字符

主逻辑: 使用哈希,char类型是8位,所以有2^8种可能,建立一个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操作,
        // *(p++)为哈希函数,计算对应的index
        array[*(p++)]++;
    }
    
    // 将p指针重新指向字符串头部
    p = cha;
    // 遍历每个字母的出现次数
    while (*p != '\0') {
        // 遇到第一个出现次数为1的字符,打印结果
        if (array[*p] == 1)
        {
            result = *p;
            break;
        }
        // 反之继续向后遍历
        p++;
    }
    
    return result;
}
复制代码

查找两个视图的公共父视图

主逻辑: 先分别遍历得到视图A和视图B的所有的父视图Array,然后逆向遍历两个Array(逆序比正序少几次遍历比较),如下图,从D开始遍历

// 获取view的父视图数组
- (NSArray *)getSuperViewArrayWithView:(UIView *)view {
    NSMutableArray *superArr = NSMutableArray.array;
    
    UIView *temp = view.superview;
    while (temp) {
        [superArr addObject:temp];
        temp = temp.superview;
    }
    return superArr;
}
复制代码
// 获取两个view的公共父视图数组
- (NSArray *)getCommonSuperViewWith:(UIView *)viewA andView:(UIView *)viewB {
    NSMutableArray *commonArr = NSMutableArray.array;
    
    NSArray *AArray = [self getSuperViewArrayWithView:viewA];
    NSArray *BArray = [self getSuperViewArrayWithView:viewB];
    
    for (int i = AArray.count - 1; i >= 0; i--) {
        Class targetClass = AArray[i];
        for (int j = BArray.count - 1; j >= 0; j--) {
            if (targetClass == BArray[j]) {
                [commonArr addObject:targetClass];
            }
        }
    }
    
    return commonArr;
}
复制代码

上面的算法是进行了两个for循环的嵌套,我们来简化一下算法:

    for (int i = AArray.count - 1; i >= 0; i--) {
        Class targetClass = AArray[i];
        if ([BArray containsObject:targetClass]) {
            [commonArr addObject:targetClass];
        }
    }
复制代码

查找无序数组中的中位数

  • 什么是中位数 : 中位数是按顺序排列的一组数据中居于中间位置的数,即在这组数据中,有一半的数据比他大,有一半的数据比他小.

有一组数据:X1,X2...Xn

将它按从小到大的顺序排序为:X1,X2,...Xn

则当n为奇数时中位数为:X(n+1)/2 ;当N为偶数时中位数为(Xn/2 + X(n/2+1))/2

主逻辑: :参考快速排序的逻辑,假设有n个数,随机选取一个数m,然后定义两个指针分别指向头和尾,左边指针取值跟m对比,比m小的话,指针往后移,右边指针也跟m对比,比m大的话,指针往前移,这样一次结束之后,比它小的就会放到左边,比它大的就会放到右边,记录此时头指针的下标,跟n/2相比较,如果大于n/2,那么就继续从左边这部分继续进行上面的遍历,如果小于n/2,就从右边继续遍历,直到最终头指针的下标位置 = n/2,说明找到了中位数。

// 求一个无序数组的中位数
float ws_findMid(int a[], int aLen)
{
    int low = 0;
    int high = aLen - 1;
    
    int mid = (aLen + 1) / 2 - 1;       //减一是因为下标是从0开始,奇数跟偶数这里的mid都是一样的算法
    int currentHeader = ws_partSort(a, low, high);
    
    while (currentHeader != mid)
    {
        if (mid < currentHeader)
        {
            //左半区间找
            currentHeader = ws_partSort(a, low, currentHeader - 1);
        }
        else
        {
            //右半区间找
            currentHeader = ws_partSort(a, currentHeader + 1, high);
        }
    }
    
    //找到了
    float midNum = a[mid];
    
    //奇偶判断
    if(aLen %2 != 0)
        return midNum;
    
    float anotherMid = a[mid+1];
    return (midNum + anotherMid)/2.0;
}

int ws_partSort(int a[], int start, int end)
{
    int low = start;
    int high = end;
    
    //选取关键字
    int key = a[start];
    
    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;
        }
    }
    
    return low;
}
复制代码
分类:
iOS
标签:
收藏成功!
已添加到「」, 点击更改