题目练习之数组的那些事儿


♥♥♥~~~~~~欢迎光临知星小度博客空间~~~~~~♥♥♥

♥♥♥零星地变得优秀~也能拼凑出星河~♥♥♥

♥♥♥我们一起努力成为更好的自己~♥♥♥

♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥

♥♥♥如果有什么问题可以评论区留言或者私信我哦~♥♥♥


 学习了这么久C语言知识,也了解了算法的一些概念,我们可以通过一些题目检验自己是否真正的掌握,我们一起来看看下面的练习~

目录

练习一:合并两个有序数组

思路1

思路2

练习二:删除有序数组中的重复项

练习三:移除元素


练习一:合并两个有序数组

合并两个有序数组: https://leetcode.cn/problems/merge-sorted-array/description/
打开题目链接,这是一道来自力扣的题目,阅读题目,要将两个非递减数组(升序数组)合并,同时要把nums2整个数组放到nums1数组里面。题目特别给出了nums1的长度是m+n,后面n元素为0。
我们通过他给的例子来更好地理解:
也就是说nums1中前面m个元素是nums1原来的数据,后面n个元素初始化为0,以便后面合并nums2的数据。那我们很快就可以用下面这个思路——先合并再排序

思路1

先把nums2数组的元素放到nums1中的后半部分,再对nums1数组进行排序

排序方法有很多,这里展示两种:

冒泡排序

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
{
    int i = 0;
    //先合并
    for(i = m ; i< m + n ; i++)
    {
        nums1[i] = nums2[i-m];
    }
    //排序
    int j = 0;
    int temp = 0;
    for(i = 0;i < m + n -1; i++)
    {
        for(j = 0; j < m + n - 1 -i; j++)
        {
            if(nums1[j+1]<nums1[j])
            {
                temp = nums1[j+1];
                nums1[j+1] = nums1[j];
                nums1[j] = temp;
            }
        }
    }
}

提交代码可以看到它通过了测试

快速排序(qsort)

int cmp_int(void const* p1, void const* p2)
{
    return (*(int*)p1 - *(int*)p2);
}
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
{
    int i = 0;
    //先合并
    for (i = m; i < m + n; i++)
    {
        nums1[i] = nums2[i - m];
    }
    //排序
    qsort(nums1, m + n, sizeof(nums1[0]), cmp_int);
}

提交代码也是成功通过,同时我们可以看到它的时间复杂度是小于冒泡排序的,这个题有没有什么其他方法也可以优化时间复杂度变成O(N)呢?这就需要我们的下一个思路了。

思路2

既然后面是元素0,那么我们可以将两个数组从后面开始向前走进行比较,哪一个数组元素大,就把哪一个元素放在后面再往前面走。

我们可以写出下面的代码:

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
{
    //记录每一个数组原来最后一个下标
    int l1 = m - 1;
    int l2 = n - 1;
    //记录合并数组最后一个下标
    int index = m + n - 1;
    while (l1 >= 0 && l2 >= 0)//保证数组下标不越界
    {
        //比较放入数据
        if (nums1[l1] > nums2[l2])
        {
            nums1[index--] = nums1[l1--];
        }
        else
        {
            nums1[index--] = nums1[l2--];
        }
    }
}

但是当我们提交时,它告诉我们解答错误,我们在代码中来看看这个错误的案例

nums1没有元素,nums2有一个元素
m=0,n=1
l1=0-1=-1
l2=0
l1已经越界,无法进入循环,nums2中有元素还没有放到nums1中

//nums1没有元素,nums2有一个元素
//m=0,n=1
//l1=0-1=-1
//l2=0
//l1已经越界,无法进入循环


void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
{
    //记录每一个数组原来最后一个下标
    int l1 = m - 1;
    int l2 = n - 1;
    //记录合并数组最后一个下标
    int index = m + n - 1;
    while (l1 >= 0 && l2 >= 0)//保证数组下标不越界
    {
        //比较放入数据
        if (nums1[l1] > nums2[l2])
        {
            nums1[index--] = nums1[l1--];
        }
        else
        {
            nums1[index--] = nums2[l2--];
        }
    }
    
}

我们怎么样来处理这一种特殊情况呢?

解决方案:

当l1已经越界,nums2中有元素还没有放到nums1中时,我们可以再写一个循环将nums2的元素放到nums1中

正确代码:

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
{
    //记录每一个数组原来最后一个下标
    int l1 = m - 1;
    int l2 = n - 1;
    //记录合并数组最后一个下标
    int index = m + n - 1;
    while (l1 >= 0 && l2 >= 0)//保证数组下标不越界
    {
        //比较放入数据
        if (nums1[l1] > nums2[l2])
        {
            nums1[index--] = nums1[l1--];
        }
        else
        {
            nums1[index--] = nums2[l2--];
        }
    }
    while (l2 >= 0)
    {
        nums1[index--] = nums2[l2--];
    }
}

练习二:删除有序数组中的重复项

这也是一道来自力扣的题目。
删除有序数组中的重复项: https://leetcode.cn/problems/remove-duplicates-from-sorted-array/description/
题目给出了原地删除,那我们就不能通过创建一个新数组来实现了。
这个题目我们知道数组是有序的,并且是一个升序数组,重复的元素一定是在一起的,所以我们可以这样做:
双指针法(双变量法)
创建两个变量pcur、dest,下标表示不同的位置, dest是初始位置下标,pcur是初始位置的下一个位置的下标
将两个位置的数据进行比较,有两种情况:
如果相等,pcur就往后面走(pcur++)
如果不相等,dest++,将pcur的值赋给dest,pcur继续往后面走(pcur++)
最后返回新数组的长度
代码如下:

int removeDuplicates(int* nums, int numsSize)
{
   //定义两个变量
	int dest = 0;//初始位置下标
	int pcur = dest + 1;//初始位置的下一个位置的下标
	while (pcur < numsSize)
	{
		//比较
		if (nums[dest] == nums[pcur])//跳过,pcur++
		{
			pcur++;
		}
		else//dest++,pcur赋值给dest后,再往后面走
		{
			dest++;
			nums[dest] = nums[pcur];
			pcur++;
		}
	}
	return dest + 1;
	//返回新数组长度
}

上面代码有点啰嗦,我们可以简单优化一下:

int removeDuplicates(int* nums, int numsSize)
{
	//定义两个变量
	int dest = 0;//初始位置下标
	int pcur = dest + 1;//初始位置的下一个位置的下标
	while (pcur < numsSize)
	{
		if (nums[dest] != nums[pcur] && ++dest != pcur)
		{
			nums[dest] = nums[pcur];
		}
		pcur++;
		//pcur往后面走
	}
	return dest + 1;
	//返回新数组长度
}

练习三:移除元素

移除元素: https://leetcode.cn/problems/remove-element/description/
题目也是给出了原地删除,那我们就不能通过创建一个新数组来实现。
我们也可以使用 双指针法(双变量法)
创建两个变量 pcur、dest指向初始位置的下标
将pcur数据与val进行比较,有两种情况:
如果相等,pcur就往后面走(pcur++)
如果不相等,将pcur的值赋给dest,pcur、dest往后面走(pcur++、dest++)
最后返回元素个数,也就是dest(dest前面已经++,不需要再+1)

int removeElement(int* nums, int numsSize, int val)
{
	//创建两个变量下标指向初始位置
	int pcur = 0;
	int dest = 0;
	while (pcur < numsSize)
	{
		if (nums[pcur] == val)//跳过
		{
			pcur++;
		}
		else//保留
		{
			nums[dest++] = nums[pcur++];
			//dest、pcur使用后++
		}
	}
	return dest;
	//返回元素个数,dest前面已经++
}

我们可以看到运行通过~

♥♥♥本篇博客内容结束,期待与各位未来的优秀程序员交流,有什么问题请私信♥♥♥

♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值