0
点赞
收藏
分享

微信扫一扫

【Python多线程】的进阶讲解

文风起武 03-22 20:30 阅读 2

顺序表OJ题(文字解读+图解)

1. 移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
OJ链接=>

int removeElement(int* nums, int numsSize, int val);

1.1 方法一(循环遍历)

这题最容易想到的解题思路为:

  1. 首先遍历整个nums数组,找到第一次val出现的位置;
  2. 将val之后的元素整体往前移,覆盖掉val;
  3. 再将整个数组大小减1.
    如图所示:
    方法一图解
int removeElement(int* nums, int numsSize, int val) {
    //一直循环,循环体中会存在中止条件
    while(1)
    {
        int pos = 0;
        for(;pos < numsSize;pos++)
        {
            //1.在nums中找val出现的位置
            if(nums[pos] == val)
            {
                break;
            }
        }

            //2.检查是否找完了,pos走到最后,直接结束循环
        if(pos == numsSize)
            break;
            
            //3.找到val后覆盖
        for(int i = pos+1;i < numsSize;i++)
        {
            nums[i-1] = nums[i];
        }
            //大小减一
        numsSize--;
    }
    return numsSize;
}

因为我们一直循环,所以要特别注意一下终止的条件。

1.2 方法二(创建数组)

第二种方法也比较常规:

  1. 直接创建一个numsSize大小的新数组和一个计数器;
  2. 循环遍历将不等于val的元素放在新数组里;
  3. 最后将新数组内存拷贝到原来的数组(因为题目要求必须仅使用 O(1) 额外空间并 原地 修改输入数组),并释放掉新数组的空间;
    方法二图解
int removeElement(int* nums, int numsSize, int val) {
    //1.创建一个与原数组大小相同的数组
    int* temp = (int*)malloc(sizeof(int)*numsSize);
    int count = 0;//计算器
    for(int i = 0;i < numsSize;i++)
    {
        //2.将nums中不等于val的元素拷贝到新数组中,并让计算器加1
        if(nums[i] != val)
        {
            temp[count++] = nums[i];
        }
    }
    //3.内存拷贝回原数组
    memcpy(nums,temp,sizeof(int)*count);
    //释放掉新数组
    free(temp);
    return count;
}

1.3 方法三(双指针)

第三种方法比较难想到:

  1. 创建两个指针(src,dest);
  2. 一个指针src先走(用来判断值),一个指针dest后走(用来改变数组元素);
  3. 如果*src不等于val,则让dest=src,并往后走;如果*src等于val,则直接跳过。
    方法三图解
int removeElement(int* nums, int numsSize, int val) {
    int src=0;
    int dest=0;
    while(src<numsSize)
    {
        if(nums[src]!=val)
        {
        	//直接赋值,并都往后走
            nums[dest++]=nums[src++];
        }
        else
        {
        	//只有src往后走
            src++;
        }
    }
    return dest;
}

这种方法就直接在原数组上移动,不需要再创建新的数组。

2. 删除有序数组中的重复项

给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。
OJ链接=>

int removeDuplicates(int* nums, int numsSize)

这题我们继承上一题的思想仍然使用双指针来解题:

  1. 创建两个指针(src,dst);
  2. src先走,如果*src不等于*dst,则让dst往后走,然后将src指向的元素赋值给dst指向的元素,最后让src往后走;如果相等,我们需要删除,就直接让src往后走,不赋值;

图解

int removeDuplicates(int* nums, int numsSize) {
	//让src在第二个元素位置,dst在第一个
    int src=1,dst=0;
    while(src<numsSize)
    {
        if(nums[src]!=nums[dst])
        {
        	//dst先++,再赋值,src再++
            ++dst;
            nums[dst]=nums[src];
            src++;
            //可简化为
            //nums[++dst]=nums[src++];
        }
        else
        {
            src++;
        }
    }
  return dst+1;
}

我们最后返回的是元素的个数,所以dst需要+1(因为dst下标从0开始的)。

3. 合并两个有序数组

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。

请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
OJ链接=>

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)

这里我们的常规思路就是取出nums2里的一个元素然后与nums1中每个元素遍历比较大小。这是可以的,就是效率不高。
我们可以换个思路,将两个数组合并nums1,从尾部开始进行比较,较大的尾插(因为题目说的很清楚,两个数组是非递减顺序 ,尾部一定是数组中最大的了),就非常高效了。
详情图解

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {
    int end1 = m - 1;
    int end2 = n - 1;
    //合并后的大小
    int i = m + n - 1;
    while(end1 >= 0&&end2 >= 0)
    {
        if(nums1[end1] > nums2[end2])
        {
            nums1[i--] = nums1[end1--]; 
        } 
        else
        {
            nums1[i--] = nums2[end2--];
        }
    }
    //如果数组2还有元素,那么直接尾插
    while(end2 >= 0)
    {
        nums1[i--] = nums2[end2--];
    }
}

4. 轮转数组

给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
OJ链接=>

void rotate(int* nums, int numsSize, int k)

这道题有一个非常妙的思路就是逆置:先逆置各自半边的元素,再整体逆置。
逆置图解

void swap(int *a,int* b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}
void reverse(int* nums,int left,int right)
{
    while(left<right)
    {
        swap(&nums[left],&nums[right]);
        left++;
        right--;
    }
}

void rotate(int* nums, int numsSize, int k) {
	//每隔numsSize次转回原数组
    k %= numsSize;
    reverse(nums,0,numsSize-k-1);
    reverse(nums,numsSize-k,numsSize-1);
    reverse(nums,0,numsSize-1);
}

5. 数组形式的整数加法

整数的 数组形式 num 是按照从左到右的顺序表示其数字的数组。

例如,对于 num = 1321 ,数组形式是 [1,3,2,1] 。
给定 num ,整数的 数组形式 ,和整数 k ,返回 整数 num + k 的 数组形式 。
OJ链接=>

int* addToArrayForm(int* num, int numSize, int k, int* returnSize);

解题思路:此题是用一个数的数组形式 + 一个整数, 返回和的数组形式。
模拟加法进行逐位相加, 从低位向高位相加,最后整体逆置,得到最终结果

  1. 每一位的值 = 对应位值的和 + 前一位的进位
  2. 每一位的值计算出来之后,需要检查是否需要进位
  3. 最高位计算之后,需要考虑是否还需要向上进位
void reserve(int* num,int left,int right)
{
    while(left<right)
    {
        int tmp = num[right];
        num[right] = num[left];
        num[left] = tmp;
        left++;
        right--;
    }
}

int* addToArrayForm(int* num, int numSize, int k, int* returnSize) {
    int* addRet = (int*)malloc(sizeof(int)*10001);
    int reti = 0;//reti: 第i位的结果
    //从低位开始相加
    int sz = numSize-1;
    int next = 0;// 进位值
    while(sz >= 0 || k > 0)
    {
        int x1 = 0;
        if(sz >= 0)
        {
            //如果sz没有越界,还有未相加的位,取出一位存入x1
            x1 = num[sz];
            sz--;
        }
        int x2 = 0;
        //如果k大于0,获取k的第i位
        if(k > 0)
        {
            x2 = k%10;
            k /= 10;
        }
        //第i位的结果:每一位的值 + 进位
        int ret = x1 + x2 + next;
        //如果结果大于9,需要进位
        if(ret > 9)
        {
            ret %= 10;
            next = 1;
        }
        else
        {
            next = 0;
        }
        //存入第i位的结果到数组中
        addRet[reti++] = ret;
    }
    //如果最高位有进位,需要在存入1
    if(next == 1)
    {
        addRet[reti++] = 1;
    }
    //逆置结果
    reserve(addRet,0,reti-1);
    *returnSize = reti;
    return addRet;
}
举报

相关推荐

0 条评论